Skip to content

Commit 734bbef

Browse files
authored
Merge pull request #897 from utmstack/bugfix/10.5.16/unable-to-create-incidents
Bugfix/10.5.16/unable to create incidents
2 parents 5aebb0b + 0c6366f commit 734bbef

File tree

21 files changed

+227
-67
lines changed

21 files changed

+227
-67
lines changed

CHANGELOG.md

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
# UTMStack 10.5.16 Release Notes
1+
# UTMStack 10.5.17 Release Notes
22
## Bugfix
3-
- False positive alerts displayed in Dashboard Overview
4-
- Correct query parsing for filterType conditions with special characters
3+
-Fixed an issue preventing incident status updates when entering long solutions.
4+
-Fixed an issue blocking incident creation from the Alerts panel.
5+
-Added Asia/Jakarta timezone to the TIMEZONES list.
6+
-Fixed Timezone changes on an instance don't get updated when access by Federation Server
7+
-Fixed Last log not displayed for a generated alert

backend/src/main/java/com/park/utmstack/repository/incident/UtmIncidentAlertRepository.java

+2
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,6 @@ public interface UtmIncidentAlertRepository extends JpaRepository<UtmIncidentAle
2323
void updateAlertStatusByAlertIdIn(@Param("alertIds") List<String> alertIds, @Param("status") Integer status);
2424

2525
List<UtmIncidentAlert> findAllByIncidentId(Long incidentId);
26+
27+
List<UtmIncidentAlert> findByAlertIdIn(List<String> alertIds);
2628
}

backend/src/main/java/com/park/utmstack/service/incident/UtmIncidentAlertService.java

+23
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import java.util.List;
1616
import java.util.Optional;
17+
import java.util.stream.Collectors;
1718

1819
/**
1920
* Service Implementation for managing UtmIncidentAlert.
@@ -98,4 +99,26 @@ public void saveAll(List<UtmIncidentAlert> utmIncidentAlerts) {
9899
public List<UtmIncidentAlert> findAllByIncidentId(Long incidentId) {
99100
return utmIncidentAlertRepository.findAllByIncidentId(incidentId);
100101
}
102+
103+
104+
/**
105+
* Checks if any of the provided alert IDs exist in the database.
106+
*
107+
* @param alertIds List of alert IDs to check.
108+
* @return True if at least one alert exists, false otherwise.
109+
*/
110+
public List<String> existsAnyAlert(List<String> alertIds) {
111+
return utmIncidentAlertRepository.findByAlertIdIn(alertIds)
112+
.stream()
113+
.map(UtmIncidentAlert::getAlertId)
114+
.collect(Collectors.toList());
115+
}
116+
117+
public String formatAlertMessage(List<String> alertIds) {
118+
return "The following alert(s) are already associated with another incident:<br><br>" +
119+
"<ul>" +
120+
alertIds.stream().map(alertId -> "<li>" + alertId + "</li>").collect(Collectors.joining()) +
121+
"</ul>" +
122+
"<br>Please review the incidents section for more details.";
123+
}
101124
}

backend/src/main/java/com/park/utmstack/web/rest/incident/UtmIncidentResource.java

+63-11
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,8 @@
33
import com.park.utmstack.domain.application_events.enums.ApplicationEventType;
44
import com.park.utmstack.domain.incident.UtmIncident;
55
import com.park.utmstack.service.application_events.ApplicationEventService;
6-
import com.park.utmstack.service.dto.incident.AddToIncidentDTO;
7-
import com.park.utmstack.service.dto.incident.IncidentUserAssignedDTO;
8-
import com.park.utmstack.service.dto.incident.NewIncidentDTO;
9-
import com.park.utmstack.service.dto.incident.UtmIncidentCriteria;
6+
import com.park.utmstack.service.dto.incident.*;
7+
import com.park.utmstack.service.incident.UtmIncidentAlertService;
108
import com.park.utmstack.service.incident.UtmIncidentQueryService;
119
import com.park.utmstack.service.incident.UtmIncidentService;
1210
import com.park.utmstack.util.UtilResponse;
@@ -29,6 +27,7 @@
2927
import java.net.URISyntaxException;
3028
import java.util.List;
3129
import java.util.Optional;
30+
import java.util.stream.Collectors;
3231

