From 857dfc8a6068e0bae33a9f27d6fda823eceb7be2 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 23 Jul 2025 17:09:08 +0300 Subject: [PATCH 01/12] UI: refactor alias/filter-select enhance reactivity and remove unused deps --- .../alias/entity-alias-select.component.html | 3 +- .../alias/entity-alias-select.component.ts | 53 +++++++------ .../filter/filter-select.component.html | 3 +- .../common/filter/filter-select.component.ts | 77 ++++++++----------- 4 files changed, 64 insertions(+), 72 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/alias/entity-alias-select.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/alias/entity-alias-select.component.html index f2a2ee4b9c..882e27b01b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/alias/entity-alias-select.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/alias/entity-alias-select.component.html @@ -74,8 +74,7 @@ - {{ translate.get('entity.no-alias-matching', - {alias: truncate.transform(searchText, true, 6, '...')}) | async }} + {{ 'entity.no-alias-matching' | translate : {alias: (searchText | truncate: true: 6: '...')} }} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/alias/entity-alias-select.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/alias/entity-alias-select.component.ts index 2d170f55e3..1af25bd01e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/alias/entity-alias-select.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/alias/entity-alias-select.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, ElementRef, forwardRef, Input, OnInit, SkipSelf, ViewChild } from '@angular/core'; +import { Component, DestroyRef, ElementRef, forwardRef, Input, OnInit, SkipSelf, ViewChild } from '@angular/core'; import { ControlValueAccessor, FormBuilder, @@ -26,18 +26,17 @@ import { } from '@angular/forms'; import { Observable, of } from 'rxjs'; import { map, mergeMap, share, tap } from 'rxjs/operators'; -import { TranslateService } from '@ngx-translate/core'; import { EntityType } from '@shared/models/entity-type.models'; import { EntityService } from '@core/http/entity.service'; import { coerceBoolean } from '@shared/decorators/coercion'; import { EntityAlias } from '@shared/models/alias.models'; import { IAliasController } from '@core/api/widget-api.models'; -import { TruncatePipe } from '@shared/pipe/truncate.pipe'; -import { MatAutocomplete, MatAutocompleteTrigger } from '@angular/material/autocomplete'; +import { MatAutocomplete } from '@angular/material/autocomplete'; import { EntityAliasSelectCallbacks } from './entity-alias-select.component.models'; import { ENTER } from '@angular/cdk/keycodes'; import { ErrorStateMatcher } from '@angular/material/core'; import { MatFormFieldAppearance, SubscriptSizing } from '@angular/material/form-field'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-entity-alias-select', @@ -47,11 +46,7 @@ import { MatFormFieldAppearance, SubscriptSizing } from '@angular/material/form- provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => EntityAliasSelectComponent), multi: true - }/*, - { - provide: ErrorStateMatcher, - useExisting: EntityAliasSelectComponent - }*/] + }] }) export class EntityAliasSelectComponent implements ControlValueAccessor, OnInit, ErrorStateMatcher { @@ -72,7 +67,6 @@ export class EntityAliasSelectComponent implements ControlValueAccessor, OnInit, showLabel: boolean; @ViewChild('entityAliasAutocomplete') entityAliasAutocomplete: MatAutocomplete; - @ViewChild('autocomplete', { read: MatAutocompleteTrigger }) autoCompleteTrigger: MatAutocompleteTrigger; @Input() @coerceBoolean() @@ -93,21 +87,18 @@ export class EntityAliasSelectComponent implements ControlValueAccessor, OnInit, @ViewChild('entityAliasInput', {static: true}) entityAliasInput: ElementRef; - entityAliasList: Array = []; - filteredEntityAliases: Observable>; searchText = ''; private dirty = false; - + private entityAliasList: Array = []; private propagateChange = (_v: any) => { }; constructor(@SkipSelf() private errorStateMatcher: ErrorStateMatcher, private entityService: EntityService, - public translate: TranslateService, - public truncate: TruncatePipe, - private fb: FormBuilder) { + private fb: FormBuilder, + private destroyRef: DestroyRef) { this.selectEntityAliasFormGroup = this.fb.group({ entityAlias: [null] }); @@ -121,15 +112,7 @@ export class EntityAliasSelectComponent implements ControlValueAccessor, OnInit, } ngOnInit() { - const entityAliases = this.aliasController.getEntityAliases(); - for (const aliasId of Object.keys(entityAliases)) { - if (this.allowedEntityTypes && this.allowedEntityTypes.length) { - if (!this.entityService.filterAliasByEntityTypes(entityAliases[aliasId], this.allowedEntityTypes)) { - continue; - } - } - this.entityAliasList.push(entityAliases[aliasId]); - } + this.loadEntityAliases(); this.filteredEntityAliases = this.selectEntityAliasFormGroup.get('entityAlias').valueChanges .pipe( @@ -149,6 +132,12 @@ export class EntityAliasSelectComponent implements ControlValueAccessor, OnInit, mergeMap(name => this.fetchEntityAliases(name) ), share() ); + + this.aliasController.entityAliasesChanged.pipe( + takeUntilDestroyed(this.destroyRef), + ).subscribe(() => { + this.loadEntityAliases(); + }); } isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean { @@ -271,4 +260,18 @@ export class EntityAliasSelectComponent implements ControlValueAccessor, OnInit, ); } } + + private loadEntityAliases(): void { + this.entityAliasList = []; + const entityAliases = this.aliasController.getEntityAliases(); + for (const aliasId of Object.keys(entityAliases)) { + if (this.allowedEntityTypes?.length) { + if (!this.entityService.filterAliasByEntityTypes(entityAliases[aliasId], this.allowedEntityTypes)) { + continue; + } + } + this.entityAliasList.push(entityAliases[aliasId]); + } + this.dirty = true; + } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/filter/filter-select.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/filter/filter-select.component.html index ff1bcb06a4..7b11058228 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/filter/filter-select.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/filter/filter-select.component.html @@ -66,8 +66,7 @@ - {{ translate.get('filter.no-filter-matching', - {filter: truncate.transform(searchText, true, 6, '...')}) | async }} + {{ 'filter.no-filter-matching' | translate : {filter: (searchText | truncate: true: 6: '...')} }} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/filter/filter-select.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/filter/filter-select.component.ts index 6484f2ca18..abc6f77e31 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/filter/filter-select.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/filter/filter-select.component.ts @@ -14,31 +14,27 @@ /// limitations under the License. /// -import { AfterViewInit, Component, ElementRef, forwardRef, Input, OnInit, SkipSelf, ViewChild } from '@angular/core'; +import { Component, DestroyRef, ElementRef, forwardRef, Input, OnInit, SkipSelf, ViewChild } from '@angular/core'; import { ControlValueAccessor, - UntypedFormBuilder, - UntypedFormControl, - UntypedFormGroup, FormGroupDirective, NG_VALUE_ACCESSOR, - NgForm + NgForm, + UntypedFormBuilder, + UntypedFormControl, + UntypedFormGroup } from '@angular/forms'; import { Observable, of } from 'rxjs'; import { map, mergeMap, share, tap } from 'rxjs/operators'; -import { Store } from '@ngrx/store'; -import { AppState } from '@app/core/core.state'; -import { TranslateService } from '@ngx-translate/core'; -import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { IAliasController } from '@core/api/widget-api.models'; -import { TruncatePipe } from '@shared/pipe/truncate.pipe'; -import { MatAutocomplete, MatAutocompleteTrigger } from '@angular/material/autocomplete'; +import { MatAutocomplete } from '@angular/material/autocomplete'; import { ENTER } from '@angular/cdk/keycodes'; import { ErrorStateMatcher } from '@angular/material/core'; import { FilterSelectCallbacks } from './filter-select.component.models'; import { Filter } from '@shared/models/query/query.models'; import { coerceBoolean } from '@shared/decorators/coercion'; import { MatFormFieldAppearance, SubscriptSizing } from '@angular/material/form-field'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-filter-select', @@ -54,7 +50,7 @@ import { MatFormFieldAppearance, SubscriptSizing } from '@angular/material/form- useExisting: FilterSelectComponent }] }) -export class FilterSelectComponent implements ControlValueAccessor, OnInit, AfterViewInit, ErrorStateMatcher { +export class FilterSelectComponent implements ControlValueAccessor, OnInit, ErrorStateMatcher { selectFilterFormGroup: UntypedFormGroup; @@ -81,40 +77,27 @@ export class FilterSelectComponent implements ControlValueAccessor, OnInit, Afte subscriptSizing: SubscriptSizing = 'fixed'; @ViewChild('filterAutocomplete') filterAutocomplete: MatAutocomplete; - @ViewChild('autocomplete', { read: MatAutocompleteTrigger }) autoCompleteTrigger: MatAutocompleteTrigger; - - private requiredValue: boolean; - get tbRequired(): boolean { - return this.requiredValue; - } @Input() - set tbRequired(value: boolean) { - this.requiredValue = coerceBooleanProperty(value); - } + @coerceBoolean() + tbRequired: boolean; @Input() disabled: boolean; @ViewChild('filterInput', {static: true}) filterInput: ElementRef; - filterList: Array = []; - filteredFilters: Observable>; searchText = ''; private dirty = false; + private filterList: Array = []; + private propagateChange = (_v: any) => { }; - private creatingFilter = false; - - private propagateChange = (v: any) => { }; - - constructor(private store: Store, - @SkipSelf() private errorStateMatcher: ErrorStateMatcher, - public translate: TranslateService, - public truncate: TruncatePipe, - private fb: UntypedFormBuilder) { + constructor(@SkipSelf() private errorStateMatcher: ErrorStateMatcher, + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { this.selectFilterFormGroup = this.fb.group({ filter: [null] }); @@ -124,19 +107,16 @@ export class FilterSelectComponent implements ControlValueAccessor, OnInit, Afte this.propagateChange = fn; } - registerOnTouched(fn: any): void { + registerOnTouched(_fn: any): void { } ngOnInit() { - const filters = this.aliasController.getFilters(); - for (const filterId of Object.keys(filters)) { - this.filterList.push(filters[filterId]); - } + this.loadFilters(); this.filteredFilters = this.selectFilterFormGroup.get('filter').valueChanges .pipe( tap(value => { - let modelValue; + let modelValue: Filter; if (typeof value === 'string' || !value) { modelValue = null; } else { @@ -151,6 +131,12 @@ export class FilterSelectComponent implements ControlValueAccessor, OnInit, Afte mergeMap(name => this.fetchFilters(name) ), share() ); + + this.aliasController.filtersChanged.pipe( + takeUntilDestroyed(this.destroyRef), + ).subscribe(() => { + this.loadFilters(); + }); } isErrorState(control: UntypedFormControl | null, form: FormGroupDirective | NgForm | null): boolean { @@ -159,8 +145,6 @@ export class FilterSelectComponent implements ControlValueAccessor, OnInit, Afte return originalErrorState || customErrorState; } - ngAfterViewInit(): void {} - setDisabledState(isDisabled: boolean): void { this.disabled = isDisabled; if (this.disabled) { @@ -227,7 +211,7 @@ export class FilterSelectComponent implements ControlValueAccessor, OnInit, Afte } textIsNotEmpty(text: string): boolean { - return (text && text != null && text.length > 0) ? true : false; + return text?.length > 0; } filterEnter($event: KeyboardEvent) { @@ -242,7 +226,6 @@ export class FilterSelectComponent implements ControlValueAccessor, OnInit, Afte createFilter($event: Event, filter: string, focusOnCancel = true) { $event.preventDefault(); $event.stopPropagation(); - this.creatingFilter = true; if (this.callbacks && this.callbacks.createFilter) { this.callbacks.createFilter(filter).subscribe((newFilter) => { if (!newFilter) { @@ -253,7 +236,6 @@ export class FilterSelectComponent implements ControlValueAccessor, OnInit, Afte }, 0); } } else { - this.filterList.push(newFilter); this.modelValue = newFilter.id; this.selectFilterFormGroup.get('filter').patchValue(newFilter, {emitEvent: true}); this.propagateChange(this.modelValue); @@ -262,4 +244,13 @@ export class FilterSelectComponent implements ControlValueAccessor, OnInit, Afte ); } } + + private loadFilters(): void { + this.filterList = []; + const filters = this.aliasController.getFilters(); + for (const filterId of Object.keys(filters)) { + this.filterList.push(filters[filterId]); + } + this.dirty = true; + } } From 87b9f74d539a8f7e2f53aefe5741eb72d22a77c5 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 23 Jul 2025 18:03:04 +0300 Subject: [PATCH 02/12] UI: Fixed domain conditionally disable redirect URI input in view mode --- .../home/pages/admin/oauth2/domains/domain.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2/domains/domain.component.html b/ui-ngx/src/app/modules/home/pages/admin/oauth2/domains/domain.component.html index 4f37d8f480..9873ca29f7 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2/domains/domain.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2/domains/domain.component.html @@ -34,7 +34,7 @@
admin.oauth2.redirect-url-template - + Date: Thu, 24 Jul 2025 11:41:14 +0300 Subject: [PATCH 03/12] UI: fixed Trendz settings update in AuthState --- ui-ngx/src/app/core/auth/auth.actions.ts | 10 +++++++++- ui-ngx/src/app/core/auth/auth.reducer.ts | 3 +++ .../home/pages/admin/trendz-settings.component.ts | 7 ++++++- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/app/core/auth/auth.actions.ts b/ui-ngx/src/app/core/auth/auth.actions.ts index dedd67bcb9..f9d63f4182 100644 --- a/ui-ngx/src/app/core/auth/auth.actions.ts +++ b/ui-ngx/src/app/core/auth/auth.actions.ts @@ -18,6 +18,7 @@ import { Action } from '@ngrx/store'; import { AuthUser, User } from '@shared/models/user.model'; import { AuthPayload } from '@core/auth/auth.models'; import { UserSettings } from '@shared/models/user-settings.models'; +import { TrendzSettings } from "@shared/models/trendz-settings.models"; export enum AuthActionTypes { AUTHENTICATED = '[Auth] Authenticated', @@ -31,6 +32,7 @@ export enum AuthActionTypes { UPDATE_OPENED_MENU_SECTION = '[Preferences] Update Opened Menu Section', PUT_USER_SETTINGS = '[Preferences] Put user settings', DELETE_USER_SETTINGS = '[Preferences] Delete user settings', + UPDATE_TRENDZ_SETTINGS = '[Auth] Update Trendz Settings', } export class ActionAuthAuthenticated implements Action { @@ -97,7 +99,13 @@ export class ActionPreferencesDeleteUserSettings implements Action { constructor(readonly payload: Array>) {} } +export class ActionAuthUpdateTrendzSettings implements Action { + readonly type = AuthActionTypes.UPDATE_TRENDZ_SETTINGS; + + constructor(readonly payload: { trendzSettings: TrendzSettings }) {} +} + export type AuthActions = ActionAuthAuthenticated | ActionAuthUnauthenticated | ActionAuthLoadUser | ActionAuthUpdateUserDetails | ActionAuthUpdateLastPublicDashboardId | ActionAuthUpdateHasRepository | ActionPreferencesUpdateOpenedMenuSection | ActionPreferencesPutUserSettings | ActionPreferencesDeleteUserSettings | - ActionAuthUpdateAuthUser | ActionUpdateMobileQrCodeEnabled; + ActionAuthUpdateAuthUser | ActionUpdateMobileQrCodeEnabled | ActionAuthUpdateTrendzSettings; diff --git a/ui-ngx/src/app/core/auth/auth.reducer.ts b/ui-ngx/src/app/core/auth/auth.reducer.ts index fde778284d..6752679261 100644 --- a/ui-ngx/src/app/core/auth/auth.reducer.ts +++ b/ui-ngx/src/app/core/auth/auth.reducer.ts @@ -99,6 +99,9 @@ export const authReducer = ( action.payload.forEach(path => unset(userSettings, path)); return { ...state, ...{ userSettings }}; + case AuthActionTypes.UPDATE_TRENDZ_SETTINGS: + return { ...state, ...action.payload }; + default: return state; } diff --git a/ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.ts b/ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.ts index 16ae6bf1da..7aec9cd9eb 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.ts @@ -21,6 +21,9 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { TrendzSettingsService } from '@core/http/trendz-settings.service'; import { TrendzSettings } from '@shared/models/trendz-settings.models'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { Store } from "@ngrx/store"; +import { AppState } from "@core/core.state"; +import { ActionAuthUpdateTrendzSettings } from "@core/auth/auth.actions"; @Component({ selector: 'tb-trendz-settings', @@ -31,7 +34,8 @@ export class TrendzSettingsComponent extends PageComponent implements OnInit, Ha trendzSettingsForm: FormGroup; - constructor(private fb: FormBuilder, + constructor(protected store: Store, + private fb: FormBuilder, private trendzSettingsService: TrendzSettingsService, private destroyRef: DestroyRef) { super(); @@ -93,6 +97,7 @@ export class TrendzSettingsComponent extends PageComponent implements OnInit, Ha this.trendzSettingsService.saveTrendzSettings(trendzSettings) .subscribe(() => { this.setTrendzSettings(trendzSettings); + this.store.dispatch(new ActionAuthUpdateTrendzSettings({ trendzSettings: trendzSettings})) }) } } From 310a412e51d0db0c699509aa70d94be3480f2244 Mon Sep 17 00:00:00 2001 From: yuliaklochai Date: Thu, 24 Jul 2025 12:46:56 +0300 Subject: [PATCH 04/12] UI: fixed Trendz settings after review --- ui-ngx/src/app/core/auth/auth.actions.ts | 2 +- ui-ngx/src/app/core/auth/auth.reducer.ts | 2 +- .../app/modules/home/pages/admin/trendz-settings.component.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/app/core/auth/auth.actions.ts b/ui-ngx/src/app/core/auth/auth.actions.ts index f9d63f4182..3e2b22ff63 100644 --- a/ui-ngx/src/app/core/auth/auth.actions.ts +++ b/ui-ngx/src/app/core/auth/auth.actions.ts @@ -102,7 +102,7 @@ export class ActionPreferencesDeleteUserSettings implements Action { export class ActionAuthUpdateTrendzSettings implements Action { readonly type = AuthActionTypes.UPDATE_TRENDZ_SETTINGS; - constructor(readonly payload: { trendzSettings: TrendzSettings }) {} + constructor(readonly payload: TrendzSettings) {} } export type AuthActions = ActionAuthAuthenticated | ActionAuthUnauthenticated | diff --git a/ui-ngx/src/app/core/auth/auth.reducer.ts b/ui-ngx/src/app/core/auth/auth.reducer.ts index 6752679261..785f40ce3c 100644 --- a/ui-ngx/src/app/core/auth/auth.reducer.ts +++ b/ui-ngx/src/app/core/auth/auth.reducer.ts @@ -100,7 +100,7 @@ export const authReducer = ( return { ...state, ...{ userSettings }}; case AuthActionTypes.UPDATE_TRENDZ_SETTINGS: - return { ...state, ...action.payload }; + return { ...state, trendzSettings: action.payload }; default: return state; diff --git a/ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.ts b/ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.ts index 7aec9cd9eb..f477b6fdaf 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.ts @@ -97,7 +97,7 @@ export class TrendzSettingsComponent extends PageComponent implements OnInit, Ha this.trendzSettingsService.saveTrendzSettings(trendzSettings) .subscribe(() => { this.setTrendzSettings(trendzSettings); - this.store.dispatch(new ActionAuthUpdateTrendzSettings({ trendzSettings: trendzSettings})) + this.store.dispatch(new ActionAuthUpdateTrendzSettings(trendzSettings)) }) } } From cff4f35b9a21a01d843f03cf7a0a4547a203fdfe Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Thu, 24 Jul 2025 13:21:19 +0300 Subject: [PATCH 05/12] Auto-remove alarms when originator is deleted --- .../controller/AlarmCommentController.java | 16 ++-- .../server/controller/AlarmController.java | 60 +++++-------- .../alarm/DefaultTbAlarmCommentService.java | 3 +- .../AlarmsDeletionTaskProcessor.java | 40 ++++----- .../housekeeper/HousekeeperServiceTest.java | 89 +++++++++++++++++-- .../server/common/data/alarm/Alarm.java | 7 +- .../common/data/alarm/AlarmComment.java | 7 ++ .../server/common/data/alarm/AlarmQuery.java | 3 - .../common/data/alarm/AlarmSeverity.java | 3 - .../server/common/data/alarm/AlarmStatus.java | 3 - .../server/dao/alarm/BaseAlarmService.java | 9 -- .../server/dao/sql/alarm/AlarmRepository.java | 3 - .../engine/api/RuleEngineAlarmService.java | 3 - 13 files changed, 147 insertions(+), 99 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/AlarmCommentController.java b/application/src/main/java/org/thingsboard/server/controller/AlarmCommentController.java index 9d123319ef..b12a253b36 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AlarmCommentController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AlarmCommentController.java @@ -19,12 +19,13 @@ import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.Schema; import lombok.RequiredArgsConstructor; import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmComment; @@ -54,6 +55,7 @@ import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LI @RequiredArgsConstructor @RequestMapping("/api") public class AlarmCommentController extends BaseController { + public static final String ALARM_ID = "alarmId"; public static final String ALARM_COMMENT_ID = "commentId"; @@ -68,8 +70,7 @@ public class AlarmCommentController extends BaseController { "\n\n If comment type is not specified the default value 'OTHER' will be saved. If 'alarmId' or 'userId' specified in body it will be ignored." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/alarm/{alarmId}/comment", method = RequestMethod.POST) - @ResponseBody + @PostMapping(value = "/alarm/{alarmId}/comment") public AlarmComment saveAlarmComment(@Parameter(description = ALARM_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_ID) String strAlarmId, @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "A JSON value representing the comment.") @RequestBody AlarmComment alarmComment) throws ThingsboardException { checkParameter(ALARM_ID, strAlarmId); @@ -82,8 +83,7 @@ public class AlarmCommentController extends BaseController { @ApiOperation(value = "Delete Alarm comment (deleteAlarmComment)", notes = "Deletes the Alarm comment. Referencing non-existing Alarm comment Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/alarm/{alarmId}/comment/{commentId}", method = RequestMethod.DELETE) - @ResponseBody + @DeleteMapping(value = "/alarm/{alarmId}/comment/{commentId}") public void deleteAlarmComment(@Parameter(description = ALARM_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_ID) String strAlarmId, @Parameter(description = ALARM_COMMENT_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_COMMENT_ID) String strCommentId) throws ThingsboardException { checkParameter(ALARM_ID, strAlarmId); AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); @@ -98,8 +98,7 @@ public class AlarmCommentController extends BaseController { notes = "Returns a page of alarm comments for specified alarm. " + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/alarm/{alarmId}/comment", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/alarm/{alarmId}/comment") public PageData getAlarmComments( @Parameter(description = ALARM_ID_PARAM_DESCRIPTION, required = true) @PathVariable(ALARM_ID) String strAlarmId, @@ -118,4 +117,5 @@ public class AlarmCommentController extends BaseController { PageLink pageLink = createPageLink(pageSize, page, null, sortProperty, sortOrder); return checkNotNull(alarmCommentService.findAlarmComments(alarm.getTenantId(), alarmId, pageLink)); } + } diff --git a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java index dd8d883145..f5a04e1e48 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java @@ -21,12 +21,13 @@ import io.swagger.v3.oas.annotations.media.Schema; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import org.thingsboard.server.common.data.EntitySubtype; @@ -58,7 +59,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.UUID; -import java.util.concurrent.ExecutionException; import static org.thingsboard.server.controller.ControllerConstants.ALARM_ID_PARAM_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.ALARM_INFO_DESCRIPTION; @@ -104,8 +104,7 @@ public class AlarmController extends BaseController { @ApiOperation(value = "Get Alarm (getAlarmById)", notes = "Fetch the Alarm object based on the provided Alarm Id. " + ALARM_SECURITY_CHECK) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/alarm/{alarmId}", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/alarm/{alarmId}") public Alarm getAlarmById(@Parameter(description = ALARM_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_ID) String strAlarmId) throws ThingsboardException { checkParameter(ALARM_ID, strAlarmId); @@ -117,8 +116,7 @@ public class AlarmController extends BaseController { notes = "Fetch the Alarm Info object based on the provided Alarm Id. " + ALARM_SECURITY_CHECK + ALARM_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/alarm/info/{alarmId}", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/alarm/info/{alarmId}") public AlarmInfo getAlarmInfoById(@Parameter(description = ALARM_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_ID) String strAlarmId) throws ThingsboardException { checkParameter(ALARM_ID, strAlarmId); @@ -136,11 +134,9 @@ public class AlarmController extends BaseController { "If the user tries to create 'HighTemperature' alarm for the same device again, the previous alarm will be updated (the 'end_ts' will be set to current timestamp). " + "If the user clears the alarm (see 'Clear Alarm(clearAlarm)'), than new alarm with the same type and same device may be created. " + "Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Alarm entity. " + - TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH - ) + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/alarm", method = RequestMethod.POST) - @ResponseBody + @PostMapping(value = "/alarm") public Alarm saveAlarm(@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "A JSON value representing the alarm.") @RequestBody Alarm alarm) throws ThingsboardException { alarm.setTenantId(getTenantId()); checkNotNull(alarm.getOriginator()); @@ -155,8 +151,7 @@ public class AlarmController extends BaseController { @ApiOperation(value = "Delete Alarm (deleteAlarm)", notes = "Deletes the Alarm. Referencing non-existing Alarm Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/alarm/{alarmId}", method = RequestMethod.DELETE) - @ResponseBody + @DeleteMapping(value = "/alarm/{alarmId}") public boolean deleteAlarm(@Parameter(description = ALARM_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_ID) String strAlarmId) throws ThingsboardException { checkParameter(ALARM_ID, strAlarmId); AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); @@ -169,7 +164,7 @@ public class AlarmController extends BaseController { "Once acknowledged, the 'ack_ts' field will be set to current timestamp and special rule chain event 'ALARM_ACK' will be generated. " + "Referencing non-existing Alarm Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/alarm/{alarmId}/ack", method = RequestMethod.POST) + @PostMapping(value = "/alarm/{alarmId}/ack") @ResponseStatus(value = HttpStatus.OK) public AlarmInfo ackAlarm(@Parameter(description = ALARM_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_ID) String strAlarmId) throws Exception { checkParameter(ALARM_ID, strAlarmId); @@ -184,7 +179,7 @@ public class AlarmController extends BaseController { "Once cleared, the 'clear_ts' field will be set to current timestamp and special rule chain event 'ALARM_CLEAR' will be generated. " + "Referencing non-existing Alarm Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/alarm/{alarmId}/clear", method = RequestMethod.POST) + @PostMapping(value = "/alarm/{alarmId}/clear") @ResponseStatus(value = HttpStatus.OK) public AlarmInfo clearAlarm(@Parameter(description = ALARM_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_ID) String strAlarmId) throws Exception { checkParameter(ALARM_ID, strAlarmId); @@ -200,7 +195,7 @@ public class AlarmController extends BaseController { "(or ALARM_REASSIGNED in case of assigning already assigned alarm) will be generated. " + "Referencing non-existing Alarm Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/alarm/{alarmId}/assign/{assigneeId}", method = RequestMethod.POST) + @PostMapping(value = "/alarm/{alarmId}/assign/{assigneeId}") @ResponseStatus(value = HttpStatus.OK) public Alarm assignAlarm(@Parameter(description = ALARM_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_ID) String strAlarmId, @@ -221,7 +216,7 @@ public class AlarmController extends BaseController { "Once unassigned, the 'assign_ts' field will be set to current timestamp and special rule chain event 'ALARM_UNASSIGNED' will be generated. " + "Referencing non-existing Alarm Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/alarm/{alarmId}/assign", method = RequestMethod.DELETE) + @DeleteMapping(value = "/alarm/{alarmId}/assign") @ResponseStatus(value = HttpStatus.OK) public Alarm unassignAlarm(@Parameter(description = ALARM_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_ID) String strAlarmId @@ -236,8 +231,7 @@ public class AlarmController extends BaseController { notes = "Returns a page of alarms for the selected entity. Specifying both parameters 'searchStatus' and 'status' at the same time will cause an error. " + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/alarm/{entityType}/{entityId}", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/alarm/{entityType}/{entityId}") public PageData getAlarms( @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable(ENTITY_TYPE) String strEntityType, @@ -265,7 +259,7 @@ public class AlarmController extends BaseController { @RequestParam(required = false) Long endTime, @Parameter(description = ALARM_QUERY_FETCH_ORIGINATOR_DESCRIPTION) @RequestParam(required = false) Boolean fetchOriginator - ) throws ThingsboardException, ExecutionException, InterruptedException { + ) throws ThingsboardException { checkParameter("EntityId", strEntityId); checkParameter("EntityType", strEntityType); EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId); @@ -292,8 +286,7 @@ public class AlarmController extends BaseController { "Specifying both parameters 'searchStatus' and 'status' at the same time will cause an error. " + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/alarms", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/alarms") public PageData getAllAlarms( @Parameter(description = ALARM_QUERY_SEARCH_STATUS_DESCRIPTION, schema = @Schema(allowableValues = {"ANY", "ACTIVE", "CLEARED", "ACK", "UNACK"})) @RequestParam(required = false) String searchStatus, @@ -317,7 +310,7 @@ public class AlarmController extends BaseController { @RequestParam(required = false) Long endTime, @Parameter(description = ALARM_QUERY_FETCH_ORIGINATOR_DESCRIPTION) @RequestParam(required = false) Boolean fetchOriginator - ) throws ThingsboardException, ExecutionException, InterruptedException { + ) throws ThingsboardException { AlarmSearchStatus alarmSearchStatus = StringUtils.isEmpty(searchStatus) ? null : AlarmSearchStatus.valueOf(searchStatus); AlarmStatus alarmStatus = StringUtils.isEmpty(status) ? null : AlarmStatus.valueOf(status); if (alarmSearchStatus != null && alarmStatus != null) { @@ -341,8 +334,7 @@ public class AlarmController extends BaseController { notes = "Returns a page of alarms for the selected entity. " + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/v2/alarm/{entityType}/{entityId}", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/v2/alarm/{entityType}/{entityId}") public PageData getAlarmsV2( @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable(ENTITY_TYPE) String strEntityType, @@ -370,7 +362,7 @@ public class AlarmController extends BaseController { @RequestParam(required = false) Long startTime, @Parameter(description = ALARM_QUERY_END_TIME_DESCRIPTION) @RequestParam(required = false) Long endTime - ) throws ThingsboardException, ExecutionException, InterruptedException { + ) throws ThingsboardException { checkParameter("EntityId", strEntityId); checkParameter("EntityType", strEntityType); EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId); @@ -407,8 +399,7 @@ public class AlarmController extends BaseController { "If the user has the authority of 'Customer User', the server returns alarms that belongs to the customer of current user. " + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/v2/alarms", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/v2/alarms") public PageData getAllAlarmsV2( @Parameter(description = ALARM_QUERY_SEARCH_STATUS_ARRAY_DESCRIPTION, array = @ArraySchema(schema = @Schema(type = "string", allowableValues = {"ANY", "ACTIVE", "CLEARED", "ACK", "UNACK"}))) @RequestParam(required = false) String[] statusList, @@ -432,7 +423,7 @@ public class AlarmController extends BaseController { @RequestParam(required = false) Long startTime, @Parameter(description = ALARM_QUERY_END_TIME_DESCRIPTION) @RequestParam(required = false) Long endTime - ) throws ThingsboardException, ExecutionException, InterruptedException { + ) throws ThingsboardException { List alarmStatusList = new ArrayList<>(); if (statusList != null) { for (String strStatus : statusList) { @@ -465,11 +456,9 @@ public class AlarmController extends BaseController { @ApiOperation(value = "Get Highest Alarm Severity (getHighestAlarmSeverity)", notes = "Search the alarms by originator ('entityType' and entityId') and optional 'status' or 'searchStatus' filters and returns the highest AlarmSeverity(CRITICAL, MAJOR, MINOR, WARNING or INDETERMINATE). " + - "Specifying both parameters 'searchStatus' and 'status' at the same time will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH - ) + "Specifying both parameters 'searchStatus' and 'status' at the same time will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/alarm/highestSeverity/{entityType}/{entityId}", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/alarm/highestSeverity/{entityType}/{entityId}") public AlarmSeverity getHighestAlarmSeverity( @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable(ENTITY_TYPE) String strEntityType, @@ -499,8 +488,7 @@ public class AlarmController extends BaseController { @ApiOperation(value = "Get Alarm Types (getAlarmTypes)", notes = "Returns a set of unique alarm types based on alarms that are either owned by the tenant or assigned to the customer which user is performing the request.") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/alarm/types", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/alarm/types") public PageData getAlarmTypes(@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) @RequestParam int pageSize, @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true) @@ -508,7 +496,7 @@ public class AlarmController extends BaseController { @Parameter(description = ALARM_QUERY_TEXT_SEARCH_DESCRIPTION) @RequestParam(required = false) String textSearch, @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"})) - @RequestParam(required = false) String sortOrder) throws ThingsboardException, ExecutionException, InterruptedException { + @RequestParam(required = false) String sortOrder) throws ThingsboardException { PageLink pageLink = createPageLink(pageSize, page, textSearch, "type", sortOrder); return checkNotNull(alarmService.findAlarmTypesByTenantId(getTenantId(), pageLink)); } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmCommentService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmCommentService.java index de4d0ac9cb..73f6e0a861 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmCommentService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmCommentService.java @@ -32,7 +32,7 @@ import org.thingsboard.server.service.entitiy.AbstractTbEntityService; @Service @AllArgsConstructor -public class DefaultTbAlarmCommentService extends AbstractTbEntityService implements TbAlarmCommentService{ +public class DefaultTbAlarmCommentService extends AbstractTbEntityService implements TbAlarmCommentService { @Autowired private AlarmCommentService alarmCommentService; @@ -68,4 +68,5 @@ public class DefaultTbAlarmCommentService extends AbstractTbEntityService implem throw new ThingsboardException("System comment could not be deleted", ThingsboardErrorCode.BAD_REQUEST_PARAMS); } } + } diff --git a/application/src/main/java/org/thingsboard/server/service/housekeeper/processor/AlarmsDeletionTaskProcessor.java b/application/src/main/java/org/thingsboard/server/service/housekeeper/processor/AlarmsDeletionTaskProcessor.java index 55107a8326..d19b26de2d 100644 --- a/application/src/main/java/org/thingsboard/server/service/housekeeper/processor/AlarmsDeletionTaskProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/housekeeper/processor/AlarmsDeletionTaskProcessor.java @@ -43,29 +43,27 @@ public class AlarmsDeletionTaskProcessor extends HousekeeperTaskProcessor> alarms = alarmService.findAlarmIdsByOriginatorId(tenantId, entityId, lastCreatedTime, lastId, 128); - if (alarms.isEmpty()) { - break; - } - - housekeeperClient.submitTask(new AlarmsDeletionHousekeeperTask(tenantId, entityId, alarms.stream().map(TbPair::getFirst).toList())); - - TbPair last = alarms.get(alarms.size() - 1); - lastId = new AlarmId(last.getFirst()); - lastCreatedTime = last.getSecond(); - log.debug("[{}][{}][{}] Submitted task for deleting {} alarms", tenantId, entityType, entityId, alarms.size()); + if (task.getAlarms() == null) { + AlarmId lastId = null; + long lastCreatedTime = 0; + while (true) { + List> alarms = alarmService.findAlarmIdsByOriginatorId(tenantId, entityId, lastCreatedTime, lastId, 128); + if (alarms.isEmpty()) { + break; } - } else { - for (UUID alarmId : task.getAlarms()) { - alarmService.delAlarm(tenantId, new AlarmId(alarmId)); - } - log.debug("[{}][{}][{}] Deleted {} alarms", tenantId, entityType, entityId, task.getAlarms().size()); + + housekeeperClient.submitTask(new AlarmsDeletionHousekeeperTask(tenantId, entityId, alarms.stream().map(TbPair::getFirst).toList())); + + TbPair last = alarms.get(alarms.size() - 1); + lastId = new AlarmId(last.getFirst()); + lastCreatedTime = last.getSecond(); + log.debug("[{}][{}][{}] Submitted task for deleting {} alarms", tenantId, entityType, entityId, alarms.size()); } + } else { + for (UUID alarmId : task.getAlarms()) { + alarmService.delAlarm(tenantId, new AlarmId(alarmId)); + } + log.debug("[{}][{}][{}] Deleted {} alarms", tenantId, entityType, entityId, task.getAlarms().size()); } int count = alarmService.deleteEntityAlarmRecords(tenantId, entityId); diff --git a/application/src/test/java/org/thingsboard/server/service/housekeeper/HousekeeperServiceTest.java b/application/src/test/java/org/thingsboard/server/service/housekeeper/HousekeeperServiceTest.java index d701ce6113..7aa05281a7 100644 --- a/application/src/test/java/org/thingsboard/server/service/housekeeper/HousekeeperServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/housekeeper/HousekeeperServiceTest.java @@ -31,6 +31,8 @@ import org.thingsboard.rule.engine.metadata.TbGetAttributesNode; import org.thingsboard.rule.engine.metadata.TbGetAttributesNodeConfiguration; import org.thingsboard.server.common.data.ApiUsageState; import org.thingsboard.server.common.data.AttributeScope; +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EventInfo; import org.thingsboard.server.common.data.StringUtils; @@ -77,6 +79,8 @@ import org.thingsboard.server.controller.AbstractControllerTest; import org.thingsboard.server.dao.alarm.AlarmDao; import org.thingsboard.server.dao.alarm.AlarmService; import org.thingsboard.server.dao.attributes.AttributesService; +import org.thingsboard.server.dao.customer.CustomerService; +import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.entity.EntityServiceRegistry; import org.thingsboard.server.dao.event.EventService; import org.thingsboard.server.dao.relation.RelationService; @@ -145,6 +149,10 @@ public class HousekeeperServiceTest extends AbstractControllerTest { private ApiUsageStateDao apiUsageStateDao; @Autowired private EntityServiceRegistry entityServiceRegistry; + @Autowired + private CustomerService customerService; + @Autowired + private DashboardService dashboardService; @SpyBean private TsHistoryDeletionTaskProcessor tsHistoryDeletionTaskProcessor; @@ -243,6 +251,62 @@ public class HousekeeperServiceTest extends AbstractControllerTest { }); } + @Test + public void whenAssetIsDeleted_thenDeleteAllAlarms() throws Exception { + Asset asset = createAsset(); + for (int i = 1; i <= 1000; i++) { + createAlarm(asset.getId()); + } + + doDelete("/api/asset/" + asset.getId()).andExpect(status().isOk()); + + await().atMost(30, TimeUnit.SECONDS).untilAsserted(() -> { + verifyNoAlarms(asset.getId()); + }); + } + + @Test + public void whenDashboardIsDeleted_thenDeleteAllAlarms() throws Exception { + Dashboard dashboard = createDashboard(); + for (int i = 1; i <= 1000; i++) { + createAlarm(dashboard.getId()); + } + + doDelete("/api/dashboard/" + dashboard.getId()).andExpect(status().isOk()); + + await().atMost(30, TimeUnit.SECONDS).untilAsserted(() -> { + verifyNoAlarms(dashboard.getId()); + }); + } + + @Test + public void whenCustomerIsDeleted_thenDeleteAllAlarms() throws Exception { + Customer customer = createCustomer(); + for (int i = 1; i <= 1000; i++) { + createAlarm(customer.getId()); + } + + doDelete("/api/customer/" + customer.getId()).andExpect(status().isOk()); + + await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { + verifyNoAlarms(customer.getId()); + }); + } + + @Test + public void whenUserIsDeleted_thenDeleteAllAlarms() throws Exception { + UserId userId = customerUserId; + for (int i = 1; i <= 1000; i++) { + createAlarm(userId); + } + + doDelete("/api/user/" + userId).andExpect(status().isOk()); + + await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { + verifyNoAlarms(userId); + }); + } + @Test public void whenTenantIsDeleted_thenDeleteAllEntitiesAndCleanUpRelatedData() throws Exception { loginDifferentTenant(); @@ -479,7 +543,6 @@ public class HousekeeperServiceTest extends AbstractControllerTest { eventService.saveAsync(event); await().atMost(10, TimeUnit.SECONDS) .until(() -> !getEvents(entityId).isEmpty()); - } private void createRelation(DeviceId to, AssetId from) { @@ -502,14 +565,14 @@ public class HousekeeperServiceTest extends AbstractControllerTest { assertThat(alarmService.findAlarmIdsByOriginatorId(tenantId, deviceId, 0, null, 10)).isNotEmpty(); } - private void createAlarm(DeviceId deviceId) { - Alarm alarm = doPost("/api/alarm", Alarm.builder() + private void createAlarm(EntityId entityId) { + doPost("/api/alarm", Alarm.builder() .tenantId(tenantId) - .originator(deviceId) + .originator(entityId) .severity(AlarmSeverity.CRITICAL) - .type("test alarm for " + deviceId + " " + RandomStringUtils.randomAlphabetic(10)) + .type("test alarm for " + entityId + " " + RandomStringUtils.randomAlphabetic(10)) .build(), Alarm.class); - assertThat(alarmService.findAlarmIdsByOriginatorId(tenantId, deviceId, 0, null, 10)).isNotEmpty(); + assertThat(alarmService.findAlarmIdsByOriginatorId(tenantId, entityId, 0, null, 10)).isNotEmpty(); } private TsKvEntry getLatestTelemetry(EntityId entityId) throws Exception { @@ -534,6 +597,20 @@ public class HousekeeperServiceTest extends AbstractControllerTest { return doPost("/api/asset", asset, Asset.class); } + private Customer createCustomer() { + Customer customer = new Customer(); + customer.setTenantId(tenantId); + customer.setTitle(StringUtils.randomAlphabetic(10)); + return customerService.saveCustomer(customer); + } + + private Dashboard createDashboard() { + Dashboard dashboard = new Dashboard(); + dashboard.setTenantId(tenantId); + dashboard.setTitle(StringUtils.randomAlphabetic(10)); + return dashboardService.saveDashboard(dashboard); + } + private RuleChainMetaData createRuleChain() { RuleChain ruleChain = new RuleChain(); ruleChain.setTenantId(tenantId); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java index a48a865fe4..519b30a356 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java @@ -37,13 +37,11 @@ import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; +import java.io.Serial; import java.util.List; import java.util.Optional; import java.util.UUID; -/** - * Created by ashvayka on 11.05.17. - */ @Schema @Data @EqualsAndHashCode(callSuper = true) @@ -52,6 +50,9 @@ import java.util.UUID; @JsonIgnoreProperties(ignoreUnknown = true) public class Alarm extends BaseData implements HasName, HasTenantId, HasCustomerId { + @Serial + private static final long serialVersionUID = -1935800187424953611L; + @Schema(description = "JSON object with Tenant Id", accessMode = Schema.AccessMode.READ_ONLY) private TenantId tenantId; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmComment.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmComment.java index 972887e702..0654ba5a9d 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmComment.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmComment.java @@ -30,11 +30,17 @@ import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; +import java.io.Serial; + @Schema @Data @Builder @AllArgsConstructor public class AlarmComment extends BaseData implements HasName { + + @Serial + private static final long serialVersionUID = -5454905526404017592L; + @Schema(description = "JSON object with Alarm id.", accessMode = Schema.AccessMode.READ_ONLY) private AlarmId alarmId; @Schema(description = "JSON object with User id.", accessMode = Schema.AccessMode.READ_ONLY) @@ -85,4 +91,5 @@ public class AlarmComment extends BaseData implements HasName { this.comment = alarmComment.getComment(); this.userId = alarmComment.getUserId(); } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmQuery.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmQuery.java index 83df975ad4..b2d01c8f52 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmQuery.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmQuery.java @@ -22,9 +22,6 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.page.TimePageLink; -/** - * Created by ashvayka on 11.05.17. - */ @Data @Builder @AllArgsConstructor diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmSeverity.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmSeverity.java index 18e989c1bd..1eaa2ea393 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmSeverity.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmSeverity.java @@ -15,9 +15,6 @@ */ package org.thingsboard.server.common.data.alarm; -/** - * Created by ashvayka on 11.05.17. - */ public enum AlarmSeverity { CRITICAL, MAJOR, MINOR, WARNING, INDETERMINATE; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmStatus.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmStatus.java index 9a69ae8e23..c97b0d24f2 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmStatus.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmStatus.java @@ -15,9 +15,6 @@ */ package org.thingsboard.server.common.data.alarm; -/** - * Created by ashvayka on 11.05.17. - */ public enum AlarmStatus { ACTIVE_UNACK, ACTIVE_ACK, CLEARED_UNACK, CLEARED_ACK; diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java index 15d8789ecd..5c3e4f3c79 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java @@ -17,7 +17,6 @@ package org.thingsboard.server.dao.alarm; import com.fasterxml.jackson.databind.JsonNode; -import com.google.common.base.Function; import com.google.common.util.concurrent.FluentFuture; import com.google.common.util.concurrent.ListenableFuture; import lombok.RequiredArgsConstructor; @@ -68,7 +67,6 @@ import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent; import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.ConstraintValidator; -import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.tenant.TenantService; import java.util.ArrayList; @@ -98,7 +96,6 @@ public class BaseAlarmService extends AbstractCachedEntityService alarmDataValidator; @TransactionalEventListener(classes = AlarmTypesCacheEvictEvent.class) @Override @@ -444,12 +441,6 @@ public class BaseAlarmService extends AbstractCachedEntityService T getAndUpdate(TenantId tenantId, AlarmId alarmId, Function function) { - validateId(alarmId, "Alarm id should be specified!"); - Alarm entity = alarmDao.findAlarmById(tenantId, alarmId.getId()); - return function.apply(entity); - } - @Override public Optional> findEntity(TenantId tenantId, EntityId entityId) { return Optional.ofNullable(findAlarmById(tenantId, new AlarmId(entityId.getId()))); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java index b9c1abe416..376f610d66 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java @@ -32,9 +32,6 @@ import java.util.List; import java.util.Set; import java.util.UUID; -/** - * Created by Valerii Sosliuk on 5/21/2017. - */ public interface AlarmRepository extends JpaRepository { @Query("SELECT a FROM AlarmEntity a WHERE a.originatorId = :originatorId AND a.type = :alarmType ORDER BY a.startTs DESC") diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineAlarmService.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineAlarmService.java index 508ebd214e..edcbffcfbc 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineAlarmService.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineAlarmService.java @@ -42,9 +42,6 @@ import org.thingsboard.server.common.data.query.AlarmDataQuery; import java.util.Collection; -/** - * Created by ashvayka on 02.04.18. - */ public interface RuleEngineAlarmService { /* From aa00631246f68904f4d4a940e30c832a22f53c71 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Thu, 24 Jul 2025 14:50:29 +0300 Subject: [PATCH 06/12] Small changes to test --- .../housekeeper/HousekeeperServiceTest.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/service/housekeeper/HousekeeperServiceTest.java b/application/src/test/java/org/thingsboard/server/service/housekeeper/HousekeeperServiceTest.java index 7aa05281a7..a276a9c5ff 100644 --- a/application/src/test/java/org/thingsboard/server/service/housekeeper/HousekeeperServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/housekeeper/HousekeeperServiceTest.java @@ -246,7 +246,7 @@ public class HousekeeperServiceTest extends AbstractControllerTest { doDelete("/api/device/" + device.getId()).andExpect(status().isOk()); - await().atMost(30, TimeUnit.SECONDS).untilAsserted(() -> { + await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { verifyNoAlarms(device.getId()); }); } @@ -260,7 +260,7 @@ public class HousekeeperServiceTest extends AbstractControllerTest { doDelete("/api/asset/" + asset.getId()).andExpect(status().isOk()); - await().atMost(30, TimeUnit.SECONDS).untilAsserted(() -> { + await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { verifyNoAlarms(asset.getId()); }); } @@ -274,7 +274,7 @@ public class HousekeeperServiceTest extends AbstractControllerTest { doDelete("/api/dashboard/" + dashboard.getId()).andExpect(status().isOk()); - await().atMost(30, TimeUnit.SECONDS).untilAsserted(() -> { + await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { verifyNoAlarms(dashboard.getId()); }); } @@ -399,7 +399,7 @@ public class HousekeeperServiceTest extends AbstractControllerTest { doDelete("/api/device/" + device.getId()).andExpect(status().isOk()); int attempts = 2; - await().atMost(30, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS).untilAsserted(() -> { + await().atMost(TIMEOUT, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS).untilAsserted(() -> { for (int i = 0; i <= attempts; i++) { int attempt = i; verify(housekeeperReprocessingService).submitForReprocessing(argThat(getTaskMatcher(device.getId(), HousekeeperTaskType.DELETE_TS_HISTORY, @@ -409,7 +409,7 @@ public class HousekeeperServiceTest extends AbstractControllerTest { assertThat(getTimeseriesHistory(device.getId())).isNotEmpty(); doCallRealMethod().when(tsHistoryDeletionTaskProcessor).process(any()); - await().atMost(30, TimeUnit.SECONDS).untilAsserted(() -> { + await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { assertThat(getTimeseriesHistory(device.getId())).isEmpty(); }); } @@ -443,7 +443,7 @@ public class HousekeeperServiceTest extends AbstractControllerTest { doDelete("/api/device/" + device.getId()).andExpect(status().isOk()); int attempts = 2; - await().atMost(30, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS).untilAsserted(() -> { + await().atMost(TIMEOUT, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS).untilAsserted(() -> { for (int i = 0; i <= attempts; i++) { int attempt = i; verify(housekeeperReprocessingService).submitForReprocessing(argThat(getTaskMatcher(device.getId(), HousekeeperTaskType.DELETE_TS_HISTORY, @@ -457,7 +457,7 @@ public class HousekeeperServiceTest extends AbstractControllerTest { doCallRealMethod().when(tsHistoryDeletionTaskProcessor).process(any()); someExecutor.shutdown(); - await().atMost(30, TimeUnit.SECONDS).untilAsserted(() -> { + await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { assertThat(getTimeseriesHistory(device.getId())).isEmpty(); }); } @@ -473,7 +473,7 @@ public class HousekeeperServiceTest extends AbstractControllerTest { doDelete("/api/device/" + device.getId()).andExpect(status().isOk()); int maxAttempts = 5; - await().atMost(30, TimeUnit.SECONDS).untilAsserted(() -> { + await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { for (int i = 1; i <= maxAttempts; i++) { verifyTaskProcessing(device.getId(), HousekeeperTaskType.DELETE_TS_HISTORY, i); } From caa831e7fa79c722a0fe65e0e10551db7244ddec Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Thu, 24 Jul 2025 16:32:38 +0300 Subject: [PATCH 07/12] Refactoring clean up of entity alarm records after review --- .../housekeeper/processor/AlarmsDeletionTaskProcessor.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/housekeeper/processor/AlarmsDeletionTaskProcessor.java b/application/src/main/java/org/thingsboard/server/service/housekeeper/processor/AlarmsDeletionTaskProcessor.java index d19b26de2d..1c7d312e8b 100644 --- a/application/src/main/java/org/thingsboard/server/service/housekeeper/processor/AlarmsDeletionTaskProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/housekeeper/processor/AlarmsDeletionTaskProcessor.java @@ -59,15 +59,14 @@ public class AlarmsDeletionTaskProcessor extends HousekeeperTaskProcessor Date: Fri, 25 Jul 2025 09:46:21 +0300 Subject: [PATCH 08/12] UI: Fixed issue where fill-height class was not applied to JSON editor --- .../src/app/shared/components/json-object-edit.component.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/shared/components/json-object-edit.component.scss b/ui-ngx/src/app/shared/components/json-object-edit.component.scss index 44a6028717..dfa09ce273 100644 --- a/ui-ngx/src/app/shared/components/json-object-edit.component.scss +++ b/ui-ngx/src/app/shared/components/json-object-edit.component.scss @@ -16,7 +16,7 @@ .tb-json-object { position: relative; - .fill-height { + &.fill-height { height: 100%; } From 1b6c96acd3ec1d4e99eb7ab8c1d6b1ac6ac08448 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 25 Jul 2025 10:34:42 +0300 Subject: [PATCH 09/12] UI: Fix incorrect card padding for title in single switch widget --- .../widget/lib/rpc/single-switch-widget.component.scss | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/single-switch-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/single-switch-widget.component.scss index 2dc70aa70c..7d6a9cf3c3 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/single-switch-widget.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/single-switch-widget.component.scss @@ -41,9 +41,9 @@ $switchColorDisabled: var(--tb-single-switch-color-disabled, #D5D7E5); } > div.tb-single-switch-title-panel { position: absolute; - top: 12px; - left: 12px; - right: 12px; + top: 0; + left: 0; + right: 0; z-index: 2; } .tb-single-switch-content { From 3b5b3fe143349b7440c99bd78a07cb8590d856ce Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 25 Jul 2025 10:54:40 +0300 Subject: [PATCH 10/12] UI: Fix error message in Alarm widget preview when opening Assign field dropdown --- .../widget/lib/alarm/alarms-table-widget.component.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/alarm/alarms-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/alarm/alarms-table-widget.component.ts index b1c0f38ef2..3972ceecb0 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/alarm/alarms-table-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/alarm/alarms-table-widget.component.ts @@ -1187,8 +1187,9 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, } openAlarmAssigneePanel($event: Event, entity: AlarmInfo) { - if ($event) { - $event.stopPropagation(); + $event?.stopPropagation(); + if (entity.id.id === NULL_UUID) { + return } const target = $event.target || $event.currentTarget; const config = new OverlayConfig(); From 53368d3151d76d8794293ab8ca14b3dca5dd721d Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 25 Jul 2025 11:15:30 +0300 Subject: [PATCH 11/12] UI: Stabilize CF argument settings window position on argument type change --- .../calculated-field-arguments-table.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/arguments-table/calculated-field-arguments-table.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/arguments-table/calculated-field-arguments-table.component.ts index 7763fd6f03..28a3f09126 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/arguments-table/calculated-field-arguments-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/arguments-table/calculated-field-arguments-table.component.ts @@ -179,7 +179,7 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces renderer: this.renderer, componentType: CalculatedFieldArgumentPanelComponent, hostView: this.viewContainerRef, - preferredPlacement: isExists ? 'left' : 'right', + preferredPlacement: isExists ? ['left', 'leftTop', 'leftBottom'] : ['topRight', 'right', 'rightTop'], context: ctx, isModal: true }); From dbd6c2a08577824dca5230a99405f1232d94540f Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Fri, 25 Jul 2025 14:02:37 +0300 Subject: [PATCH 12/12] Remove tomcat version override because spring boot uses fixed version. Update nimbus-jose-jwt version to fix vulnerabilities and remove test scope as it used directly by spring-security-oauth2-client. --- pom.xml | 21 ++------------------- rule-engine/rule-engine-components/pom.xml | 5 ----- 2 files changed, 2 insertions(+), 24 deletions(-) diff --git a/pom.xml b/pom.xml index 5d656214b8..21049ba9af 100755 --- a/pom.xml +++ b/pom.xml @@ -38,8 +38,7 @@ ${project.name} /var/log/${pkg.name} /usr/share/${pkg.name} - 3.4.7 - 10.1.43 + 3.4.8 2.4.0-b180830.0359 5.1.5 0.12.5 @@ -52,6 +51,7 @@ 2.16.1 1.3.1 1.10.0 + 10.0.2 4.5.14 2.12.7 4.4.0 @@ -121,7 +121,6 @@ 2.7.3 1.5.6 5.15.0 - 9.37.2 1.3.0 1.2.7 5.0.0 @@ -1131,21 +1130,6 @@ jaxb-api ${javax.xml.bind-api.version} - - org.apache.tomcat.embed - tomcat-embed-core - ${tomcat.version} - - - org.apache.tomcat.embed - tomcat-embed-el - ${tomcat.version} - - - org.apache.tomcat.embed - tomcat-embed-websocket - ${tomcat.version} - org.springframework.boot spring-boot-starter-test @@ -1782,7 +1766,6 @@ com.nimbusds nimbus-jose-jwt ${nimbus-jose-jwt.version} - test org.mock-server diff --git a/rule-engine/rule-engine-components/pom.xml b/rule-engine/rule-engine-components/pom.xml index 530cbfe7d3..3f2ee164df 100644 --- a/rule-engine/rule-engine-components/pom.xml +++ b/rule-engine/rule-engine-components/pom.xml @@ -144,11 +144,6 @@ mockserver-netty test - - com.nimbusds - nimbus-jose-jwt - test - org.mock-server mockserver-client-java