3332
/**
3433
* REST controller for managing UtmIncident.
@@ -43,20 +42,37 @@ public class UtmIncidentResource {
4342

4443
private final UtmIncidentService utmIncidentService;
4544

45+
private final UtmIncidentAlertService utmIncidentAlertService;
46+
4647
private final UtmIncidentQueryService utmIncidentQueryService;
4748
private final ApplicationEventService applicationEventService;
4849

49-
public UtmIncidentResource(UtmIncidentService utmIncidentService, UtmIncidentQueryService utmIncidentQueryService, ApplicationEventService applicationEventService) {
50+
public UtmIncidentResource(UtmIncidentService utmIncidentService,
51+
UtmIncidentAlertService utmIncidentAlertService,
52+
UtmIncidentQueryService utmIncidentQueryService,
53+
ApplicationEventService applicationEventService) {
5054
this.utmIncidentService = utmIncidentService;
55+
this.utmIncidentAlertService = utmIncidentAlertService;
5156
this.utmIncidentQueryService = utmIncidentQueryService;
5257
this.applicationEventService = applicationEventService;
5358
}
5459

5560
/**
56-
* POST /utm-incidents : Create a new utmIncident.
61+
* Creates a new incident based on the provided details.
62+
*
63+
* This endpoint accepts a {@link NewIncidentDTO} object, validates the data,
64+
* and attempts to create a new incident. The process includes:
65+
* - Verifying that the alert list is not empty.
66+
* - Checking if any of the provided alerts are already associated with another incident.
67+
* - Creating the incident if all validations pass.
5768
*
58-
* @param newIncidentDTO the utmIncident to create
59-
* @return the ResponseEntity with status 201 (Created) and with body the new utmIncident, or with status 400 (Bad Request) if the utmIncident has already an ID
69+
* @param newIncidentDTO the DTO containing the details of the incident to create, including associated alerts.
70+
* @return a {@link ResponseEntity} containing:
71+
* - HTTP 201 (Created) if the incident is successfully created.
72+
* - HTTP 400 (Bad Request) if the alert list is empty.
73+
* - HTTP 409 (Conflict) if one or more alerts are already associated with another incident.
74+
* - HTTP 500 (Internal Server Error) if an unexpected error occurs during processing.
75+
* @throws IllegalArgumentException if the input data is invalid.
6076
*/
6177
@PostMapping("/utm-incidents")
6278
public ResponseEntity<UtmIncident> createUtmIncident(@Valid @RequestBody NewIncidentDTO newIncidentDTO) {
@@ -68,6 +84,22 @@ public ResponseEntity<UtmIncident> createUtmIncident(@Valid @RequestBody NewInci
6884
applicationEventService.createEvent(msg, ApplicationEventType.ERROR);
6985
return UtilResponse.buildErrorResponse(HttpStatus.BAD_REQUEST, msg);
7086
}
87+
88+
List<String> alertIds = newIncidentDTO.getAlertList().stream()
89+
.map(RelatedIncidentAlertsDTO::getAlertId)
90+
.collect(Collectors.toList());
91+
92+
List<String> alertsFound = utmIncidentAlertService.existsAnyAlert(alertIds);
93+
94+
if (!alertsFound.isEmpty()) {
95+
String alertIdsList = String.join(", ", alertIds);
96+
String msg = "Some alerts are already linked to another incident. Alert IDs: " + alertIdsList + ". Check the related incidents for more details.";
97+
log.error(msg);
98+
applicationEventService.createEvent(ctx + ": " + msg , ApplicationEventType.ERROR);
99+
return UtilResponse.buildErrorResponse(HttpStatus.CONFLICT, utmIncidentAlertService.formatAlertMessage(alertsFound));
100+
}
101+
102+
71103
return ResponseEntity.ok(utmIncidentService.createIncident(newIncidentDTO));
72104
} catch (Exception e) {
73105
String msg = ctx + ": " + e.getMessage();
@@ -78,10 +110,17 @@ public ResponseEntity<UtmIncident> createUtmIncident(@Valid @RequestBody NewInci
78110
}
79111

80112
/**
81-
* POST /utm-incidents : Create a new utmIncident.
113+
* POST /utm-incidents/add-alerts : Add alerts to an existing utmIncident.
82114
*
83-
* @param addToIncidentDTO the utmIncident to add alerts to
84-
* @return the ResponseEntity with status 201 (Created) and with body the new utmIncident, or with status 400 (Bad Request) if the utmIncident has already an ID
115+
* This endpoint allows users to associate a list of alerts with an existing utmIncident.
116+
* If any of the provided alerts are already linked to another incident, a conflict response is returned.
117+
*
118+
* @param addToIncidentDTO the DTO containing the details of the utmIncident and the list of alerts to add
119+
* @return the ResponseEntity with:
120+
* - status 201 (Created) and the updated utmIncident if successful,
121+
* - status 400 (Bad Request) if the alert list is empty,
122+
* - status 409 (Conflict) if some alerts are already associated with another incident,
123+
* - status 500 (Internal Server Error) if an unexpected error occurs.
85124
* @throws URISyntaxException if the Location URI syntax is incorrect
86125
*/
87126
@PostMapping("/utm-incidents/add-alerts")
@@ -92,6 +131,19 @@ public ResponseEntity<UtmIncident> addAlertsToUtmIncident(@Valid @RequestBody Ad
92131
if (CollectionUtils.isEmpty(addToIncidentDTO.getAlertList())) {
93132
throw new BadRequestAlertException("Add utmIncident cannot already have an empty related alerts", ENTITY_NAME, "alertList");
94133
}
134+
List<String> alertIds = addToIncidentDTO.getAlertList().stream()
135+
.map(RelatedIncidentAlertsDTO::getAlertId)
136+
.collect(Collectors.toList());
137+
138+
List<String> alertsFound = utmIncidentAlertService.existsAnyAlert(alertIds);
139+
140+
if (!alertsFound.isEmpty()) {
141+
String alertIdsList = String.join(", ", alertIds);
142+
String msg = "Some alerts are already linked to another incident. Alert IDs: " + alertIdsList + ". Check the related incidents for more details.";
143+
log.error(msg);
144+
applicationEventService.createEvent(ctx + ": " + msg , ApplicationEventType.ERROR);
145+
return UtilResponse.buildErrorResponse(HttpStatus.CONFLICT, utmIncidentAlertService.formatAlertMessage(alertsFound));
146+
}
95147
UtmIncident result = utmIncidentService.addAlertsIncident(addToIncidentDTO);
96148
return ResponseEntity.created(new URI("/api/utm-incidents/add-alerts" + result.getId()))
97149
.headers(HeaderUtil.createEntityCreationAlert(ENTITY_NAME, result.getId().toString()))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<databaseChangeLog
3+
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd">
6+
7+
<changeSet id="20241122001" author="manuel">
8+
<sql dbms="postgresql" splitStatements="true" stripComments="true">
9+
ALTER TABLE utm_incident_history
10+
ALTER COLUMN action_detail TYPE VARCHAR(1000);
11+
</sql>
12+
</changeSet>
13+
</databaseChangeLog>

backend/src/main/resources/config/liquibase/master.xml

+1
Original file line numberDiff line numberDiff line change
@@ -65,5 +65,6 @@
6565

6666
<include file="/config/liquibase/changelog/20240517001_add_collectors_source_menu.xml" relativeToChangelogFile="false"/>
6767

68+
<include file="/config/liquibase/changelog/20241122001_alter_action_detail_length.xml" relativeToChangelogFile="false"/>
6869

6970
</databaseChangeLog>

frontend/src/app/app-management/app-management.module.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import {NgxSortableModule} from 'ngx-sortable-2';
1010
import {ComplianceManagementModule} from '../compliance/compliance-management/compliance-management.module';
1111
import {NavBehavior} from '../shared/behaviors/nav.behavior';
1212
import {VersionUpdateBehavior} from '../shared/behaviors/version-update.behavior';
13-
import {TimezoneFormatService} from '../shared/services/utm-timezone.service';
1413
import {UtmSharedModule} from '../shared/utm-shared.module';
1514
import {AppConfigComponent} from './app-config/app-config.component';
1615
import {AppLogsComponent} from './app-logs/app-logs.component';
@@ -102,7 +101,7 @@ import {UtmApiDocComponent} from './utm-api-doc/utm-api-doc.component';
102101
HealthChecksComponent,
103102
],
104103
schemas: [CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA],
105-
providers: [NavBehavior, VersionUpdateBehavior, TimezoneFormatService]
104+
providers: [NavBehavior, VersionUpdateBehavior]
106105
})
107106
export class AppManagementModule {
108107
}

frontend/src/app/app.component.ts

+1-4
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import {ThemeChangeBehavior} from './shared/behaviors/theme-change.behavior';
88
import {ADMIN_ROLE, USER_ROLE} from './shared/constants/global.constant';
99
import {AppThemeLocationEnum} from './shared/enums/app-theme-location.enum';
1010
import {UtmAppThemeService} from './shared/services/theme/utm-app-theme.service';
11-
import {TimezoneFormatService} from './shared/services/utm-timezone.service';
1211

1312
@Component({
1413
selector: 'app-root',
@@ -32,8 +31,7 @@ export class AppComponent implements OnInit {
3231
private themeChangeBehavior: ThemeChangeBehavior,
3332
private utmAppThemeService: UtmAppThemeService,
3433
private router: Router, private renderer: Renderer2,
35-
private apiServiceCheckerService: ApiServiceCheckerService,
36-
private timezoneFormatService: TimezoneFormatService) {
34+
private apiServiceCheckerService: ApiServiceCheckerService) {
3735

3836
this.translate.setDefaultLang('en');
3937

@@ -128,7 +126,6 @@ export class AppComponent implements OnInit {
128126
}
129127

130128
init() {
131-
this.timezoneFormatService.loadTimezoneAndFormat();
132129
this.getReportLogo();
133130
}
134131
}

frontend/src/app/app.module.ts

+12-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {HTTP_INTERCEPTORS, HttpClient, HttpClientModule} from '@angular/common/http';
2-
import {CUSTOM_ELEMENTS_SCHEMA, Injector, NgModule} from '@angular/core';
2+
import {APP_INITIALIZER, CUSTOM_ELEMENTS_SCHEMA, Injector, NgModule} from '@angular/core';
33
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
44
import {BrowserModule} from '@angular/platform-browser';
55
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
@@ -36,6 +36,10 @@ import {TimezoneFormatService} from './shared/services/utm-timezone.service';
3636
import {UtmSharedModule} from './shared/utm-shared.module';
3737
import {AccountService} from "./core/auth/account.service";
3838

39+
export function initTimezoneFormat(timezoneService: TimezoneFormatService) {
40+
return () => timezoneService.loadTimezoneAndFormat();
41+
}
42+
3943
@NgModule({
4044
declarations: [
4145
AppComponent,
@@ -105,11 +109,16 @@ import {AccountService} from "./core/auth/account.service";
105109
useClass: ManageHttpInterceptor,
106110
multi: true,
107111
},
112+
{
113+
provide: APP_INITIALIZER,
114+
useFactory: initTimezoneFormat,
115+
deps: [TimezoneFormatService],
116+
multi: true
117+
},
108118
NewAlertBehavior,
109119
NavBehavior,
110120
AlertIncidentStatusChangeBehavior,
111-
GettingStartedBehavior,
112-
TimezoneFormatService
121+
GettingStartedBehavior
113122
],
114123
bootstrap: [AppComponent],
115124
schemas: [CUSTOM_ELEMENTS_SCHEMA],
@@ -118,6 +127,5 @@ export class AppModule {
118127
constructor(private dpConfig: NgbDatepickerConfig, private config: NgbModalConfig) {
119128
this.dpConfig.minDate = {year: moment().year() - 100, month: 1, day: 1};
120129
config.backdrop = 'static';
121-
//timezoneFormatService.loadTimezoneAndFormat();
122130
}
123131
}

frontend/src/app/data-management/alert-management/alert-view/alert-view.component.html

+1
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ <h6 class="card-title text-blue-800 font-weight-light">
227227
[alert]="alertDetail"
228228
[hideEmptyField]=" true"
229229
[tags]="tags"
230+
[timeFilter]="getFilterTime()"
230231
[dataType]="dataType"></app-alert-view-detail>
231232
</div>
232233
</div>

frontend/src/app/data-management/alert-management/alert-view/alert-view.component.ts

+4
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,10 @@ export class AlertViewComponent implements OnInit, OnDestroy {
550550
modal.componentInstance.alert = alert;
551551
}
552552

553+
getFilterTime(){
554+
return this.filters.find(f => f.field === ALERT_TIMESTAMP_FIELD);
555+
}
556+
553557
ngOnDestroy(): void {
554558
this.destroy$.next();
555559
this.destroy$.complete();

frontend/src/app/data-management/alert-management/shared/components/alert-view-detail/alert-view-detail.component.html

+5
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,11 @@
176176
</span>
177177
</div>
178178
</ng-template>
179+
<ng-container *ngIf="isEmptyResponse()">
180+
<div class="d-flex has-fixed-height w-100 justify-content-center align-items-center">
181+
<app-no-data-found></app-no-data-found>
182+
</div>
183+
</ng-container>
179184
</div>
180185
</div>
181186
</div>

0 commit comments

Comments
 (0)