From 831e95b313844583a3dfdf8d75d44b2f9c5d6447 Mon Sep 17 00:00:00 2001 From: Maksym Tsymbarov Date: Tue, 16 Dec 2025 16:08:05 +0200 Subject: [PATCH 01/47] Fixed button changing after dialog closing --- .../lwm2m/lwm2m-bootstrap-config-servers.component.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-config-servers.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-config-servers.component.ts index 86f01e4cd8..f988d7e6aa 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-config-servers.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-config-servers.component.ts @@ -30,7 +30,7 @@ import { TranslateService } from '@ngx-translate/core'; import { DialogService } from '@core/services/dialog.service'; import { MatDialog } from '@angular/material/dialog'; import { Lwm2mBootstrapAddConfigServerDialogComponent } from '@home/components/profile/device/lwm2m/lwm2m-bootstrap-add-config-server-dialog.component'; -import { mergeMap, takeUntil } from 'rxjs/operators'; +import { filter, mergeMap, takeUntil } from 'rxjs/operators'; import { DeviceProfileService } from '@core/http/device-profile.service'; import { Lwm2mSecurityType } from '@shared/models/lwm2m-security-config.models'; @@ -164,10 +164,8 @@ export class Lwm2mBootstrapConfigServersComponent implements OnInit, ControlValu panelClass: ['tb-dialog', 'tb-fullscreen-dialog'] }).afterClosed(); const addServerConfigObs = addDialogObs.pipe( + filter((isBootstrap) => isBootstrap !== null), mergeMap((isBootstrap) => { - if (isBootstrap === null) { - return of(null); - } return this.deviceProfileService.getLwm2mBootstrapSecurityInfoBySecurityType(isBootstrap, Lwm2mSecurityType.NO_SEC); }) ); From ecedfab6425e5a2afe522699ed570eb1d5154a69 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Thu, 18 Dec 2025 22:20:18 +0200 Subject: [PATCH 02/47] lwm2m: fix bug: after reboot Lwm2mDeviceProfileTransportConfiguration is null for lts --- .../server/client/LwM2mClientContextImpl.java | 21 ++++-- .../ota/DefaultLwM2MOtaUpdateService.java | 14 ++-- .../uplink/DefaultLwM2mUplinkMsgHandler.java | 69 ++++++------------- 3 files changed, 43 insertions(+), 61 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java index 36cf1639db..7b3102a55c 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java @@ -289,6 +289,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { @Override public String getObjectIdByKeyNameFromProfile(LwM2mClient client, String keyName) { Lwm2mDeviceProfileTransportConfiguration profile = getProfile(client.getRegistration()); + if (profile == null) throw new IllegalArgumentException(keyName + " is not configured in the device profile! Device profile is null"); for (Map.Entry entry : profile.getObserveAttr().getKeyName().entrySet()) { String k = entry.getKey(); String v = entry.getValue(); @@ -347,6 +348,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { PowerMode powerMode = lwM2MClient.getPowerMode(); if (powerMode == null) { Lwm2mDeviceProfileTransportConfiguration deviceProfile = getProfile(lwM2MClient.getRegistration()); + if (deviceProfile == null) return null; powerMode = deviceProfile.getClientLwM2mSettings().getPowerMode(); } return powerMode; @@ -377,8 +379,8 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { } } } else { - log.warn("Device profile not found! The device profile ID is null. Return Lwm2mDeviceProfileTransportConfiguration with default."); - result = new Lwm2mDeviceProfileTransportConfiguration(); + log.warn("Device profile not found! The device profile ID is null. Return Lwm2mDeviceProfileTransportConfiguration with null."); + result = null; } return result; } @@ -413,6 +415,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { OtherConfiguration profileSettings = null; if (powerMode == null && client.getProfileId() != null) { var clientProfile = getProfile(client.getRegistration()); + if (clientProfile == null) return true; profileSettings = clientProfile.getClientLwM2mSettings(); powerMode = profileSettings.getPowerMode(); } @@ -459,8 +462,10 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { OtherConfiguration profileSettings = null; if (powerMode == null && client.getProfileId() != null) { var clientProfile = getProfile(client.getRegistration()); - profileSettings = clientProfile.getClientLwM2mSettings(); - powerMode = profileSettings.getPowerMode(); + if (clientProfile != null) { + profileSettings = clientProfile.getClientLwM2mSettings(); + powerMode = profileSettings.getPowerMode(); + } } if (powerMode == null || PowerMode.DRX.equals(powerMode)) { client.updateLastUplinkTime(); @@ -515,9 +520,11 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { timeout = client.getEdrxCycle(); } else { var clientProfile = getProfile(client.getRegistration()); - OtherConfiguration clientLwM2mSettings = clientProfile.getClientLwM2mSettings(); - if (PowerMode.E_DRX.equals(clientLwM2mSettings.getPowerMode())) { - timeout = clientLwM2mSettings.getEdrxCycle(); + if (clientProfile != null) { + OtherConfiguration clientLwM2mSettings = clientProfile.getClientLwM2mSettings(); + if (PowerMode.E_DRX.equals(clientLwM2mSettings.getPowerMode())) { + timeout = clientLwM2mSettings.getEdrxCycle(); + } } } if (timeout == null || timeout == 0L) { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java index 325f1e2280..cc89fb8fd8 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java @@ -200,7 +200,8 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl attributesToFetch.add(SOFTWARE_URL); } - var clientSettings = clientContext.getProfile(client.getRegistration()).getClientLwM2mSettings(); + var clientProfile = clientContext.getProfile(client.getRegistration()); + var clientSettings = clientProfile != null ? clientProfile.getClientLwM2mSettings() : null; if (clientSettings != null) { initFwStrategy(client, clientSettings); initSwStrategy(client, clientSettings); @@ -532,7 +533,8 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl } else { strategy = info.getDeliveryMethod() == FirmwareDeliveryMethod.PULL.code ? LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL : LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY; } - Boolean useObject19ForOtaInfo = clientContext.getProfile(client.getRegistration()).getClientLwM2mSettings().getUseObject19ForOtaInfo(); + var clientProfile = clientContext.getProfile(client.getRegistration()); + Boolean useObject19ForOtaInfo = clientProfile != null ? clientProfile.getClientLwM2mSettings().getUseObject19ForOtaInfo() : null; if (useObject19ForOtaInfo != null && useObject19ForOtaInfo){ sendInfoToObject19ForOta(client, FW_INFO_19_INSTANCE_ID, response, otaPackageId); } @@ -558,7 +560,8 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus())) { UUID otaPackageId = new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB()); LwM2MSoftwareUpdateStrategy strategy = info.getStrategy(); - Boolean useObject19ForOtaInfo = clientContext.getProfile(client.getRegistration()).getClientLwM2mSettings().getUseObject19ForOtaInfo(); + var clientProfile = clientContext.getProfile(client.getRegistration()); + Boolean useObject19ForOtaInfo = clientProfile != null ? clientProfile.getClientLwM2mSettings().getUseObject19ForOtaInfo() : null; if (useObject19ForOtaInfo != null && useObject19ForOtaInfo){ sendInfoToObject19ForOta(client, SW_INFO_19_INSTANCE_ID, response, otaPackageId); } @@ -647,8 +650,9 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl LwM2MClientSwOtaInfo info = otaInfoStore.getSw(endpoint); if (info == null) { var profile = clientContext.getProfile(client.getRegistration()); - info = new LwM2MClientSwOtaInfo(endpoint, profile.getClientLwM2mSettings().getSwUpdateResource(), - LwM2MSoftwareUpdateStrategy.fromStrategySwByCode(profile.getClientLwM2mSettings().getSwUpdateStrategy())); + OtherConfiguration clientLwM2mSettings = profile == null ? new OtherConfiguration() : profile.getClientLwM2mSettings(); + info = new LwM2MClientSwOtaInfo(endpoint, clientLwM2mSettings.getSwUpdateResource(), + LwM2MSoftwareUpdateStrategy.fromStrategySwByCode(clientLwM2mSettings.getSwUpdateStrategy())); update(info); } return info; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java index 5e27b936f3..26eb6e83c4 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java @@ -418,7 +418,7 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl }); if (!clients.isEmpty()) { var oldProfile = clientContext.getProfile(clients.get(0).getRegistration()); - this.onDeviceProfileUpdate(clients, oldProfile, deviceProfile); + if (oldProfile != null) this.onDeviceProfileUpdate(clients, oldProfile, deviceProfile); } } catch (Exception e) { log.warn("[{}] failed to update profile: {} [{}]", deviceProfile.getId(), e.getMessage(), deviceProfile); @@ -714,8 +714,10 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl private void onDeviceUpdate(LwM2mClient lwM2MClient, Device device, Optional deviceProfileOpt) { var oldProfile = clientContext.getProfile(lwM2MClient.getRegistration()); - deviceProfileOpt.ifPresent(deviceProfile -> this.onDeviceProfileUpdate(Collections.singletonList(lwM2MClient), oldProfile, deviceProfile)); - lwM2MClient.onDeviceUpdate(device, deviceProfileOpt); + if (oldProfile != null) { + deviceProfileOpt.ifPresent(deviceProfile -> this.onDeviceProfileUpdate(Collections.singletonList(lwM2MClient), oldProfile, deviceProfile)); + lwM2MClient.onDeviceUpdate(device, deviceProfileOpt); + } } /** @@ -729,45 +731,12 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl Set paths = updateResource.getPaths(); ResultsAddKeyValueProto results = new ResultsAddKeyValueProto(); var profile = clientContext.getProfile(registration); - List resultAttributes = new ArrayList<>(); - Set attributes = profile.getObserveAttr().getAttribute().stream() - .filter(paths::contains) - .collect(Collectors.toSet()); - if (!attributes.isEmpty()){ - attributes.stream() - .map(attr -> this.getKvToThingsBoard(attr, registration)) - .filter(Objects::nonNull) - .forEach(resultAttributes::add); - } - List resultTelemetries = new ArrayList<>(); - Set telemetries = profile.getObserveAttr().getTelemetry().stream() - .filter(paths::contains) - .collect(Collectors.toSet()); - if (!telemetries.isEmpty()){ - telemetries.stream() - .map(telemetry -> this.getKvToThingsBoard(telemetry, registration)) - .filter(Objects::nonNull) - .forEach(resultTelemetries::add); - } - if (resultAttributes.size() > 0) { - results.setResultAttributes(resultAttributes); - } - if (resultTelemetries.size() > 0) { - results.setResultTelemetries(resultTelemetries); - } - return results; - } - - private ResultsAddKeyValueProto getParametersFromProfile(Registration registration, Set path) { - if (!path.isEmpty()) { - ResultsAddKeyValueProto results = new ResultsAddKeyValueProto(); - var profile = clientContext.getProfile(registration); + if (profile != null) { List resultAttributes = new ArrayList<>(); Set attributes = profile.getObserveAttr().getAttribute().stream() - .map(LwM2MTransportUtil::fromVersionedIdToObjectId) - .filter(path::contains) + .filter(paths::contains) .collect(Collectors.toSet()); - if (!attributes.isEmpty()){ + if (!attributes.isEmpty()) { attributes.stream() .map(attr -> this.getKvToThingsBoard(attr, registration)) .filter(Objects::nonNull) @@ -775,10 +744,9 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl } List resultTelemetries = new ArrayList<>(); Set telemetries = profile.getObserveAttr().getTelemetry().stream() - .map(LwM2MTransportUtil::fromVersionedIdToObjectId) - .filter(path::contains) + .filter(paths::contains) .collect(Collectors.toSet()); - if (!telemetries.isEmpty()){ + if (!telemetries.isEmpty()) { telemetries.stream() .map(telemetry -> this.getKvToThingsBoard(telemetry, registration)) .filter(Objects::nonNull) @@ -790,14 +758,15 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl if (resultTelemetries.size() > 0) { results.setResultTelemetries(resultTelemetries); } - return results; } - return null; + return results; } private TransportProtos.KeyValueProto getKvToThingsBoard(String pathIdVer, Registration registration) { LwM2mClient lwM2MClient = this.clientContext.getClientByEndpoint(registration.getEndpoint()); - Map names = clientContext.getProfile(lwM2MClient.getRegistration()).getObserveAttr().getKeyName(); + var clientProfile = clientContext.getProfile(lwM2MClient.getRegistration()); + if (clientProfile == null) return null; + Map names = clientProfile.getObserveAttr().getKeyName(); if (names != null && names.containsKey(pathIdVer)) { String resourceName = names.get(pathIdVer); if (resourceName != null && !resourceName.isEmpty()) { @@ -887,10 +856,12 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl private void onDeviceProfileUpdate(List clients, Lwm2mDeviceProfileTransportConfiguration oldProfileTransportConfiguration, DeviceProfile deviceProfile) { if (clientContext.profileUpdate(deviceProfile) != null) { var newProfileTransportConfiguration = clientContext.getProfile(clients.get(0).getRegistration()); - ParametersUpdateAnalyzeResult parametersUpdate = getParametersUpdate(oldProfileTransportConfiguration, newProfileTransportConfiguration); - ParametersObserveAnalyzeResult parametersObserve = getParametersObserve(oldProfileTransportConfiguration.getObserveAttr(), newProfileTransportConfiguration.getObserveAttr(), deviceProfile.getId().getId()); - compareAndSetWriteAttributesObservations(clients, parametersUpdate, parametersObserve); - updateValueOta(clients, newProfileTransportConfiguration, oldProfileTransportConfiguration); + if (newProfileTransportConfiguration != null) { + ParametersUpdateAnalyzeResult parametersUpdate = getParametersUpdate(oldProfileTransportConfiguration, newProfileTransportConfiguration); + ParametersObserveAnalyzeResult parametersObserve = getParametersObserve(oldProfileTransportConfiguration.getObserveAttr(), newProfileTransportConfiguration.getObserveAttr(), deviceProfile.getId().getId()); + compareAndSetWriteAttributesObservations(clients, parametersUpdate, parametersObserve); + updateValueOta(clients, newProfileTransportConfiguration, oldProfileTransportConfiguration); + } } } From 6827949e7fee59e2c335f750b98e09148876b000 Mon Sep 17 00:00:00 2001 From: ArtemDzhereleiko Date: Mon, 22 Dec 2025 11:25:26 +0200 Subject: [PATCH 03/47] UI: Fixed color settings panel update --- .../lib/settings/common/color-range-list.component.ts | 7 ------- .../settings/common/color-settings-panel.component.html | 2 +- .../lib/settings/common/color-settings-panel.component.ts | 7 ------- .../widget/lib/settings/common/gradient.component.ts | 5 ----- 4 files changed, 1 insertion(+), 20 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-list.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-list.component.ts index f7c68fec3d..84d3719ef5 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-list.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-list.component.ts @@ -113,9 +113,6 @@ export class ColorRangeListComponent implements OnInit, ControlValueAccessor, On this.colorRangeListFormGroup.valueChanges.pipe( takeUntil(this.destroy$) ).subscribe(() => this.updateModel()); - this.colorRangeListFormGroup.get('advancedMode').valueChanges.pipe( - takeUntil(this.destroy$) - ).subscribe(() => setTimeout(() => {this.popover?.updatePosition();}, 0)); } ngOnDestroy() { @@ -179,7 +176,6 @@ export class ColorRangeListComponent implements OnInit, ControlValueAccessor, On public removeAdvancedRange(index: number) { (this.colorRangeListFormGroup.get('rangeAdvanced') as UntypedFormArray).removeAt(index); - setTimeout(() => {this.popover?.updatePosition();}, 0); } get advancedRangeFormArray(): UntypedFormArray { @@ -193,7 +189,6 @@ export class ColorRangeListComponent implements OnInit, ControlValueAccessor, On removeRange(index: number) { this.rangeListFormArray.removeAt(index); this.colorRangeListFormGroup.markAsDirty(); - setTimeout(() => {this.popover?.updatePosition();}, 0); } rangeDrop(event: CdkDragDrop, range: string) { @@ -216,7 +211,6 @@ export class ColorRangeListComponent implements OnInit, ControlValueAccessor, On const advancedRangeColorsArray = this.colorRangeListFormGroup.get('rangeAdvanced') as UntypedFormArray; const advancedRangeColorControl = this.fb.control(advancedRange, [advancedRangeValidator]); advancedRangeColorsArray.push(advancedRangeColorControl); - setTimeout(() => {this.popover?.updatePosition();}, 0); } addRange() { @@ -228,7 +222,6 @@ export class ColorRangeListComponent implements OnInit, ControlValueAccessor, On }; this.rangeListFormArray.push(this.colorRangeControl(newRange)); this.colorRangeListFormGroup.markAsDirty(); - setTimeout(() => {this.popover?.updatePosition();}, 0); } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings-panel.component.html index d4e71096b4..bfd6a674b7 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings-panel.component.html @@ -94,7 +94,7 @@ -
+
{ - this.updateValidators(); - setTimeout(() => {this.popover?.updatePosition();}, 0); - }); this.updateValidators(); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/gradient.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/gradient.component.ts index 9078c8db2b..6eca820e27 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/gradient.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/gradient.component.ts @@ -117,9 +117,6 @@ export class GradientComponent implements OnInit, ControlValueAccessor, OnDestro this.gradientFormGroup.valueChanges.pipe( takeUntil(this.destroy$) ).subscribe(() => this.updateModel()); - this.gradientFormGroup.get('advancedMode').valueChanges.pipe( - takeUntil(this.destroy$) - ).subscribe(() => setTimeout(() => {this.popover?.updatePosition();}, 0)); } ngOnDestroy() { @@ -236,7 +233,6 @@ export class GradientComponent implements OnInit, ControlValueAccessor, OnDestro this.gradientListFormArray.removeAt(index); } this.gradientFormGroup.markAsDirty(); - setTimeout(() => {this.popover?.updatePosition();}, 0); } gradientDrop(event: CdkDragDrop, advanced = false) { @@ -255,7 +251,6 @@ export class GradientComponent implements OnInit, ControlValueAccessor, OnDestro this.gradientListFormArray.push(this.colorGradientControl('rgba(0,0,0,0.87)')); } this.gradientFormGroup.markAsDirty(); - setTimeout(() => {this.popover?.updatePosition();}, 0); } updateModel() { From 295d29fc681a24d5b289e2d8cb561064975ded9e Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Mon, 22 Dec 2025 15:16:17 +0200 Subject: [PATCH 04/47] Version set to 4.2.1.1-RC --- application/pom.xml | 2 +- .../service/install/DefaultDatabaseSchemaSettingsService.java | 2 +- common/actor/pom.xml | 2 +- common/cache/pom.xml | 2 +- common/cluster-api/pom.xml | 2 +- common/coap-server/pom.xml | 2 +- common/dao-api/pom.xml | 2 +- common/data/pom.xml | 2 +- common/discovery-api/pom.xml | 2 +- common/edge-api/pom.xml | 2 +- common/edqs/pom.xml | 2 +- common/message/pom.xml | 2 +- common/pom.xml | 2 +- common/proto/pom.xml | 2 +- common/queue/pom.xml | 2 +- common/script/pom.xml | 2 +- common/script/remote-js-client/pom.xml | 2 +- common/script/script-api/pom.xml | 2 +- common/stats/pom.xml | 2 +- common/transport/coap/pom.xml | 2 +- common/transport/http/pom.xml | 2 +- common/transport/lwm2m/pom.xml | 2 +- common/transport/mqtt/pom.xml | 2 +- common/transport/pom.xml | 2 +- common/transport/snmp/pom.xml | 2 +- common/transport/transport-api/pom.xml | 2 +- common/util/pom.xml | 2 +- common/version-control/pom.xml | 2 +- dao/pom.xml | 2 +- edqs/pom.xml | 2 +- monitoring/pom.xml | 2 +- msa/black-box-tests/pom.xml | 2 +- msa/edqs/pom.xml | 2 +- msa/js-executor/package.json | 2 +- msa/js-executor/pom.xml | 2 +- msa/monitoring/pom.xml | 2 +- msa/pom.xml | 2 +- msa/tb-node/pom.xml | 2 +- msa/tb/pom.xml | 2 +- msa/transport/coap/pom.xml | 2 +- msa/transport/http/pom.xml | 2 +- msa/transport/lwm2m/pom.xml | 2 +- msa/transport/mqtt/pom.xml | 2 +- msa/transport/pom.xml | 2 +- msa/transport/snmp/pom.xml | 2 +- msa/vc-executor-docker/pom.xml | 2 +- msa/vc-executor/pom.xml | 2 +- msa/web-ui/package.json | 2 +- msa/web-ui/pom.xml | 2 +- netty-mqtt/pom.xml | 4 ++-- pom.xml | 2 +- rest-client/pom.xml | 2 +- rule-engine/pom.xml | 2 +- rule-engine/rule-engine-api/pom.xml | 2 +- rule-engine/rule-engine-components/pom.xml | 2 +- tools/pom.xml | 2 +- transport/coap/pom.xml | 2 +- transport/http/pom.xml | 2 +- transport/lwm2m/pom.xml | 2 +- transport/mqtt/pom.xml | 2 +- transport/pom.xml | 2 +- transport/snmp/pom.xml | 2 +- ui-ngx/package.json | 2 +- ui-ngx/pom.xml | 2 +- 64 files changed, 65 insertions(+), 65 deletions(-) diff --git a/application/pom.xml b/application/pom.xml index 9c683cbd25..47772dfa97 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1-RC + 4.2.1.1-RC thingsboard application diff --git a/application/src/main/java/org/thingsboard/server/service/install/DefaultDatabaseSchemaSettingsService.java b/application/src/main/java/org/thingsboard/server/service/install/DefaultDatabaseSchemaSettingsService.java index 1917b4e22d..44aa14b782 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/DefaultDatabaseSchemaSettingsService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/DefaultDatabaseSchemaSettingsService.java @@ -30,7 +30,7 @@ public class DefaultDatabaseSchemaSettingsService implements DatabaseSchemaSetti // This list should include all versions that are compatible for the upgrade in 4 digits format (like 4.2.0.0, etc.). // The compatibility cycle usually breaks when we have some scripts written in Java that may not work after a new release. - private static final List SUPPORTED_VERSIONS_FOR_UPGRADE = List.of("4.2.0.0"); + private static final List SUPPORTED_VERSIONS_FOR_UPGRADE = List.of("4.2.0.0", "4.2.1.0"); private final ProjectInfo projectInfo; private final JdbcTemplate jdbcTemplate; diff --git a/common/actor/pom.xml b/common/actor/pom.xml index 4149fe2ad9..6411934f7d 100644 --- a/common/actor/pom.xml +++ b/common/actor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1-RC + 4.2.1.1-RC common org.thingsboard.common diff --git a/common/cache/pom.xml b/common/cache/pom.xml index 6831156fde..a43009b6e1 100644 --- a/common/cache/pom.xml +++ b/common/cache/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1-RC + 4.2.1.1-RC common org.thingsboard.common diff --git a/common/cluster-api/pom.xml b/common/cluster-api/pom.xml index 78ef7980e8..9a75a6b0c3 100644 --- a/common/cluster-api/pom.xml +++ b/common/cluster-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1-RC + 4.2.1.1-RC common org.thingsboard.common diff --git a/common/coap-server/pom.xml b/common/coap-server/pom.xml index b24fd98f5d..1950a9ecc2 100644 --- a/common/coap-server/pom.xml +++ b/common/coap-server/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.2.1-RC + 4.2.1.1-RC common org.thingsboard.common diff --git a/common/dao-api/pom.xml b/common/dao-api/pom.xml index 555b4a54bb..8b0ad31e23 100644 --- a/common/dao-api/pom.xml +++ b/common/dao-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1-RC + 4.2.1.1-RC common org.thingsboard.common diff --git a/common/data/pom.xml b/common/data/pom.xml index d81566f521..a574a6ed07 100644 --- a/common/data/pom.xml +++ b/common/data/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1-RC + 4.2.1.1-RC common org.thingsboard.common diff --git a/common/discovery-api/pom.xml b/common/discovery-api/pom.xml index cb5238a664..9e65abf10a 100644 --- a/common/discovery-api/pom.xml +++ b/common/discovery-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1-RC + 4.2.1.1-RC common org.thingsboard.common diff --git a/common/edge-api/pom.xml b/common/edge-api/pom.xml index 2b7db0cbd3..208e1e7b55 100644 --- a/common/edge-api/pom.xml +++ b/common/edge-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1-RC + 4.2.1.1-RC common org.thingsboard.common diff --git a/common/edqs/pom.xml b/common/edqs/pom.xml index cc5c5db14e..677f5ad5ae 100644 --- a/common/edqs/pom.xml +++ b/common/edqs/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1-RC + 4.2.1.1-RC common org.thingsboard.common diff --git a/common/message/pom.xml b/common/message/pom.xml index c84a4fde2d..f70bc260cf 100644 --- a/common/message/pom.xml +++ b/common/message/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1-RC + 4.2.1.1-RC common org.thingsboard.common diff --git a/common/pom.xml b/common/pom.xml index f45a2bdd28..5467f452d2 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1-RC + 4.2.1.1-RC thingsboard common diff --git a/common/proto/pom.xml b/common/proto/pom.xml index 30d4b0bc68..bab2f8d12b 100644 --- a/common/proto/pom.xml +++ b/common/proto/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1-RC + 4.2.1.1-RC common org.thingsboard.common diff --git a/common/queue/pom.xml b/common/queue/pom.xml index e6ab3a4704..19368c1345 100644 --- a/common/queue/pom.xml +++ b/common/queue/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1-RC + 4.2.1.1-RC common org.thingsboard.common diff --git a/common/script/pom.xml b/common/script/pom.xml index ab5ac032c7..4b7a812737 100644 --- a/common/script/pom.xml +++ b/common/script/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1-RC + 4.2.1.1-RC common org.thingsboard.common diff --git a/common/script/remote-js-client/pom.xml b/common/script/remote-js-client/pom.xml index 1a3e00a88c..45d4698efe 100644 --- a/common/script/remote-js-client/pom.xml +++ b/common/script/remote-js-client/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.2.1-RC + 4.2.1.1-RC script org.thingsboard.common.script diff --git a/common/script/script-api/pom.xml b/common/script/script-api/pom.xml index 316f823a44..3bc2168dfe 100644 --- a/common/script/script-api/pom.xml +++ b/common/script/script-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.2.1-RC + 4.2.1.1-RC script org.thingsboard.common.script diff --git a/common/stats/pom.xml b/common/stats/pom.xml index 4bf24411dc..66011657d8 100644 --- a/common/stats/pom.xml +++ b/common/stats/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.2.1-RC + 4.2.1.1-RC common org.thingsboard.common diff --git a/common/transport/coap/pom.xml b/common/transport/coap/pom.xml index be3c62c057..e9d5dcfc1a 100644 --- a/common/transport/coap/pom.xml +++ b/common/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.2.1-RC + 4.2.1.1-RC transport org.thingsboard.common.transport diff --git a/common/transport/http/pom.xml b/common/transport/http/pom.xml index a136079cb5..caff518fe2 100644 --- a/common/transport/http/pom.xml +++ b/common/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.2.1-RC + 4.2.1.1-RC transport org.thingsboard.common.transport diff --git a/common/transport/lwm2m/pom.xml b/common/transport/lwm2m/pom.xml index 904e359bcc..2bec8f7bcb 100644 --- a/common/transport/lwm2m/pom.xml +++ b/common/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.2.1-RC + 4.2.1.1-RC transport org.thingsboard.common.transport diff --git a/common/transport/mqtt/pom.xml b/common/transport/mqtt/pom.xml index 45f9b76af7..18ff4e96c3 100644 --- a/common/transport/mqtt/pom.xml +++ b/common/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.2.1-RC + 4.2.1.1-RC transport org.thingsboard.common.transport diff --git a/common/transport/pom.xml b/common/transport/pom.xml index f786bcdd0e..feaef2bcce 100644 --- a/common/transport/pom.xml +++ b/common/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1-RC + 4.2.1.1-RC common org.thingsboard.common diff --git a/common/transport/snmp/pom.xml b/common/transport/snmp/pom.xml index d3b2c46d69..da5093349d 100644 --- a/common/transport/snmp/pom.xml +++ b/common/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard.common - 4.2.1-RC + 4.2.1.1-RC transport diff --git a/common/transport/transport-api/pom.xml b/common/transport/transport-api/pom.xml index a3fee460e1..1cb128b439 100644 --- a/common/transport/transport-api/pom.xml +++ b/common/transport/transport-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.2.1-RC + 4.2.1.1-RC transport org.thingsboard.common.transport diff --git a/common/util/pom.xml b/common/util/pom.xml index ebedf5dea3..6509f154ad 100644 --- a/common/util/pom.xml +++ b/common/util/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1-RC + 4.2.1.1-RC common org.thingsboard.common diff --git a/common/version-control/pom.xml b/common/version-control/pom.xml index b6a0ea1d2e..939d0d47ad 100644 --- a/common/version-control/pom.xml +++ b/common/version-control/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1-RC + 4.2.1.1-RC common org.thingsboard.common diff --git a/dao/pom.xml b/dao/pom.xml index a1843959df..5044c0edfb 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1-RC + 4.2.1.1-RC thingsboard dao diff --git a/edqs/pom.xml b/edqs/pom.xml index d648d9d786..ef91e0bed4 100644 --- a/edqs/pom.xml +++ b/edqs/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1-RC + 4.2.1.1-RC thingsboard edqs diff --git a/monitoring/pom.xml b/monitoring/pom.xml index 77874c5ac9..3fb4ba5314 100644 --- a/monitoring/pom.xml +++ b/monitoring/pom.xml @@ -21,7 +21,7 @@ 4.0.0 org.thingsboard - 4.2.1-RC + 4.2.1.1-RC thingsboard diff --git a/msa/black-box-tests/pom.xml b/msa/black-box-tests/pom.xml index fed33f0002..6800c0c4cf 100644 --- a/msa/black-box-tests/pom.xml +++ b/msa/black-box-tests/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 4.2.1-RC + 4.2.1.1-RC msa org.thingsboard.msa diff --git a/msa/edqs/pom.xml b/msa/edqs/pom.xml index c5be28e7bb..62374eb2bc 100644 --- a/msa/edqs/pom.xml +++ b/msa/edqs/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1-RC + 4.2.1.1-RC msa org.thingsboard.msa diff --git a/msa/js-executor/package.json b/msa/js-executor/package.json index 88fc7924bf..3f4370b642 100644 --- a/msa/js-executor/package.json +++ b/msa/js-executor/package.json @@ -1,7 +1,7 @@ { "name": "thingsboard-js-executor", "private": true, - "version": "4.2.1", + "version": "4.2.1.1", "description": "ThingsBoard JavaScript Executor Microservice", "main": "server.ts", "bin": "server.js", diff --git a/msa/js-executor/pom.xml b/msa/js-executor/pom.xml index 651b142160..3aaf380e2f 100644 --- a/msa/js-executor/pom.xml +++ b/msa/js-executor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1-RC + 4.2.1.1-RC msa org.thingsboard.msa diff --git a/msa/monitoring/pom.xml b/msa/monitoring/pom.xml index abd12ed9e0..b81abce27d 100644 --- a/msa/monitoring/pom.xml +++ b/msa/monitoring/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.2.1-RC + 4.2.1.1-RC msa diff --git a/msa/pom.xml b/msa/pom.xml index 24cc67f4bf..48c83f6ee1 100644 --- a/msa/pom.xml +++ b/msa/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1-RC + 4.2.1.1-RC thingsboard msa diff --git a/msa/tb-node/pom.xml b/msa/tb-node/pom.xml index 0103bf9352..e473e1dfdf 100644 --- a/msa/tb-node/pom.xml +++ b/msa/tb-node/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1-RC + 4.2.1.1-RC msa org.thingsboard.msa diff --git a/msa/tb/pom.xml b/msa/tb/pom.xml index 28e1366a73..9db071c9ae 100644 --- a/msa/tb/pom.xml +++ b/msa/tb/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1-RC + 4.2.1.1-RC msa org.thingsboard.msa diff --git a/msa/transport/coap/pom.xml b/msa/transport/coap/pom.xml index 8aefc94122..098e3263b8 100644 --- a/msa/transport/coap/pom.xml +++ b/msa/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.2.1-RC + 4.2.1.1-RC transport org.thingsboard.msa.transport diff --git a/msa/transport/http/pom.xml b/msa/transport/http/pom.xml index a1481203de..3acbeb7a2d 100644 --- a/msa/transport/http/pom.xml +++ b/msa/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.2.1-RC + 4.2.1.1-RC transport org.thingsboard.msa.transport diff --git a/msa/transport/lwm2m/pom.xml b/msa/transport/lwm2m/pom.xml index 7af2b16588..bc6d0d004e 100644 --- a/msa/transport/lwm2m/pom.xml +++ b/msa/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.2.1-RC + 4.2.1.1-RC transport org.thingsboard.msa.transport diff --git a/msa/transport/mqtt/pom.xml b/msa/transport/mqtt/pom.xml index 176dca19eb..ef20c96f26 100644 --- a/msa/transport/mqtt/pom.xml +++ b/msa/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.2.1-RC + 4.2.1.1-RC transport org.thingsboard.msa.transport diff --git a/msa/transport/pom.xml b/msa/transport/pom.xml index afbab0c450..76cec909da 100644 --- a/msa/transport/pom.xml +++ b/msa/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1-RC + 4.2.1.1-RC msa org.thingsboard.msa diff --git a/msa/transport/snmp/pom.xml b/msa/transport/snmp/pom.xml index 553ed8bb37..348880019c 100644 --- a/msa/transport/snmp/pom.xml +++ b/msa/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard.msa transport - 4.2.1-RC + 4.2.1.1-RC org.thingsboard.msa.transport diff --git a/msa/vc-executor-docker/pom.xml b/msa/vc-executor-docker/pom.xml index d0a291e246..b1c11efe0a 100644 --- a/msa/vc-executor-docker/pom.xml +++ b/msa/vc-executor-docker/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1-RC + 4.2.1.1-RC msa org.thingsboard.msa diff --git a/msa/vc-executor/pom.xml b/msa/vc-executor/pom.xml index cf777c4c96..a59a8aa823 100644 --- a/msa/vc-executor/pom.xml +++ b/msa/vc-executor/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 4.2.1-RC + 4.2.1.1-RC msa org.thingsboard.msa diff --git a/msa/web-ui/package.json b/msa/web-ui/package.json index e8d4eedd5d..97d69546bc 100644 --- a/msa/web-ui/package.json +++ b/msa/web-ui/package.json @@ -1,7 +1,7 @@ { "name": "thingsboard-web-ui", "private": true, - "version": "4.2.1", + "version": "4.2.1.1", "description": "ThingsBoard Web UI Microservice", "main": "server.ts", "bin": "server.js", diff --git a/msa/web-ui/pom.xml b/msa/web-ui/pom.xml index a56d0ca9d9..70200eddb3 100644 --- a/msa/web-ui/pom.xml +++ b/msa/web-ui/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1-RC + 4.2.1.1-RC msa org.thingsboard.msa diff --git a/netty-mqtt/pom.xml b/netty-mqtt/pom.xml index 08f2a55f4a..c0743024ea 100644 --- a/netty-mqtt/pom.xml +++ b/netty-mqtt/pom.xml @@ -19,11 +19,11 @@ 4.0.0 org.thingsboard - 4.2.1-RC + 4.2.1.1-RC thingsboard netty-mqtt - 4.2.1-RC + 4.2.1.1-RC jar Netty MQTT Client diff --git a/pom.xml b/pom.xml index 4c5b2291be..7f8ef8074a 100755 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard thingsboard - 4.2.1-RC + 4.2.1.1-RC pom Thingsboard diff --git a/rest-client/pom.xml b/rest-client/pom.xml index e6e82da954..eddcd84d9a 100644 --- a/rest-client/pom.xml +++ b/rest-client/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1-RC + 4.2.1.1-RC thingsboard rest-client diff --git a/rule-engine/pom.xml b/rule-engine/pom.xml index 8a9ad96d95..99372b9703 100644 --- a/rule-engine/pom.xml +++ b/rule-engine/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1-RC + 4.2.1.1-RC thingsboard rule-engine diff --git a/rule-engine/rule-engine-api/pom.xml b/rule-engine/rule-engine-api/pom.xml index 0ed5a8cdbf..c52fb92594 100644 --- a/rule-engine/rule-engine-api/pom.xml +++ b/rule-engine/rule-engine-api/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.2.1-RC + 4.2.1.1-RC rule-engine org.thingsboard.rule-engine diff --git a/rule-engine/rule-engine-components/pom.xml b/rule-engine/rule-engine-components/pom.xml index 90e6a87e52..f582f1277a 100644 --- a/rule-engine/rule-engine-components/pom.xml +++ b/rule-engine/rule-engine-components/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.2.1-RC + 4.2.1.1-RC rule-engine org.thingsboard.rule-engine diff --git a/tools/pom.xml b/tools/pom.xml index 981c4804c8..783dc1ee73 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1-RC + 4.2.1.1-RC thingsboard tools diff --git a/transport/coap/pom.xml b/transport/coap/pom.xml index 8ae86d381f..f43d31e35b 100644 --- a/transport/coap/pom.xml +++ b/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1-RC + 4.2.1.1-RC transport org.thingsboard.transport diff --git a/transport/http/pom.xml b/transport/http/pom.xml index 487e1de186..7e55ae3db5 100644 --- a/transport/http/pom.xml +++ b/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1-RC + 4.2.1.1-RC transport org.thingsboard.transport diff --git a/transport/lwm2m/pom.xml b/transport/lwm2m/pom.xml index 30effe6e42..f64f8b4aa4 100644 --- a/transport/lwm2m/pom.xml +++ b/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1-RC + 4.2.1.1-RC transport org.thingsboard.transport diff --git a/transport/mqtt/pom.xml b/transport/mqtt/pom.xml index a609e8d002..0bcf68cbb8 100644 --- a/transport/mqtt/pom.xml +++ b/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1-RC + 4.2.1.1-RC transport org.thingsboard.transport diff --git a/transport/pom.xml b/transport/pom.xml index c6a82349f5..1d3755a766 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1-RC + 4.2.1.1-RC thingsboard transport diff --git a/transport/snmp/pom.xml b/transport/snmp/pom.xml index 1894f09787..5ea39a4066 100644 --- a/transport/snmp/pom.xml +++ b/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 4.2.1-RC + 4.2.1.1-RC transport diff --git a/ui-ngx/package.json b/ui-ngx/package.json index e993271fa8..74b4bbbb9c 100644 --- a/ui-ngx/package.json +++ b/ui-ngx/package.json @@ -1,6 +1,6 @@ { "name": "thingsboard", - "version": "4.2.1", + "version": "4.2.1.1", "scripts": { "ng": "ng", "start": "node --max_old_space_size=8048 ./node_modules/@angular/cli/bin/ng serve --configuration development --host 0.0.0.0 --open", diff --git a/ui-ngx/pom.xml b/ui-ngx/pom.xml index 905a1a8aca..d5bd48de78 100644 --- a/ui-ngx/pom.xml +++ b/ui-ngx/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1-RC + 4.2.1.1-RC thingsboard org.thingsboard From 68f432b581e27bf94ec9c401bf9239f3b70ef32c Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Mon, 22 Dec 2025 16:51:46 +0200 Subject: [PATCH 05/47] Set default docker base image to thingsboard/openjdk25:trixie-slim --- msa/pom.xml | 2 +- msa/tb/docker-cassandra/Dockerfile | 2 +- msa/tb/docker-postgres/Dockerfile | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/msa/pom.xml b/msa/pom.xml index 48c83f6ee1..70a907292b 100644 --- a/msa/pom.xml +++ b/msa/pom.xml @@ -32,7 +32,7 @@ ${basedir}/.. thingsboard - thingsboard/openjdk17:bookworm-slim + thingsboard/openjdk25:trixie-slim true true 1.4.13 diff --git a/msa/tb/docker-cassandra/Dockerfile b/msa/tb/docker-cassandra/Dockerfile index 20a0d8cf7a..a915e16ba2 100644 --- a/msa/tb/docker-cassandra/Dockerfile +++ b/msa/tb/docker-cassandra/Dockerfile @@ -14,7 +14,7 @@ # limitations under the License. # -FROM thingsboard/openjdk17:bookworm-slim +FROM ${docker.base.image} ENV PG_MAJOR=16 diff --git a/msa/tb/docker-postgres/Dockerfile b/msa/tb/docker-postgres/Dockerfile index 7c66d626c7..f9898e955e 100644 --- a/msa/tb/docker-postgres/Dockerfile +++ b/msa/tb/docker-postgres/Dockerfile @@ -14,7 +14,7 @@ # limitations under the License. # -FROM thingsboard/openjdk17:bookworm-slim +FROM ${docker.base.image} ENV PG_MAJOR 12 From 9eedf7476303e8049530417cfa1e4be85b8f26a7 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Mon, 22 Dec 2025 16:58:18 +0200 Subject: [PATCH 06/47] Revert docker base openjdk25 for monolith images --- msa/tb/docker-cassandra/Dockerfile | 2 +- msa/tb/docker-postgres/Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/msa/tb/docker-cassandra/Dockerfile b/msa/tb/docker-cassandra/Dockerfile index a915e16ba2..20a0d8cf7a 100644 --- a/msa/tb/docker-cassandra/Dockerfile +++ b/msa/tb/docker-cassandra/Dockerfile @@ -14,7 +14,7 @@ # limitations under the License. # -FROM ${docker.base.image} +FROM thingsboard/openjdk17:bookworm-slim ENV PG_MAJOR=16 diff --git a/msa/tb/docker-postgres/Dockerfile b/msa/tb/docker-postgres/Dockerfile index f9898e955e..7c66d626c7 100644 --- a/msa/tb/docker-postgres/Dockerfile +++ b/msa/tb/docker-postgres/Dockerfile @@ -14,7 +14,7 @@ # limitations under the License. # -FROM ${docker.base.image} +FROM thingsboard/openjdk17:bookworm-slim ENV PG_MAJOR 12 From dbbd97cbccea827b5520b6f01c7fed9eb9a86e02 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Mon, 22 Dec 2025 17:15:42 +0200 Subject: [PATCH 07/47] Edge mergeAndFilterDownlinkDuplicates - handle cases when no TS in attrbiutes body --- .../service/edge/EdgeMsgConstructorUtils.java | 35 +++++++++++-------- .../server/edge/DeviceEdgeTest.java | 5 +-- .../edge/EdgeMsgConstructorUtilsTest.java | 25 +++++++++++++ 3 files changed, 46 insertions(+), 19 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/EdgeMsgConstructorUtils.java b/application/src/main/java/org/thingsboard/server/service/edge/EdgeMsgConstructorUtils.java index e1c7f1198c..33084f98fa 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/EdgeMsgConstructorUtils.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/EdgeMsgConstructorUtils.java @@ -560,7 +560,7 @@ public class EdgeMsgConstructorUtils { .setEntityIdMSB(entityId.getId().getMostSignificantBits()) .setEntityIdLSB(entityId.getId().getLeastSignificantBits()) .setEntityType(entityId.getEntityType().name()); - long ts = getTs(entityData.getAsJsonObject()); + long ts = extractTs(entityData.getAsJsonObject()); switch (actionType) { case TIMESERIES_UPDATED: try { @@ -613,8 +613,8 @@ public class EdgeMsgConstructorUtils { return builder.build(); } - private static long getTs(JsonObject data) { - if (data.get("ts") != null && !data.get("ts").isJsonNull()) { + private static long extractTs(JsonObject data) { + if (data.has("ts") && data.get("ts").isJsonPrimitive()) { return data.getAsJsonPrimitive("ts").getAsLong(); } return System.currentTimeMillis(); @@ -740,7 +740,7 @@ public class EdgeMsgConstructorUtils { result.sort(Comparator.comparingLong(EdgeEvent::getSeqId)); return result; } catch (Exception e) { - log.warn("Can't merge downlink duplicates, edgeEvents [{}]", edgeEvents, e); + log.info("Can't merge downlink duplicates. Sending downlinks without merge. Original edgeEvents [{}]", edgeEvents, e); return edgeEvents; } } @@ -751,6 +751,9 @@ public class EdgeMsgConstructorUtils { } String bodyStr = JacksonUtil.toString(body); var jsonObject = JsonParser.parseString(bodyStr).getAsJsonObject(); + if (!jsonObject.has("ts")) { + return new AttrsTs(0L, List.of()); + } long ts = jsonObject.get("ts").getAsLong(); var kv = jsonObject.getAsJsonObject("kv"); List attrs = JsonConverter.convertToAttributes( @@ -761,22 +764,24 @@ public class EdgeMsgConstructorUtils { } private static JsonNode filterAttributesBody(JsonNode body, Map latestByKey) { - if (body == null || latestByKey == null || latestByKey.isEmpty()) { + if (body == null) { return null; } String bodyStr = JacksonUtil.toString(body); JsonObject jsonObject = JsonParser.parseString(bodyStr).getAsJsonObject(); - long ts = jsonObject.get("ts").getAsLong(); - JsonObject kv = jsonObject.getAsJsonObject("kv"); - for (Iterator> it = kv.entrySet().iterator(); it.hasNext(); ) { - Map.Entry e = it.next(); - Long latestTs = latestByKey.get(e.getKey()); - if (latestTs == null || !latestTs.equals(ts)) { - it.remove(); + if (jsonObject.has("ts") && latestByKey != null && !latestByKey.isEmpty()) { + long ts = jsonObject.get("ts").getAsLong(); + JsonObject kv = jsonObject.getAsJsonObject("kv"); + for (Iterator> it = kv.entrySet().iterator(); it.hasNext(); ) { + Map.Entry e = it.next(); + Long latestTs = latestByKey.get(e.getKey()); + if (latestTs == null || !latestTs.equals(ts)) { + it.remove(); + } + } + if (kv.isEmpty()) { + return null; } - } - if (kv.isEmpty()) { - return null; } return JacksonUtil.toJsonNode(jsonObject.toString()); } diff --git a/application/src/test/java/org/thingsboard/server/edge/DeviceEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/DeviceEdgeTest.java index 380dec8015..5c3f43b451 100644 --- a/application/src/test/java/org/thingsboard/server/edge/DeviceEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/DeviceEdgeTest.java @@ -49,8 +49,6 @@ import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.ota.OtaPackageType; -import org.thingsboard.server.common.data.page.PageData; -import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentialsType; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; @@ -671,8 +669,7 @@ public class DeviceEdgeTest extends AbstractEdgeTest { .atMost(10, TimeUnit.SECONDS) .until(() -> { String urlTemplate = "/api/plugins/telemetry/DEVICE/" + device.getId() + "/keys/attributes/" + scope; - List actualKeys = doGetAsyncTyped(urlTemplate, new TypeReference<>() { - }); + List actualKeys = doGetAsyncTyped(urlTemplate, new TypeReference<>() {}); return actualKeys != null && !actualKeys.isEmpty() && actualKeys.contains(expectedKey); }); diff --git a/application/src/test/java/org/thingsboard/server/service/edge/EdgeMsgConstructorUtilsTest.java b/application/src/test/java/org/thingsboard/server/service/edge/EdgeMsgConstructorUtilsTest.java index ebb8305871..1e268e3bcb 100644 --- a/application/src/test/java/org/thingsboard/server/service/edge/EdgeMsgConstructorUtilsTest.java +++ b/application/src/test/java/org/thingsboard/server/service/edge/EdgeMsgConstructorUtilsTest.java @@ -230,10 +230,35 @@ public class EdgeMsgConstructorUtilsTest { Assertions.assertEquals(8, getIntValue(assetMergedAttrAD.getBody(), "d")); } + @Test + public void testMergeDownlinkDuplicates_attrBodyHasNoTs_returnOriginalList() { + UUID deviceId = UUID.randomUUID(); + TenantId tenantId = TenantId.fromUUID(UUID.randomUUID()); + + var deviceAttrUpdate1 = createEdgeEvent(tenantId, 1, EdgeEventActionType.ATTRIBUTES_UPDATED, + deviceId, EdgeEventType.DEVICE, createAttrBodyWithoutTs("{\"a\":1,\"b\":1,\"d\":1}")); + var deviceAttrUpdate2 = createEdgeEvent(tenantId, 2, EdgeEventActionType.ATTRIBUTES_UPDATED, + deviceId, EdgeEventType.DEVICE, createAttrBodyWithoutTs("{\"a\":2,\"b\":2,\"c\":2}")); + var deviceAttrUpdate3 = createEdgeEvent(tenantId, 3, EdgeEventActionType.ATTRIBUTES_UPDATED, + deviceId, EdgeEventType.DEVICE, createAttrBodyWithoutTs("{\"a\":3,\"d\":3}")); + + List input = List.of(deviceAttrUpdate1, deviceAttrUpdate2, deviceAttrUpdate3); + List merged = EdgeMsgConstructorUtils.mergeAndFilterDownlinkDuplicates(input); + + Assertions.assertEquals(3, merged.size()); + Assertions.assertEquals(deviceAttrUpdate1, merged.get(0)); + Assertions.assertEquals(deviceAttrUpdate2, merged.get(1)); + Assertions.assertEquals(deviceAttrUpdate3, merged.get(2)); + } + private Integer getIntValue(JsonNode body, String key) { return body.get("kv").get(key) != null ? body.get("kv").get(key).asInt() : null; } + private static JsonNode createAttrBodyWithoutTs(String kvJson) { + return JacksonUtil.toJsonNode("{\"kv\":" + kvJson + "}"); + } + private static JsonNode createAttrBody(long ts, String kvJson) { return JacksonUtil.toJsonNode("{\"ts\":" + ts + ",\"kv\":" + kvJson + "}"); } From cb7b2e2705d762149d61f43e0388e428e84abba6 Mon Sep 17 00:00:00 2001 From: ArtemDzhereleiko <83352633+ArtemDzhereleiko@users.noreply.github.com> Date: Mon, 22 Dec 2025 19:07:32 +0200 Subject: [PATCH 08/47] Fixed see debug event and change color alarm severity for alarm table (#14666) * UI: Fixed see debug event * UI: Fixed alarm colors --------- Co-authored-by: ArtemDzhereleiko --- .../components/alarm-rules/alarm-rules-table-config.ts | 2 +- .../alarm-rules/create-cf-alarm-rules.component.ts | 5 ++--- .../components/notification/notification.component.ts | 9 ++++----- ui-ngx/src/app/shared/models/alarm.models.ts | 10 +++++----- ui-ngx/src/app/shared/models/notification.models.ts | 10 ---------- 5 files changed, 12 insertions(+), 24 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table-config.ts b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table-config.ts index 46d43f2921..3d1c066839 100644 --- a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table-config.ts +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table-config.ts @@ -75,7 +75,7 @@ export class AlarmRulesTableConfig extends EntityTableConfig this.openDebugEventsDialog.call(this, calculatedField), + action: (calculatedField: AlarmRuleTableEntity) => this.openDebugEventsDialog.call(this, null, calculatedField), }; alarmRuleFilterConfig: CalculatedFieldsQuery; diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/create-cf-alarm-rules.component.ts b/ui-ngx/src/app/modules/home/components/alarm-rules/create-cf-alarm-rules.component.ts index 8959d709fa..09a2d52756 100644 --- a/ui-ngx/src/app/modules/home/components/alarm-rules/create-cf-alarm-rules.component.ts +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/create-cf-alarm-rules.component.ts @@ -27,10 +27,9 @@ import { Validator, Validators } from '@angular/forms'; -import { AlarmSeverity, alarmSeverityTranslations } from '@shared/models/alarm.models'; +import { AlarmSeverity, alarmSeverityColors, alarmSeverityTranslations } from '@shared/models/alarm.models'; import { AlarmRule } from "@shared/models/alarm-rule.models"; import { CalculatedFieldArgument } from "@shared/models/calculated-field.models"; -import { AlarmSeverityNotificationColors } from "@shared/models/notification.models"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { coerceBoolean } from "@shared/decorators/coercion"; import { Observable } from "rxjs"; @@ -68,7 +67,7 @@ export class CreateCfAlarmRulesComponent implements ControlValueAccessor, Valida alarmSeverityEnum = AlarmSeverity; alarmSeverityTranslationMap = alarmSeverityTranslations; - AlarmSeverityNotificationColors = AlarmSeverityNotificationColors; + AlarmSeverityNotificationColors = alarmSeverityColors; createAlarmRulesFormGroup = this.fb.group({ createAlarmRules: this.fb.array<{severity: AlarmSeverity, alarmRule: AlarmRule}>([]) diff --git a/ui-ngx/src/app/shared/components/notification/notification.component.ts b/ui-ngx/src/app/shared/components/notification/notification.component.ts index 77be7ff7cd..204c9e3230 100644 --- a/ui-ngx/src/app/shared/components/notification/notification.component.ts +++ b/ui-ngx/src/app/shared/components/notification/notification.component.ts @@ -17,7 +17,6 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { ActionButtonLinkType, - AlarmSeverityNotificationColors, Notification, NotificationStatus, NotificationType, @@ -25,7 +24,7 @@ import { } from '@shared/models/notification.models'; import { UtilsService } from '@core/services/utils.service'; import { Router } from '@angular/router'; -import { alarmSeverityTranslations } from '@shared/models/alarm.models'; +import { alarmSeverityColors, alarmSeverityTranslations } from '@shared/models/alarm.models'; import tinycolor from 'tinycolor2'; import { StateObject } from '@core/api/widget-api.models'; import { objToBase64URI } from '@core/utils'; @@ -135,12 +134,12 @@ export class NotificationComponent implements OnInit { } alarmColorSeverity(alpha: number) { - return tinycolor(AlarmSeverityNotificationColors.get(this.notification.info.alarmSeverity)).setAlpha(alpha).toRgbString(); + return tinycolor(alarmSeverityColors.get(this.notification.info.alarmSeverity)).setAlpha(alpha).toRgbString(); } notificationColor(): string { if (this.notification.type === NotificationType.ALARM && !this.notification.info.cleared) { - return AlarmSeverityNotificationColors.get(this.notification.info.alarmSeverity); + return alarmSeverityColors.get(this.notification.info.alarmSeverity); } return 'transparent'; } @@ -154,7 +153,7 @@ export class NotificationComponent implements OnInit { notificationIconColor(): object { if (this.notification.type === NotificationType.ALARM) { - return {color: AlarmSeverityNotificationColors.get(this.notification.info.alarmSeverity)}; + return {color: alarmSeverityColors.get(this.notification.info.alarmSeverity)}; } else if (this.notification.type === NotificationType.RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT) { return {color: '#D12730'}; } else if (this.notification.type === NotificationType.ENTITIES_LIMIT_INCREASE_REQUEST) { diff --git a/ui-ngx/src/app/shared/models/alarm.models.ts b/ui-ngx/src/app/shared/models/alarm.models.ts index 5e1e5def25..fda79a0154 100644 --- a/ui-ngx/src/app/shared/models/alarm.models.ts +++ b/ui-ngx/src/app/shared/models/alarm.models.ts @@ -88,11 +88,11 @@ export const alarmSearchStatusTranslations = new Map( export const alarmSeverityColors = new Map( [ - [AlarmSeverity.CRITICAL, 'red'], - [AlarmSeverity.MAJOR, 'orange'], - [AlarmSeverity.MINOR, '#ffca3d'], - [AlarmSeverity.WARNING, '#abab00'], - [AlarmSeverity.INDETERMINATE, 'green'] + [AlarmSeverity.CRITICAL, '#D12730'], + [AlarmSeverity.MAJOR, '#FEAC0C'], + [AlarmSeverity.MINOR, '#F2DA05'], + [AlarmSeverity.WARNING, '#F66716'], + [AlarmSeverity.INDETERMINATE, '#00000061'] ] ); diff --git a/ui-ngx/src/app/shared/models/notification.models.ts b/ui-ngx/src/app/shared/models/notification.models.ts index f9abf6462b..352e49e01e 100644 --- a/ui-ngx/src/app/shared/models/notification.models.ts +++ b/ui-ngx/src/app/shared/models/notification.models.ts @@ -551,16 +551,6 @@ export const NotificationTypeIcons = new Map([ [NotificationType.RESOURCES_SHORTAGE, 'warning'] ]); -export const AlarmSeverityNotificationColors = new Map( - [ - [AlarmSeverity.CRITICAL, '#D12730'], - [AlarmSeverity.MAJOR, '#FEAC0C'], - [AlarmSeverity.MINOR, '#F2DA05'], - [AlarmSeverity.WARNING, '#F66716'], - [AlarmSeverity.INDETERMINATE, '#00000061'] - ] -); - export enum ActionButtonLinkType { LINK = 'LINK', DASHBOARD = 'DASHBOARD' From 028083da9d83a19588da79db1cc4f7f6cee99ec5 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Tue, 23 Dec 2025 10:48:21 +0200 Subject: [PATCH 09/47] Add LTS docker tag build config --- msa/pom.xml | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/msa/pom.xml b/msa/pom.xml index 70a907292b..5eff7cc06d 100644 --- a/msa/pom.xml +++ b/msa/pom.xml @@ -125,6 +125,48 @@ + + lts + + + lts + + + + 4.2.1-latest + + + + + org.codehaus.mojo + exec-maven-plugin + + + push-lts-docker-amd-arm-images + ${docker.push-arm-amd-image.phase} + + exec + + + docker + ${project.build.directory} + + buildx + build + -t + ${docker.repo}/${docker.name}:${docker.lts.tag} + --platform=linux/amd64,linux/arm64 + -o + type=registry + . + + + + + + + + From 934b54180be8e60a8f4af7d13bf2b3cee97ad5d5 Mon Sep 17 00:00:00 2001 From: Maksym Tsymbarov Date: Tue, 23 Dec 2025 12:20:23 +0200 Subject: [PATCH 10/47] Fixed disabling update button based on filters change (#14669) --- .../alarm-rules/alarm-rule-filter-config.component.ts | 1 + .../home/components/alarm/alarm-filter-config.component.ts | 1 + .../home/components/audit-log/audit-log-filter.component.ts | 2 ++ .../table-header/calculated-fields-filter-config.component.ts | 1 + .../home/components/device/device-info-filter.component.ts | 1 + 5 files changed, 6 insertions(+) diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-filter-config.component.ts b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-filter-config.component.ts index 03c6932067..a624a5ae12 100644 --- a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-filter-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-filter-config.component.ts @@ -196,6 +196,7 @@ export class AlarmRuleFilterConfigComponent implements OnInit, ControlValueAcces cancel() { this.updateAlarmRuleConfigForm(this.alarmRuleFilterConfig); + this.alarmRuleFilterConfigForm.markAsPristine(); if (this.overlayRef) { this.overlayRef.dispose(); } else { diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-filter-config.component.ts b/ui-ngx/src/app/modules/home/components/alarm/alarm-filter-config.component.ts index 093858a500..e938b6fa19 100644 --- a/ui-ngx/src/app/modules/home/components/alarm/alarm-filter-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-filter-config.component.ts @@ -224,6 +224,7 @@ export class AlarmFilterConfigComponent implements OnInit, OnDestroy, ControlVal cancel() { this.updateAlarmConfigForm(this.alarmFilterConfig); + this.alarmFilterConfigForm.markAsPristine(); if (this.overlayRef) { this.overlayRef.dispose(); } else { diff --git a/ui-ngx/src/app/modules/home/components/audit-log/audit-log-filter.component.ts b/ui-ngx/src/app/modules/home/components/audit-log/audit-log-filter.component.ts index f810eb5df7..18e5f1a1db 100644 --- a/ui-ngx/src/app/modules/home/components/audit-log/audit-log-filter.component.ts +++ b/ui-ngx/src/app/modules/home/components/audit-log/audit-log-filter.component.ts @@ -163,11 +163,13 @@ export class AuditLogFilterComponent implements OnInit, ControlValueAccessor { cancel() { this.updateAuditLogFilterForm(this.auditLogFilter); + this.auditLogFilterForm.markAsPristine(); this.auditLogOverlayRef.dispose(); } update() { this.auditLogFilterUpdated(this.auditLogFilterForm.value); + this.auditLogFilterForm.markAsPristine(); this.auditLogOverlayRef.dispose(); } diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/table-header/calculated-fields-filter-config.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/table-header/calculated-fields-filter-config.component.ts index 83ea05af4c..ffe986d434 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/table-header/calculated-fields-filter-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/table-header/calculated-fields-filter-config.component.ts @@ -204,6 +204,7 @@ export class CalculatedFieldsFilterConfigComponent implements OnInit, ControlVal cancel() { this.updateCfConfigForm(this.cfFilterConfig); + this.cfFilterForm.markAsPristine(); if (this.overlayRef) { this.overlayRef.dispose(); } else { diff --git a/ui-ngx/src/app/modules/home/components/device/device-info-filter.component.ts b/ui-ngx/src/app/modules/home/components/device/device-info-filter.component.ts index d6a81883d2..531323bf5c 100644 --- a/ui-ngx/src/app/modules/home/components/device/device-info-filter.component.ts +++ b/ui-ngx/src/app/modules/home/components/device/device-info-filter.component.ts @@ -196,6 +196,7 @@ export class DeviceInfoFilterComponent implements OnInit, ControlValueAccessor { cancel() { this.updateDeviceInfoFilterForm(this.deviceInfoFilter); + this.deviceInfoFilterForm.markAsPristine(); if (this.overlayRef) { this.overlayRef.dispose(); } else { From f704fcc6a8e7656adadf648376aef84fec87d513 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Tue, 23 Dec 2025 12:31:52 +0200 Subject: [PATCH 11/47] Separate push-lts-docker-amd-arm-images profile --- msa/pom.xml | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/msa/pom.xml b/msa/pom.xml index 5eff7cc06d..66e92678ce 100644 --- a/msa/pom.xml +++ b/msa/pom.xml @@ -126,10 +126,10 @@ - lts + push-lts-docker-amd-arm-images - lts + push-lts-docker-amd-arm-images @@ -141,6 +141,27 @@ org.codehaus.mojo exec-maven-plugin + + push-version-docker-amd-arm-images + ${docker.push-arm-amd-image.phase} + + exec + + + docker + ${project.build.directory} + + buildx + build + -t + ${docker.repo}/${docker.name}:${project.version} + --platform=linux/amd64,linux/arm64 + -o + type=registry + . + + + push-lts-docker-amd-arm-images ${docker.push-arm-amd-image.phase} From 4a30d778782f3aa0bf5d510bced1c0fba4e0928f Mon Sep 17 00:00:00 2001 From: Maksym Tsymbarov Date: Tue, 23 Dec 2025 12:39:20 +0200 Subject: [PATCH 12/47] Added translations for severity in alarm comments --- .../alarm/alarm-comment.component.ts | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.ts b/ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.ts index 800fdf2371..220a49210d 100644 --- a/ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.ts +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.ts @@ -32,13 +32,15 @@ import { AlarmCommentInfo, AlarmCommentType, AlarmMessage, + AlarmSeverity, + alarmSeverityTranslations, getUserDisplayName } from '@shared/models/alarm.models'; import { UtilsService } from '@core/services/utils.service'; import { EntityType } from '@shared/models/entity-type.models'; import { DatePipe } from '@angular/common'; import { ImportExportService } from '@shared/import-export/import-export.service'; -import { isNotEmptyStr } from '@core/utils'; +import { deepClone, isNotEmptyStr } from '@core/utils'; interface AlarmCommentsDisplayData { commentId?: string; @@ -152,9 +154,24 @@ export class AlarmCommentComponent implements OnInit { private parseSystemComment(alarm: AlarmCommentInfo): string { const subTypeKey = alarm.comment?.subtype; + const alarmComment = deepClone(alarm.comment); if (subTypeKey && AlarmMessage[subTypeKey]) { const translationKey = AlarmMessage[subTypeKey]; - return this.translate.instant(translationKey, alarm.comment); + if (alarmComment?.newSeverity) { + const newSeverityKey = + (alarmSeverityTranslations.has(alarmComment.newSeverity) + ? alarmSeverityTranslations.get(alarmComment.newSeverity) + : alarmComment.newSeverity) as AlarmSeverity; + alarmComment.newSeverity = this.translate.instant(newSeverityKey); + } + if (alarmComment?.oldSeverity) { + const oldSeverityKey = + (alarmSeverityTranslations.has(alarmComment.oldSeverity) + ? alarmSeverityTranslations.get(alarmComment.oldSeverity) + : alarmComment.oldSeverity) as AlarmSeverity; + alarmComment.oldSeverity = this.translate.instant(oldSeverityKey); + } + return this.translate.instant(translationKey, alarmComment); } return alarm.comment.text; } From ea29bdaf4ad9824d5e6f7bdb8daf9001e80462b2 Mon Sep 17 00:00:00 2001 From: ArtemDzhereleiko Date: Tue, 23 Dec 2025 12:40:22 +0200 Subject: [PATCH 13/47] UI: Enhancement for alarm severity colors --- .../notification/notification.component.html | 2 +- .../notification/notification.component.ts | 10 +++++++--- ui-ngx/src/app/shared/models/alarm.models.ts | 20 ++++++++++++++----- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/ui-ngx/src/app/shared/components/notification/notification.component.html b/ui-ngx/src/app/shared/components/notification/notification.component.html index 674a074a85..0a667ff334 100644 --- a/ui-ngx/src/app/shared/components/notification/notification.component.html +++ b/ui-ngx/src/app/shared/components/notification/notification.component.html @@ -42,7 +42,7 @@ check_circle_outline
+ [style.background-color]="alarmColorSeverityBackground()"> {{alarmSeverityTranslations.get(notification.info.alarmSeverity) | translate}} diff --git a/ui-ngx/src/app/shared/components/notification/notification.component.ts b/ui-ngx/src/app/shared/components/notification/notification.component.ts index 204c9e3230..9e7398e457 100644 --- a/ui-ngx/src/app/shared/components/notification/notification.component.ts +++ b/ui-ngx/src/app/shared/components/notification/notification.component.ts @@ -24,7 +24,11 @@ import { } from '@shared/models/notification.models'; import { UtilsService } from '@core/services/utils.service'; import { Router } from '@angular/router'; -import { alarmSeverityColors, alarmSeverityTranslations } from '@shared/models/alarm.models'; +import { + alarmSeverityBackgroundColors, + alarmSeverityColors, + alarmSeverityTranslations +} from '@shared/models/alarm.models'; import tinycolor from 'tinycolor2'; import { StateObject } from '@core/api/widget-api.models'; import { objToBase64URI } from '@core/utils'; @@ -133,8 +137,8 @@ export class NotificationComponent implements OnInit { } } - alarmColorSeverity(alpha: number) { - return tinycolor(alarmSeverityColors.get(this.notification.info.alarmSeverity)).setAlpha(alpha).toRgbString(); + alarmColorSeverityBackground() { + return alarmSeverityBackgroundColors.get(this.notification.info.alarmSeverity); } notificationColor(): string { diff --git a/ui-ngx/src/app/shared/models/alarm.models.ts b/ui-ngx/src/app/shared/models/alarm.models.ts index fda79a0154..76199faf2c 100644 --- a/ui-ngx/src/app/shared/models/alarm.models.ts +++ b/ui-ngx/src/app/shared/models/alarm.models.ts @@ -88,11 +88,21 @@ export const alarmSearchStatusTranslations = new Map( export const alarmSeverityColors = new Map( [ - [AlarmSeverity.CRITICAL, '#D12730'], - [AlarmSeverity.MAJOR, '#FEAC0C'], - [AlarmSeverity.MINOR, '#F2DA05'], - [AlarmSeverity.WARNING, '#F66716'], - [AlarmSeverity.INDETERMINATE, '#00000061'] + [AlarmSeverity.CRITICAL, 'var(--tb-alarm-severity-critical, rgb(209, 39, 48))'], + [AlarmSeverity.MAJOR, 'var(--tb-alarm-severity-major, rgb(246, 103, 22))'], + [AlarmSeverity.MINOR, 'var(--tb-alarm-severity-minor, rgb(250, 164, 5))'], + [AlarmSeverity.WARNING, 'var(--tb-alarm-severity-warning, rgb(242, 218, 5))'], + [AlarmSeverity.INDETERMINATE, 'var(--tb-alarm-severity-indeterminate, rgba(0, 0, 0, 0.38))'] + ] +); + +export const alarmSeverityBackgroundColors = new Map( + [ + [AlarmSeverity.CRITICAL, `var(--tb-alarm-severity-critical-bg, rgba(209, 39, 48, 0.06))`], + [AlarmSeverity.MAJOR, 'var(--tb-alarm-severity-major-bg, rgba(246, 103, 22, 0.06))'], + [AlarmSeverity.MINOR, 'var(--tb-alarm-severity-minor-bg, rgba(250, 164, 5, 0.06))'], + [AlarmSeverity.WARNING, 'var(--tb-alarm-severity-warning-bg, rgba(242, 218, 5, 0.06))'], + [AlarmSeverity.INDETERMINATE, 'var(--tb-alarm-severity-indeterminate-bg, rgba(0, 0, 0, 0.06))'] ] ); From 73a09f79d21ad036f27f91bce025af35ccd53103 Mon Sep 17 00:00:00 2001 From: Maksym Tsymbarov Date: Tue, 23 Dec 2025 13:05:00 +0200 Subject: [PATCH 14/47] Simplified logic --- .../home/components/alarm/alarm-comment.component.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.ts b/ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.ts index 220a49210d..6d426cdcd0 100644 --- a/ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.ts +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.ts @@ -154,22 +154,20 @@ export class AlarmCommentComponent implements OnInit { private parseSystemComment(alarm: AlarmCommentInfo): string { const subTypeKey = alarm.comment?.subtype; - const alarmComment = deepClone(alarm.comment); if (subTypeKey && AlarmMessage[subTypeKey]) { + const alarmComment = deepClone(alarm.comment); const translationKey = AlarmMessage[subTypeKey]; if (alarmComment?.newSeverity) { - const newSeverityKey = + alarmComment.newSeverity = (alarmSeverityTranslations.has(alarmComment.newSeverity) - ? alarmSeverityTranslations.get(alarmComment.newSeverity) + ? this.translate.instant(alarmSeverityTranslations.get(alarmComment.newSeverity)) : alarmComment.newSeverity) as AlarmSeverity; - alarmComment.newSeverity = this.translate.instant(newSeverityKey); } if (alarmComment?.oldSeverity) { - const oldSeverityKey = + alarmComment.oldSeverity = (alarmSeverityTranslations.has(alarmComment.oldSeverity) - ? alarmSeverityTranslations.get(alarmComment.oldSeverity) + ? this.translate.instant(alarmSeverityTranslations.get(alarmComment.oldSeverity)) : alarmComment.oldSeverity) as AlarmSeverity; - alarmComment.oldSeverity = this.translate.instant(oldSeverityKey); } return this.translate.instant(translationKey, alarmComment); } From 8f90c7fb659e7c5e101b0692e0079d27c1631dca Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Tue, 23 Dec 2025 14:47:08 +0200 Subject: [PATCH 15/47] added help pages for related entities aggregation scripts --- ...culated-field-metrics-panel.component.html | 2 +- .../en_US/calculated-field/expression_fn.md | 9 ++- .../calculated-field/filter_expression_fn.md | 64 ++++++++++++++- .../calculated-field/map_expression_fn.md | 79 +++++++++++++++++++ 4 files changed, 149 insertions(+), 5 deletions(-) create mode 100644 ui-ngx/src/assets/help/en_US/calculated-field/map_expression_fn.md diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/metrics/calculated-field-metrics-panel.component.html b/ui-ngx/src/app/modules/home/components/calculated-fields/components/metrics/calculated-field-metrics-panel.component.html index f86c15ba73..178fedbecd 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/metrics/calculated-field-metrics-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/metrics/calculated-field-metrics-panel.component.html @@ -170,7 +170,7 @@ [highlightRules]="highlightRules" [editorCompleter]="editorCompleter" [helpPopupStyle]="{ width: '1200px' }" - helpId="calculated-field/expression_fn"> + helpId="calculated-field/map_expression_fn">
{{ 'api-usage.tbel' | translate }}
diff --git a/ui-ngx/src/assets/help/en_US/calculated-field/expression_fn.md b/ui-ngx/src/assets/help/en_US/calculated-field/expression_fn.md index 1d45dea3c8..6129dac351 100644 --- a/ui-ngx/src/assets/help/en_US/calculated-field/expression_fn.md +++ b/ui-ngx/src/assets/help/en_US/calculated-field/expression_fn.md @@ -11,12 +11,16 @@ function calculate(ctx, arg1, arg2, ...): object | object[] ### Supported Arguments +Arguments are passed to the function by **name** defined in the calculated field configuration. + There are three types of arguments supported in the calculated field configuration: #### Attribute and Latest Telemetry Arguments These arguments are single values and may be of type: boolean, int64 (long), double, string, or JSON. +#### Direct argument access via **``** + **Example: Convert Temperature from Fahrenheit to Celsius** ```javascript @@ -26,7 +30,9 @@ return { } ``` -Alternatively, using `ctx` to access the argument as an object: +#### Accessing argument via **`ctx.args.`** + +In addition to direct access, arguments can be accessed via the `ctx.args.` object, which includes both the `value` of an argument and its timestamp as `ts`: ```json { @@ -37,7 +43,6 @@ Alternatively, using `ctx` to access the argument as an object: } ``` -You may notice that the object includes both the `value` of an argument and its timestamp as `ts`. Let's modify the function that converts Fahrenheit to Celsius to also return the timestamp information: ```javascript diff --git a/ui-ngx/src/assets/help/en_US/calculated-field/filter_expression_fn.md b/ui-ngx/src/assets/help/en_US/calculated-field/filter_expression_fn.md index ce8ca22f51..22316fe418 100644 --- a/ui-ngx/src/assets/help/en_US/calculated-field/filter_expression_fn.md +++ b/ui-ngx/src/assets/help/en_US/calculated-field/filter_expression_fn.md @@ -1,10 +1,70 @@ ## Calculated Field TBEL Filter Function -The **filter()** function is a user-defined script that enables custom calculations using [TBEL](${siteBaseUrl}/docs${docPlatformPrefix}/user-guide/tbel/) on telemetry and attribute data. +The **filter()** function is a [TBEL](${siteBaseUrl}/docs${docPlatformPrefix}/user-guide/tbel/) script used in aggregation metrics of a calculated field. + It receives arguments configured in the calculated field setup, along with an additional `ctx` object that stores `latestTs` and provides access to all arguments. +It allows you to include or exclude related entities from aggregation based on their telemetry or attribute values. + +The filter is evaluated per related entity before the aggregation function is applied. + ### Function Signature ```javascript -function calculate(ctx, arg1, arg2, ...): boolean +function filter(ctx, arg1, arg2, ...): boolean +``` + +### Supported Arguments + +Arguments are passed to the function by **name** defined in the calculated field configuration. + +There are two types of arguments supported in the calculated field configuration: **Attribute and Latest Telemetry Arguments** + +These arguments are single values and may be of type: boolean, int64 (long), double, string, or JSON. + +#### Direct argument access via **``** + +**Example: Count free parking spaces** + +**Goal**: Include only parking spaces that are active and not occupied. + +```javascript +return active == true && occupied == false; +``` + +Only entities that satisfy this condition will be included in the aggregation. + +#### Accessing argument via **`ctx.args.`** + +In addition to direct access, arguments can be accessed via the `ctx.args.` object, which includes both the `value` of an argument and its timestamp as `ts`: + +```json +{ + "consumption": { + "ts": 1740644656669, + "value": 542.6 + } +} +``` + +The `ctx.latestTs` property represents the latest timestamp across all related entities and their arguments participating in the aggregation. + +**Example: Calculate the total consumption across multiple related pumps** + +**Scenario**: Each pump reports consumption telemetry approximately every 10 minutes, but reporting times may vary due to network delays (up to ~30 seconds). +To avoid counting outdated values, only recently updated telemetry should be included. + +**Goal**: Include only pumps whose consumption value was reported within 1 minute of the latest timestamp. + +```javascript +var ONE_MINUTE = 60 * 1000; +return (ctx.latestTs - ctx.args.consumption.ts) <= ONE_MINUTE; ``` + +### Function return format + +The function **must** return a boolean: +- `true` → include entity in aggregation +- `false` → exclude entity from aggregation + +Any other return type is considered invalid. diff --git a/ui-ngx/src/assets/help/en_US/calculated-field/map_expression_fn.md b/ui-ngx/src/assets/help/en_US/calculated-field/map_expression_fn.md new file mode 100644 index 0000000000..41ccf3540b --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/calculated-field/map_expression_fn.md @@ -0,0 +1,79 @@ +## Calculated Field TBEL Map Function + +The **map()** function is a [TBEL](${siteBaseUrl}/docs${docPlatformPrefix}/user-guide/tbel/) script used in aggregation metrics of a calculated field. + +It determines the value applied by each related entity to the aggregation. + +The function receives arguments configured in the calculated field setup, along with an additional `ctx` object that stores `latestTs` and provides access to all arguments. + +The function is evaluated **per related entity**, after filtering is applied. + +### Function Signature + +```javascript +function map(ctx, arg1, arg2, ...): number | boolean | string +``` + +### Supported Arguments + +Arguments are passed to the function by **name** defined in the calculated field configuration. + +There are two types of arguments supported in the calculated field configuration: **Attribute and Latest Telemetry Arguments** + +These arguments are single values and may be of type: boolean, int64 (long), double, string, or JSON. + +#### Direct argument access via **``** + +**Example: Calculate average temperature across sensors** + +**Scenario**: Multiple related sensors report temperature in Fahrenheit. You want to aggregate temperature values, but the aggregation must be performed in Celsius. + +**Goal**: Convert temperature to Celsius before aggregation + +```javascript +var temperatureC = (temperature - 32) / 1.8; +return toFixed(temperatureC, 2); +``` + +Instead of creating a separate calculated field for conversion, the transformation is performed per entity, before aggregation. + +#### Accessing argument via **`ctx.args.`** + +In addition to direct access, arguments can be accessed via the `ctx.args.` object, which includes both the `value` of an argument and its timestamp as `ts`: + +```json +{ + "temperature": { + "ts": 1740644656761, + "value": 33.6 + } +} +``` + +The `ctx.latestTs` property represents the latest timestamp across all related entities and their arguments participating in the aggregation. + +**Example: Calculate average temperature** + +**Scenario**: Each sensor reports temperature approximately every 10 minutes, but reporting times may vary due to network delays (up to ~30 seconds). +If a temperature value is outdated, you want the entity to remain part of the aggregation, but contribute a default value instead of a stale one. + +**Goal**: Return a default value when the temperature was not reported within 1 minute of the latest aggregation timestamp. + +```javascript +var ONE_MINUTE = 60 * 1000; +if ((ctx.latestTs - ctx.args.temperature.ts) > ONE_MINUTE) { + return 0; +}; +return temperature; +``` + +### Function return format + +The returned value is passed directly to the aggregation engine: + +| Aggregation function | Expected return value | +|--------------------------------|-----------------------| +| Count, Count unique | any value | +| Sum, Average, Minimum, Maximum | number | + +Returning a value of an incompatible type may result in the entity being ignored or the aggregation producing incorrect results. From 1ff694f0fd0f233012ac3e2093835c59f4d74230 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Tue, 23 Dec 2025 15:21:41 +0200 Subject: [PATCH 16/47] Revert to Java 17 due to fonts issue --- msa/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/msa/pom.xml b/msa/pom.xml index 66e92678ce..a210c45eef 100644 --- a/msa/pom.xml +++ b/msa/pom.xml @@ -32,7 +32,7 @@ ${basedir}/.. thingsboard - thingsboard/openjdk25:trixie-slim + thingsboard/openjdk17:bookworm-slim true true 1.4.13 From cff9286290c507e8f2e953f85b11ffaecdc5ae0f Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Tue, 23 Dec 2025 15:49:02 +0200 Subject: [PATCH 17/47] Add optional latest tag push for LTS --- msa/pom.xml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/msa/pom.xml b/msa/pom.xml index 66e92678ce..6af7521df6 100644 --- a/msa/pom.xml +++ b/msa/pom.xml @@ -134,6 +134,7 @@ 4.2.1-latest + false @@ -141,6 +142,28 @@ org.codehaus.mojo exec-maven-plugin + + push-latest-docker-amd-arm-images + ${docker.push-arm-amd-image.phase} + + exec + + + ${docker.skip.latest.tag} + docker + ${project.build.directory} + + buildx + build + -t + ${docker.repo}/${docker.name}:latest + --platform=linux/amd64,linux/arm64 + -o + type=registry + . + + + push-version-docker-amd-arm-images ${docker.push-arm-amd-image.phase} From 0ea0dcadc04cfc27913cf330444581170341ec10 Mon Sep 17 00:00:00 2001 From: ArtemDzhereleiko Date: Tue, 23 Dec 2025 18:41:40 +0200 Subject: [PATCH 18/47] UI: overide alarm severity colors from widget and dashboard css settings --- ui-ngx/src/app/core/services/utils.service.ts | 4 ++-- .../components/dashboard-page/dashboard-page.component.ts | 1 + .../home/components/dashboard-page/dashboard-page.models.ts | 1 + .../widget/lib/alarm/alarms-table-widget.component.ts | 2 +- .../home/components/widget/widget-container.component.ts | 4 ++-- ui-ngx/src/app/modules/home/models/widget-component.models.ts | 2 ++ 6 files changed, 9 insertions(+), 5 deletions(-) diff --git a/ui-ngx/src/app/core/services/utils.service.ts b/ui-ngx/src/app/core/services/utils.service.ts index a57e3480b1..045725271c 100644 --- a/ui-ngx/src/app/core/services/utils.service.ts +++ b/ui-ngx/src/app/core/services/utils.service.ts @@ -454,11 +454,11 @@ export class UtilsService { return base64toObj(b64Encoded); } - public applyCssToElement(renderer: Renderer2, element: any, cssClassPrefix: string, css: string): string { + public applyCssToElement(renderer: Renderer2, element: any, cssClassPrefix: string, css: string, addTbDefaultClass: boolean = false): string { const cssParser = new cssjs(); cssParser.testMode = false; const cssClass = `${cssClassPrefix}-${guid()}`; - cssParser.cssPreviewNamespace = cssClass; + cssParser.cssPreviewNamespace = addTbDefaultClass ? 'tb-default .' + cssClass : cssClass; cssParser.createStyleElement(cssClass, css); renderer.addClass(element, cssClass); return cssClass; diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts index d0c3325c72..090822a146 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts @@ -548,6 +548,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC const cssParser = new cssjs(); cssParser.testMode = false; this.dashboardPageClass = 'tb-dashboard-page-css-' + guid(); + this.dashboardCtx.dashboardCssClass = this.dashboardPageClass; cssParser.cssPreviewNamespace = 'tb-default .' + this.dashboardPageClass; cssParser.createStyleElement(this.dashboardPageClass, cssString); } diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.models.ts b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.models.ts index a0fa516374..fec05ff2dd 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.models.ts +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.models.ts @@ -51,6 +51,7 @@ export interface DashboardContext { stateChanged: Observable; stateId: Observable; runChangeDetection: () => void; + dashboardCssClass?: string; } export interface IDashboardController { 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 bf715330a7..357eed49d2 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 @@ -968,7 +968,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, (AlarmDetailsDialogComponent, { disableClose: true, - panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + panelClass: ['tb-dialog', 'tb-fullscreen-dialog', this.ctx.stateController.dashboardCtrl.dashboardCtx.dashboardCssClass, this.ctx.widgetCssClass], data: { alarmId: alarm.id.id, allowAcknowledgment: this.allowAcknowledgment, diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-container.component.ts b/ui-ngx/src/app/modules/home/components/widget/widget-container.component.ts index 51ed592e7e..6edac690e7 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-container.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-container.component.ts @@ -151,8 +151,8 @@ export class WidgetContainerComponent extends PageComponent implements OnInit, O this.widget.widgetContext.containerChangeDetector = this.cd; const cssString = this.widget.widget.config.widgetCss; if (isNotEmptyStr(cssString)) { - this.cssClass = - this.utils.applyCssToElement(this.renderer, this.gridsterItem.el, 'tb-widget-css', cssString); + this.cssClass = this.utils.applyCssToElement(this.renderer, this.gridsterItem.el, 'tb-widget-css', cssString, true); + this.widget.widgetContext.widgetCssClass = this.cssClass; } $(this.gridsterItem.el).on('mousedown', (e) => this.onMouseDown(e.originalEvent)); $(this.gridsterItem.el).on('click', (e) => this.onClicked(e.originalEvent)); diff --git a/ui-ngx/src/app/modules/home/models/widget-component.models.ts b/ui-ngx/src/app/modules/home/models/widget-component.models.ts index 293381194e..d3349b2ed4 100644 --- a/ui-ngx/src/app/modules/home/models/widget-component.models.ts +++ b/ui-ngx/src/app/modules/home/models/widget-component.models.ts @@ -308,6 +308,8 @@ export class WidgetContext { widgetNamespace?: string; subscriptionApi?: WidgetSubscriptionApi; + widgetCssClass?: string; + actionsApi?: WidgetActionsApi; activeEntityInfo?: SubscriptionEntityInfo; From 960481752157fe641b7328952ba5563db38c3df2 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Tue, 23 Dec 2025 19:25:14 +0200 Subject: [PATCH 19/47] UI: Add cf details page --- .../calculated-field.component.html | 192 ++++++++++++++++++ .../calculated-field.component.scss | 18 ++ .../calculated-field.component.ts | 166 +++++++++++++++ .../calculated-field.module.ts | 4 +- .../calculated-fields-table-config.ts | 52 +++-- .../calculated-fields-table.component.ts | 4 +- .../debug/entity-debug-settings.service.ts | 4 +- .../calculated-fields-routing.module.ts | 84 +++++++- .../app/shared/models/entity-type.models.ts | 8 +- .../assets/locale/locale.constant-en_US.json | 3 +- 10 files changed, 510 insertions(+), 25 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.html create mode 100644 ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.ts diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.html b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.html new file mode 100644 index 0000000000..5513770627 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.html @@ -0,0 +1,192 @@ + +
+ + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
{{ 'common.general' | translate }}
+
+ + {{ 'entity-field.title' | translate }} + + @if (entityForm.get('name').errors && entityForm.get('name').touched) { + + @if (entityForm.get('name').hasError('required')) { + {{ 'common.hint.title-required' | translate }} + } @else if (entityForm.get('name').hasError('pattern')) { + {{ 'common.hint.title-pattern' | translate }} + } @else if (entityForm.get('name').hasError('maxlength')) { + {{ 'common.hint.title-max-length' | translate }} + } + + } + + + +
+ + + {{ 'common.type' | translate }} + + @for (type of fieldTypes; track type) { + {{ CalculatedFieldTypeTranslations.get(type).name | translate}} + } + + @if (CalculatedFieldTypeTranslations.get(entityForm.get('type').value).hint) { + {{ CalculatedFieldTypeTranslations.get(entityForm.get('type').value).hint | translate }} + } + +
+ @switch (entityForm.get('type').value) { + @case (CalculatedFieldType.GEOFENCING) { + + } + @case (CalculatedFieldType.PROPAGATION) { + + } + @case (CalculatedFieldType.RELATED_ENTITIES_AGGREGATION) { + + } + @case (CalculatedFieldType.ENTITY_AGGREGATION) { + + } + @default { + + } + } +
+
+
diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.scss b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.scss new file mode 100644 index 0000000000..7cfc6dfef2 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.scss @@ -0,0 +1,18 @@ +/** + * Copyright © 2016-2025 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +:host { + +} diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.ts new file mode 100644 index 0000000000..c521c624d4 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.ts @@ -0,0 +1,166 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { ChangeDetectorRef, Component, DestroyRef, Inject, Input } from '@angular/core'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { EntityComponent } from '../../components/entity/entity.component'; +import { FormBuilder, FormGroup, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { AliasEntityType, EntityType } from '@shared/models/entity-type.models'; +import { NULL_UUID } from '@shared/models/id/has-uuid'; +import { ActionNotificationShow } from '@core/notification/notification.actions'; +import { TranslateService } from '@ngx-translate/core'; +import { AssetInfo } from '@app/shared/models/asset.models'; +import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; +import { + CalculatedFieldConfiguration, + CalculatedFieldInfo, calculatedFieldsEntityTypeList, + CalculatedFieldType, calculatedFieldTypes, CalculatedFieldTypeTranslations, OutputStrategyType +} from '@shared/models/calculated-field.models'; +import { oneSpaceInsideRegex } from '@shared/models/regex.constants'; +import { isDefined } from '@core/utils'; +import { EntityId } from '@shared/models/id/entity-id'; +import { pairwise, switchMap } from 'rxjs/operators'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { BaseData } from '@shared/models/base-data'; +import { Observable, of } from 'rxjs'; +import { CalculatedFieldsService } from '@core/http/calculated-fields.service'; +import { getCurrentAuthUser } from '@core/auth/auth.selectors'; +import { + CalculatedFieldsTableConfig, + CalculatedFieldsTableEntity +} from '@home/components/calculated-fields/calculated-fields-table-config'; + +@Component({ + selector: 'tb-calculated-field', + templateUrl: './calculated-field.component.html', + styleUrls: ['./calculated-field.component.scss'] +}) +export class CalculatedFieldComponent extends EntityComponent { + + @Input() + standalone = false; + + @Input() + entityName: string; + + disabledConfiguration = false; + + readonly tenantId = getCurrentAuthUser(this.store).tenantId; + readonly ownerId = getCurrentAuthUser(this.store).tenantId; + readonly EntityType = EntityType; + readonly calculatedFieldsEntityTypeList = calculatedFieldsEntityTypeList; + readonly CalculatedFieldType = CalculatedFieldType; + readonly fieldTypes = calculatedFieldTypes; + readonly CalculatedFieldTypeTranslations = CalculatedFieldTypeTranslations; + + constructor(protected store: Store, + protected translate: TranslateService, + @Inject('entity') protected entityValue: CalculatedFieldInfo, + @Inject('entitiesTableConfig') protected entitiesTableConfigValue: CalculatedFieldsTableConfig, + protected fb: FormBuilder, + protected cd: ChangeDetectorRef, + private destroyRef: DestroyRef, + private calculatedFieldsService: CalculatedFieldsService) { + super(store, fb, entityValue, entitiesTableConfigValue, cd); + } + + hideDelete() { + if (this.entitiesTableConfig) { + return !this.entitiesTableConfig.deleteEnabled(this.entity); + } else { + return false; + } + } + + isAssignedToCustomer(entity: AssetInfo): boolean { + return entity && entity.customerId && entity.customerId.id !== NULL_UUID; + } + + get entityId(): EntityId { + return this.entityForm.get('entityId').value; + } + + get entitiesTableConfig(): CalculatedFieldsTableConfig { + return this.entitiesTableConfigValue; + } + + changeEntity(entity: BaseData): void { + this.entityName = entity?.name; + } + + buildForm(entity?: CalculatedFieldInfo): FormGroup { + const form = this.fb.group({ + name: ['', [Validators.required, Validators.pattern(oneSpaceInsideRegex), Validators.maxLength(255)]], + entityId: [null], + type: [CalculatedFieldType.SIMPLE], + debugSettings: [], + configuration: this.fb.control({} as CalculatedFieldConfiguration), + }); + form.get('type').valueChanges.pipe( + pairwise(), + takeUntilDestroyed(this.destroyRef) + ).subscribe(([prevType, nextType]) => { + if (![CalculatedFieldType.SIMPLE, CalculatedFieldType.SCRIPT].includes(prevType) || + ![CalculatedFieldType.SIMPLE, CalculatedFieldType.SCRIPT].includes(nextType)) { + form.get('configuration').setValue(({} as CalculatedFieldConfiguration), {emitEvent: false}); + } + }); + return form; + } + + updateForm(entity: CalculatedFieldInfo) { + const { configuration = {} as CalculatedFieldConfiguration, type = CalculatedFieldType.SIMPLE, debugSettings = { failuresEnabled: true, allEnabled: true }, entityId = this.entityId, ...value } = entity ?? {}; + if (configuration.type !== CalculatedFieldType.ALARM) { + if (isDefined(configuration?.output) && !configuration?.output?.strategy) { + configuration.output.strategy = {type: OutputStrategyType.RULE_CHAIN}; + } + } + this.entityForm.patchValue({ configuration, type, debugSettings, entityId, ...value }, {emitEvent: false}); + setTimeout(() => this.entityForm.get('type').updateValueAndValidity({onlySelf: true})); + if (!entityId) { + this.entityForm.get('configuration').disable({emitEvent: false}); + this.disabledConfiguration = true; + } + } + + onTestScript(expression?: string): Observable { + const calculatedFieldId = this.entityId?.id; + if (calculatedFieldId) { + return this.calculatedFieldsService.getLatestCalculatedFieldDebugEvent(calculatedFieldId, {ignoreLoading: true}) + .pipe( + switchMap(event => { + const args = event?.arguments ? JSON.parse(event.arguments) : null; + return this.entitiesTableConfig.getTestScriptDialog(this.entityFormValue(), args, false, expression); + }), + takeUntilDestroyed(this.destroyRef) + ) + } + + return this.entitiesTableConfig.getTestScriptDialog(this.entityFormValue(), null, false, expression); + } + + updateFormState() { + if (this.entityForm) { + if (this.isEditValue) { + this.entityForm.enable({emitEvent: false}); + this.entityForm.get('entityId').disable({emitEvent: false}); + } else { + this.entityForm.disable({emitEvent: false}); + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.module.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.module.ts index e3f5ac9cc2..8a9241970d 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.module.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.module.ts @@ -50,6 +50,7 @@ import { import { CalculatedFieldsFilterConfigComponent } from '@home/components/calculated-fields/table-header/calculated-fields-filter-config.component'; +import { CalculatedFieldComponent } from '@home/components/calculated-fields/calculated-field.component'; @NgModule({ declarations: [ @@ -57,7 +58,8 @@ import { CalculatedFieldScriptTestDialogComponent, CalculatedFieldTestArgumentsComponent, CalculatedFieldsHeaderComponent, - CalculatedFieldsFilterConfigComponent + CalculatedFieldsFilterConfigComponent, + CalculatedFieldComponent ], imports: [ CommonModule, diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table-config.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table-config.ts index 8385b5cb45..977e03e454 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table-config.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table-config.ts @@ -20,7 +20,7 @@ import { EntityTableColumn, EntityTableConfig } from '@home/models/entity/entities-table-config.models'; -import { EntityType, entityTypeTranslations } from '@shared/models/entity-type.models'; +import { EntityType, entityTypeResources, entityTypeTranslations } from '@shared/models/entity-type.models'; import { TranslateService } from '@ngx-translate/core'; import { Direction } from '@shared/models/page/sort-order'; import { MatDialog, MatDialogRef } from '@angular/material/dialog'; @@ -67,8 +67,11 @@ import { EventsDialogComponent, EventsDialogData } from '@home/dialogs/events-di import { CalculatedFieldsHeaderComponent } from '@home/components/calculated-fields/table-header/calculated-fields-header.component'; +import { EntityAction } from '@home/models/entity/entity-component.models'; +import { CalculatedFieldComponent } from '@home/components/calculated-fields/calculated-field.component'; +import { Router } from '@angular/router'; -type CalculatedFieldsTableEntity = CalculatedField | CalculatedFieldInfo; +export type CalculatedFieldsTableEntity = CalculatedField | CalculatedFieldInfo; export class CalculatedFieldsTableConfig extends EntityTableConfig { @@ -93,30 +96,33 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig { - this.editCalculatedField($event, entity); - this.rowPointer = true; - return true; - }; + this.entityComponent = CalculatedFieldComponent; + this.rowPointer = true; } this.tableTitle = this.pageMode ? '' : this.translate.instant('entity.type-calculated-fields'); - this.detailsPanelEnabled = false; + this.detailsPanelEnabled = this.pageMode; this.entityType = EntityType.CALCULATED_FIELD; this.entityTranslations = entityTypeTranslations.get(EntityType.CALCULATED_FIELD); + this.entityResources = entityTypeResources.get(EntityType.CALCULATED_FIELD); this.entitiesFetchFunction = (pageLink: PageLink) => this.fetchCalculatedFields(pageLink); this.addEntity = this.getCalculatedFieldDialog.bind(this); + this.loadEntity = id => this.calculatedFieldsService.getCalculatedFieldById(id.id); this.deleteEntityTitle = (field) => this.translate.instant('calculated-fields.delete-title', {title: field.name}); this.deleteEntityContent = () => this.translate.instant('calculated-fields.delete-text'); this.deleteEntitiesTitle = count => this.translate.instant('calculated-fields.delete-multiple-title', {count}); this.deleteEntitiesContent = () => this.translate.instant('calculated-fields.delete-multiple-text'); this.deleteEntity = id => this.calculatedFieldsService.deleteCalculatedField(id.id); + + this.onEntityAction = action => this.onCFAction(action); + this.addActionDescriptors = [ { name: this.translate.instant('calculated-fields.create'), @@ -172,14 +178,16 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig true, iconFunction: ({ debugSettings }) => this.entityDebugSettingsService.isDebugActive(debugSettings?.allEnabledUntil) || debugSettings?.failuresEnabled ? 'mdi:bug' : 'mdi:bug-outline', onAction: ($event, entity) => this.onOpenDebugConfig($event, entity), - }, - { + } + ); + if (!this.pageMode) { + this.cellActionDescriptors.push({ name: this.translate.instant('action.edit'), icon: 'edit', isEnabled: () => true, onAction: ($event, entity) => this.editCalculatedField($event, entity), - } - ); + }) + } } fetchCalculatedFields(pageLink: PageLink): Observable> { @@ -354,7 +362,7 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig this.updateData()); } - private getTestScriptDialog(calculatedField: CalculatedFieldsTableEntity, argumentsObj?: CalculatedFieldEventArguments, openCalculatedFieldEdit = true, expression?: string): Observable { + getTestScriptDialog(calculatedField: CalculatedFieldsTableEntity, argumentsObj?: CalculatedFieldEventArguments, openCalculatedFieldEdit = true, expression?: string): Observable { if ( calculatedField.type === CalculatedFieldType.SCRIPT || calculatedField.type === CalculatedFieldType.RELATED_ENTITIES_AGGREGATION || @@ -394,4 +402,22 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig): boolean { + switch (action.action) { + case 'open': + this.openCalculatedField(action.event, action.entity); + return true; + case 'export': + this.exportCalculatedField(action.event, action.entity); + return true; + } + return false; + } } diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table.component.ts index 2a0c29e31a..7687277113 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table.component.ts @@ -36,7 +36,7 @@ import { ImportExportService } from '@shared/import-export/import-export.service import { EntityDebugSettingsService } from '@home/components/entity/debug/entity-debug-settings.service'; import { DatePipe } from '@angular/common'; import { UtilsService } from "@core/services/utils.service"; -import { ActivatedRoute } from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; @Component({ selector: 'tb-calculated-fields-table', @@ -70,6 +70,7 @@ export class CalculatedFieldsTableComponent { private utilsService: UtilsService, private destroyRef: DestroyRef, private route: ActivatedRoute, + private router: Router ) { this.pageMode = !!this.route.snapshot.data.isPage; effect(() => { @@ -88,6 +89,7 @@ export class CalculatedFieldsTableComponent { this.importExportService, this.entityDebugSettingsService, this.utilsService, + this.router, this.pageMode, ); this.cd.markForCheck(); diff --git a/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings.service.ts b/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings.service.ts index cb982b9650..76def20a24 100644 --- a/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings.service.ts +++ b/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings.service.ts @@ -22,7 +22,9 @@ import { TranslateService } from '@ngx-translate/core'; import { DurationLeftPipe } from '@shared/pipe/duration-left.pipe'; import { EntityDebugSettingPanelConfig } from '@home/components/entity/debug/entity-debug-settings.model'; -@Injectable() +@Injectable({ + providedIn: 'root' +}) export class EntityDebugSettingsService { constructor( diff --git a/ui-ngx/src/app/modules/home/pages/calculated-fields/calculated-fields-routing.module.ts b/ui-ngx/src/app/modules/home/pages/calculated-fields/calculated-fields-routing.module.ts index 0ed435075c..03be3d86f6 100644 --- a/ui-ngx/src/app/modules/home/pages/calculated-fields/calculated-fields-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/calculated-fields/calculated-fields-routing.module.ts @@ -14,24 +14,94 @@ /// limitations under the License. /// -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { DestroyRef, inject, NgModule, Renderer2 } from '@angular/core'; +import { ActivatedRouteSnapshot, ResolveFn, Router, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { Authority } from '@shared/models/authority.enum'; import { MenuId } from '@core/services/menu.models'; import { CalculatedFieldsTableComponent } from '@home/components/calculated-fields/calculated-fields-table.component'; +import { EntityDetailsPageComponent } from '@home/components/entity/entity-details-page.component'; +import { ConfirmOnExitGuard } from '@core/guards/confirm-on-exit.guard'; +import { entityDetailsPageBreadcrumbLabelFunction } from '@home/pages/home-pages.models'; +import { BreadCrumbConfig } from '@shared/components/breadcrumb'; +import { CalculatedFieldsTableConfig } from '@home/components/calculated-fields/calculated-fields-table-config'; +import { CalculatedFieldsService } from '@core/http/calculated-fields.service'; +import { TranslateService } from '@ngx-translate/core'; +import { MatDialog } from '@angular/material/dialog'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { DatePipe } from '@angular/common'; +import { ImportExportService } from '@shared/import-export/import-export.service'; +import { EntityDebugSettingsService } from '@home/components/entity/debug/entity-debug-settings.service'; +import { UtilsService } from '@core/services/utils.service'; + +export const CalculatedFieldsTableConfigResolver: ResolveFn = + (_route: ActivatedRouteSnapshot, + _state: RouterStateSnapshot, + calculatedFieldsService = inject(CalculatedFieldsService), + translate = inject(TranslateService), + dialog = inject(MatDialog), + store = inject(Store), + datePipe = inject(DatePipe), + destroyRef = inject(DestroyRef), + importExportService = inject(ImportExportService), + entityDebugSettingsService = inject(EntityDebugSettingsService), + utilsService = inject(UtilsService), + router = inject(Router), + ) => { + return new CalculatedFieldsTableConfig( + calculatedFieldsService, + translate, + dialog, + datePipe, + null, + store, + destroyRef, + null, + null, + null, + importExportService, + entityDebugSettingsService, + utilsService, + router, + false, + ); + }; const routes: Routes = [ { path: 'calculatedFields', - component: CalculatedFieldsTableComponent, data: { - auth: [Authority.TENANT_ADMIN], - title: 'entity.type-calculated-fields', breadcrumb: { menuId: MenuId.calculated_fields + } + }, + children: [ + { + path: '', + component: CalculatedFieldsTableComponent, + data: { + auth: [Authority.TENANT_ADMIN], + title: 'entity.type-calculated-fields', + isPage: true, + } }, - isPage: true, - } + { + path: ':entityId', + component: EntityDetailsPageComponent, + canDeactivate: [ConfirmOnExitGuard], + data: { + breadcrumb: { + labelFunction: entityDetailsPageBreadcrumbLabelFunction, + icon: 'mdi:function-variant' + } as BreadCrumbConfig, + auth: [Authority.TENANT_ADMIN], + title: 'entity.type-calculated-fields', + }, + resolve: { + entitiesTableConfig: CalculatedFieldsTableConfigResolver + } + } + ] } ]; diff --git a/ui-ngx/src/app/shared/models/entity-type.models.ts b/ui-ngx/src/app/shared/models/entity-type.models.ts index 48428932e4..bc0d3fd470 100644 --- a/ui-ngx/src/app/shared/models/entity-type.models.ts +++ b/ui-ngx/src/app/shared/models/entity-type.models.ts @@ -490,7 +490,7 @@ export const entityTypeTranslations = new Map Date: Wed, 24 Dec 2025 11:53:15 +0200 Subject: [PATCH 20/47] Exclude TbAiNode for Edge-4.1 --- .../server/service/edge/EdgeMsgConstructorUtils.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/EdgeMsgConstructorUtils.java b/application/src/main/java/org/thingsboard/server/service/edge/EdgeMsgConstructorUtils.java index e1c7f1198c..c5579098a4 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/EdgeMsgConstructorUtils.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/EdgeMsgConstructorUtils.java @@ -27,6 +27,7 @@ import com.google.gson.reflect.TypeToken; import lombok.extern.slf4j.Slf4j; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.action.TbSaveToCustomCassandraTableNode; +import org.thingsboard.rule.engine.ai.TbAiNode; import org.thingsboard.rule.engine.aws.lambda.TbAwsLambdaNode; import org.thingsboard.rule.engine.rest.TbSendRestApiCallReplyNode; import org.thingsboard.rule.engine.telemetry.TbCalculatedFieldsNode; @@ -167,6 +168,10 @@ public class EdgeMsgConstructorUtils { ); public static final Map> EXCLUDED_NODES_BY_EDGE_VERSION = Map.of( + EdgeVersion.V_4_1_0, + Set.of( + TbAiNode.class.getName() + ), EdgeVersion.V_3_9_0, Set.of( TbCalculatedFieldsNode.class.getName() From bd95161fa7cee882c2b43c53a6ea45dd176045ee Mon Sep 17 00:00:00 2001 From: Nikita Mazurenko Date: Wed, 24 Dec 2025 15:02:57 +0200 Subject: [PATCH 21/47] Exclude TbAiNode for Edge-4.0 --- .../server/service/edge/EdgeMsgConstructorUtils.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/EdgeMsgConstructorUtils.java b/application/src/main/java/org/thingsboard/server/service/edge/EdgeMsgConstructorUtils.java index c5579098a4..513163d54a 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/EdgeMsgConstructorUtils.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/EdgeMsgConstructorUtils.java @@ -172,6 +172,10 @@ public class EdgeMsgConstructorUtils { Set.of( TbAiNode.class.getName() ), + EdgeVersion.V_4_0_0, + Set.of( + TbAiNode.class.getName() + ), EdgeVersion.V_3_9_0, Set.of( TbCalculatedFieldsNode.class.getName() From ac7c710e5df60c18032615a97147364ba7038e87 Mon Sep 17 00:00:00 2001 From: ArtemDzhereleiko Date: Wed, 24 Dec 2025 16:03:02 +0200 Subject: [PATCH 22/47] UI: Fixed bug with cf page --- .../calculated-field.component.html | 7 ++++++- .../calculated-fields/calculated-field.component.ts | 2 +- .../calculated-fields-table-config.ts | 2 +- .../output/calculated-field-output.component.ts | 2 +- .../simple-configuration.component.ts | 2 +- .../components/entity/entity-select.component.html | 2 ++ .../components/entity/entity-select.component.ts | 13 ++++++++++++- 7 files changed, 24 insertions(+), 6 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.html b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.html index 5513770627..10ab150b69 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.html +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.html @@ -125,7 +125,12 @@ [additionalActionConfig]="entitiesTableConfig.additionalDebugActionConfig" />
- + + {{ 'common.type' | translate }} diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.ts index c521c624d4..0b62d9bb96 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.ts @@ -60,7 +60,7 @@ export class CalculatedFieldComponent extends EntityComponent void): void { diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/simple-configuration/simple-configuration.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/simple-configuration/simple-configuration.component.ts index e6ff1d1bad..2771795635 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/simple-configuration/simple-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/simple-configuration/simple-configuration.component.ts @@ -109,6 +109,7 @@ export class SimpleConfigurationComponent implements ControlValueAccessor, Valid private propagateChange: (config: SimpeConfiguration) => void = () => { }; constructor(private fb: FormBuilder) { + this.updatedFormWithScript(); this.simpleConfiguration.get('output').valueChanges.pipe( takeUntilDestroyed(), ).subscribe(() => { @@ -151,7 +152,6 @@ export class SimpleConfigurationComponent implements ControlValueAccessor, Valid formValue.expressionSIMPLE = formValue.expression; } this.simpleConfiguration.patchValue(formValue, {emitEvent: false}); - this.updatedFormWithScript(); setTimeout(() => { this.simpleConfiguration.get('arguments').updateValueAndValidity({onlySelf: true}); }); diff --git a/ui-ngx/src/app/shared/components/entity/entity-select.component.html b/ui-ngx/src/app/shared/components/entity/entity-select.component.html index c0b4f1aafb..49e853e722 100644 --- a/ui-ngx/src/app/shared/components/entity/entity-select.component.html +++ b/ui-ngx/src/app/shared/components/entity/entity-select.component.html @@ -25,6 +25,7 @@ [useAliasEntityTypes]="useAliasEntityTypes" [allowedEntityTypes]="allowedEntityTypes" [additionEntityTypes]="additionEntityTypes" + [filterAllowedEntityTypes]="filterAllowedEntityTypes" formControlName="entityType">
diff --git a/ui-ngx/src/app/shared/components/entity/entity-select.component.ts b/ui-ngx/src/app/shared/components/entity/entity-select.component.ts index 5e75426952..fbfb7ae453 100644 --- a/ui-ngx/src/app/shared/components/entity/entity-select.component.ts +++ b/ui-ngx/src/app/shared/components/entity/entity-select.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { AfterViewInit, Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; +import { AfterViewInit, Component, DestroyRef, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; @@ -26,6 +26,7 @@ import { NULL_UUID } from '@shared/models/id/has-uuid'; import { coerceBoolean } from '@shared/decorators/coercion'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { MatFormFieldAppearance } from '@angular/material/form-field'; +import { BaseData } from '@shared/models/base-data'; @Component({ selector: 'tb-entity-select', @@ -66,6 +67,12 @@ export class EntitySelectComponent implements ControlValueAccessor, OnInit, Afte @coerceBoolean() useEntityDisplayName = false; + @Input() + filterAllowedEntityTypes: boolean; + + @Output() + entityChanged = new EventEmitter>(); + displayEntityTypeSelect: boolean; AliasEntityType = AliasEntityType; @@ -176,4 +183,8 @@ export class EntitySelectComponent implements ControlValueAccessor, OnInit, Afte } } } + + changeEntity(entity: BaseData): void { + this.entityChanged.emit(entity); + } } From a35bbcc1de6d2ddfb845ca6c8295aa7496ec15df Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Wed, 24 Dec 2025 16:31:11 +0200 Subject: [PATCH 23/47] handle future telemetry and fill intervals after restart based on watermark --- ...tractCalculatedFieldProcessingService.java | 6 +- .../EntityAggregationArgumentEntry.java | 74 ++++++++++++++++--- ...EntityAggregationCalculatedFieldState.java | 18 +++++ .../utils/CalculatedFieldArgumentUtils.java | 4 +- .../EntityAggregationCalculatedFieldTest.java | 53 ++++++++++++- 5 files changed, 135 insertions(+), 20 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/cf/AbstractCalculatedFieldProcessingService.java b/application/src/main/java/org/thingsboard/server/service/cf/AbstractCalculatedFieldProcessingService.java index ec645085e6..cd88c93a71 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/AbstractCalculatedFieldProcessingService.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/AbstractCalculatedFieldProcessingService.java @@ -233,7 +233,7 @@ public abstract class AbstractCalculatedFieldProcessingService { return config.getArguments().entrySet().stream() .collect(Collectors.toMap( Map.Entry::getKey, - entry -> fetchTimeSeries(ctx.getTenantId(), entityId, entry.getValue(), config.getInterval(), ts) + entry -> fetchTimeSeries(ctx, entityId, entry.getValue(), config.getInterval(), ts) )); } @@ -341,11 +341,11 @@ public abstract class AbstractCalculatedFieldProcessingService { return resolveArgumentValue(argKey, argumentEntryFut); } - private ListenableFuture fetchTimeSeries(TenantId tenantId, EntityId entityId, Argument argument, AggInterval interval, long queryEndTs) { + private ListenableFuture fetchTimeSeries(CalculatedFieldCtx ctx, EntityId entityId, Argument argument, AggInterval interval, long queryEndTs) { long intervalStartTs = interval.getCurrentIntervalStartTs(); long intervalEndTs = interval.getCurrentIntervalEndTs(); ReadTsKvQuery query = new BaseReadTsKvQuery(argument.getRefEntityKey().getKey(), intervalStartTs, queryEndTs, 0, 1, Aggregation.NONE); - return fetchTimeSeriesInternal(tenantId, entityId, query, timeSeries -> transformAggregationArgument(timeSeries, intervalStartTs, intervalEndTs)); + return fetchTimeSeriesInternal(ctx.getTenantId(), entityId, query, timeSeries -> transformAggregationArgument(timeSeries, intervalStartTs, intervalEndTs, ctx)); } private ListenableFuture fetchTsRolling(TenantId tenantId, EntityId entityId, Argument argument, long queryEndTs) { diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/single/EntityAggregationArgumentEntry.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/single/EntityAggregationArgumentEntry.java index 7ec5098bc3..ef34b6ae8e 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/single/EntityAggregationArgumentEntry.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/single/EntityAggregationArgumentEntry.java @@ -19,11 +19,18 @@ import com.fasterxml.jackson.databind.JsonNode; import lombok.Data; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.script.api.tbel.TbelCfArg; +import org.thingsboard.server.common.data.cf.configuration.aggregation.single.EntityAggregationCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.aggregation.single.interval.AggInterval; +import org.thingsboard.server.common.data.cf.configuration.aggregation.single.interval.Watermark; import org.thingsboard.server.service.cf.ctx.state.ArgumentEntry; import org.thingsboard.server.service.cf.ctx.state.ArgumentEntryType; +import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldCtx; import org.thingsboard.server.service.cf.ctx.state.SingleValueArgumentEntry; +import java.time.Instant; +import java.time.ZonedDateTime; import java.util.Map; +import java.util.concurrent.TimeUnit; @Data public class EntityAggregationArgumentEntry implements ArgumentEntry { @@ -32,10 +39,18 @@ public class EntityAggregationArgumentEntry implements ArgumentEntry { private boolean forceResetPrevious; + private AggInterval interval; + private long watermarkDuration; + public EntityAggregationArgumentEntry(Map aggIntervals) { this.aggIntervals = aggIntervals; } + public EntityAggregationArgumentEntry(Map aggIntervals, CalculatedFieldCtx ctx) { + this(aggIntervals); + setCtx(ctx); + } + @Override public ArgumentEntryType getType() { return ArgumentEntryType.ENTITY_AGGREGATION; @@ -46,29 +61,64 @@ public class EntityAggregationArgumentEntry implements ArgumentEntry { return aggIntervals; } + public void setCtx(CalculatedFieldCtx ctx) { + var configuration = (EntityAggregationCalculatedFieldConfiguration) ctx.getCalculatedField().getConfiguration(); + interval = configuration.getInterval(); + Watermark watermark = configuration.getWatermark(); + watermarkDuration = watermark == null ? 0 : TimeUnit.SECONDS.toMillis(watermark.getDuration()); + } + @Override public boolean updateEntry(ArgumentEntry entry) { - boolean updated = false; if (entry instanceof EntityAggregationArgumentEntry entityAggEntry) { aggIntervals.putAll(entityAggEntry.getAggIntervals()); + return true; } else if (entry instanceof SingleValueArgumentEntry singleValueArgEntry) { long entryTs = singleValueArgEntry.getTs(); - long argUpdateTs = System.currentTimeMillis(); - for (Map.Entry aggIntervalEntry : aggIntervals.entrySet()) { - if (singleValueArgEntry.isForceResetPrevious()) { - aggIntervalEntry.getValue().setLastArgsRefreshTs(argUpdateTs); - updated = true; - continue; - } - if (aggIntervalEntry.getKey().belongsToInterval(entryTs)) { - aggIntervalEntry.getValue().setLastArgsRefreshTs(argUpdateTs); - return true; - } + long now = System.currentTimeMillis(); + if (updateExistingIntervals(singleValueArgEntry, entryTs, now)) { + return true; + } + return createNewInterval(entryTs, now); + } + return false; + } + + private boolean updateExistingIntervals(SingleValueArgumentEntry entry, long entryTs, long now) { + boolean updated = false; + + for (Map.Entry aggIntervalEntry : aggIntervals.entrySet()) { + AggIntervalEntry interval = aggIntervalEntry.getKey(); + AggIntervalEntryStatus status = aggIntervalEntry.getValue(); + if (entry.isForceResetPrevious()) { + status.setLastArgsRefreshTs(now); + updated = true; + continue; + } + if (interval.belongsToInterval(entryTs)) { + status.setLastArgsRefreshTs(now); + return true; } } + return updated; } + private boolean createNewInterval(long entryTs, long now) { + ZonedDateTime zdt = ZonedDateTime.ofInstant(Instant.ofEpochMilli(entryTs), interval.getZoneId()); + + long startTs = interval.getDateTimeIntervalStartTs(zdt); + long endTs = interval.getDateTimeIntervalEndTs(zdt); + + if (now - endTs > watermarkDuration) { + return false; + } + + AggIntervalEntry newInterval = new AggIntervalEntry(startTs, endTs); + aggIntervals.computeIfAbsent(newInterval, i -> new AggIntervalEntryStatus(now)); + return true; + } + @Override public boolean isEmpty() { return aggIntervals.isEmpty(); diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/single/EntityAggregationCalculatedFieldState.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/single/EntityAggregationCalculatedFieldState.java index 0f6e01a344..59335717fb 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/single/EntityAggregationCalculatedFieldState.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/single/EntityAggregationCalculatedFieldState.java @@ -82,6 +82,15 @@ public class EntityAggregationCalculatedFieldState extends BaseCalculatedFieldSt interval = configuration.getInterval(); metrics = configuration.getMetrics(); produceIntermediateResult = configuration.isProduceIntermediateResult(); + setCtxToArguments(); + } + + private void setCtxToArguments() { + arguments.values().forEach(argument -> { + if (argument instanceof EntityAggregationArgumentEntry entityAggArgument) { + entityAggArgument.setCtx(ctx); + } + }); } @Override @@ -154,8 +163,10 @@ public class EntityAggregationCalculatedFieldState extends BaseCalculatedFieldSt } private void fillMissingIntervals() { + long now = System.currentTimeMillis(); ZoneId zoneId = interval.getZoneId(); long currentIntervalEndTs = interval.getCurrentIntervalEndTs(); + long watermarkThresholdTs = now - watermarkDuration; Map> intervals = getIntervals(); AggIntervalEntry lastIntervalEntry = intervals.keySet().stream().max(Comparator.comparing(AggIntervalEntry::getEndTs)).orElse(null); @@ -169,6 +180,13 @@ public class EntityAggregationCalculatedFieldState extends BaseCalculatedFieldSt while (nextEnd.toInstant().toEpochMilli() <= currentIntervalEndTs) { long nextStartTs = nextStart.toInstant().toEpochMilli(); long nextEndTs = nextEnd.toInstant().toEpochMilli(); + + if (nextEndTs < watermarkThresholdTs) { + nextStart = nextEnd; + nextEnd = interval.getNextIntervalStart(nextStart); + continue; + } + AggIntervalEntry missing = new AggIntervalEntry(nextStartTs, nextEndTs); arguments.forEach((argName, argumentEntry) -> { diff --git a/application/src/main/java/org/thingsboard/server/utils/CalculatedFieldArgumentUtils.java b/application/src/main/java/org/thingsboard/server/utils/CalculatedFieldArgumentUtils.java index 7e0701cd2d..d6f08cbc69 100644 --- a/application/src/main/java/org/thingsboard/server/utils/CalculatedFieldArgumentUtils.java +++ b/application/src/main/java/org/thingsboard/server/utils/CalculatedFieldArgumentUtils.java @@ -75,7 +75,7 @@ public class CalculatedFieldArgumentUtils { return new SingleValueArgumentEntry(); } - public static ArgumentEntry transformAggregationArgument(List timeSeries, long startIntervalTs, long endIntervalTs) { + public static ArgumentEntry transformAggregationArgument(List timeSeries, long startIntervalTs, long endIntervalTs, CalculatedFieldCtx ctx) { Map aggIntervals = new HashMap<>(); AggIntervalEntry aggIntervalEntry = new AggIntervalEntry(startIntervalTs, endIntervalTs); if (timeSeries == null || timeSeries.isEmpty()) { @@ -83,7 +83,7 @@ public class CalculatedFieldArgumentUtils { } else { aggIntervals.put(aggIntervalEntry, new AggIntervalEntryStatus(System.currentTimeMillis())); } - return new EntityAggregationArgumentEntry(aggIntervals); + return new EntityAggregationArgumentEntry(aggIntervals, ctx); } private static KvEntry createDefaultKvEntry(Argument argument) { diff --git a/application/src/test/java/org/thingsboard/server/cf/EntityAggregationCalculatedFieldTest.java b/application/src/test/java/org/thingsboard/server/cf/EntityAggregationCalculatedFieldTest.java index 253d02ebe5..66c11a06a6 100644 --- a/application/src/test/java/org/thingsboard/server/cf/EntityAggregationCalculatedFieldTest.java +++ b/application/src/test/java/org/thingsboard/server/cf/EntityAggregationCalculatedFieldTest.java @@ -55,6 +55,8 @@ import static org.thingsboard.server.cf.CalculatedFieldIntegrationTest.POLL_INTE @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) public class EntityAggregationCalculatedFieldTest extends AbstractControllerTest { + private final String TZ = "Europe/Kyiv"; + private Tenant savedTenant; @Before @@ -93,7 +95,7 @@ public class EntityAggregationCalculatedFieldTest extends AbstractControllerTest public void testCreateCfAndNoTelemetryDuringInterval_checkAggregation() throws Exception { Device device = createDevice("Device", "1234567890111"); - CustomInterval customInterval = new CustomInterval("Europe/Kyiv", 0L, 5L); + CustomInterval customInterval = new CustomInterval(TZ, 0L, 5L); createConsumptionCF(device.getId(), customInterval, null); long interval = customInterval.getCurrentIntervalDurationMillis(); @@ -113,7 +115,7 @@ public class EntityAggregationCalculatedFieldTest extends AbstractControllerTest public void testCreateCfWithoutWatermark_checkAggregation() throws Exception { Device device = createDevice("Device", "1234567890111"); - CustomInterval customInterval = new CustomInterval("Europe/Kyiv", 0L, 5L); + CustomInterval customInterval = new CustomInterval(TZ, 0L, 5L); createConsumptionCF(device.getId(), customInterval, null); long currentIntervalStartTs = customInterval.getCurrentIntervalStartTs(); @@ -156,7 +158,7 @@ public class EntityAggregationCalculatedFieldTest extends AbstractControllerTest public void testCreateCfWithWatermark_checkAggregationDuringWatermark() throws Exception { Device device = createDevice("Device", "1234567890111"); - CustomInterval customInterval = new CustomInterval("Europe/Kyiv", 0L, 5L); + CustomInterval customInterval = new CustomInterval(TZ, 0L, 5L); Watermark watermark = new Watermark(10); createConsumptionCF(device.getId(), customInterval, watermark); @@ -196,6 +198,51 @@ public class EntityAggregationCalculatedFieldTest extends AbstractControllerTest }); } + @Test + public void testSendFutureTelemetry_checkAggregation() throws Exception { + Device device = createDevice("Device", "1234567890111"); + + CustomInterval customInterval = new CustomInterval(TZ, 0L, 2L); + createConsumptionCF(device.getId(), customInterval, null); + + long currentIntervalStartTs = customInterval.getCurrentIntervalStartTs(); + + long tsBeforeInterval = currentIntervalStartTs - 1000; + long tsInInterval_1 = currentIntervalStartTs + 1000; + long tsInInterval_2 = currentIntervalStartTs + 500; + long tsInInterval_3 = currentIntervalStartTs + 200; + postTelemetry(device.getId(), String.format("{\"ts\": \"%s\", \"values\": {\"energy\":120}}", tsBeforeInterval)); + postTelemetry(device.getId(), String.format("{\"ts\": \"%s\", \"values\": {\"energy\":100}}", tsInInterval_1)); + postTelemetry(device.getId(), String.format("{\"ts\": \"%s\", \"values\": {\"energy\":180}}", tsInInterval_2)); + postTelemetry(device.getId(), String.format("{\"ts\": \"%s\", \"values\": {\"energy\":120}}", tsInInterval_3)); + + long interval = customInterval.getCurrentIntervalDurationMillis(); + + await().alias("create CF -> perform aggregation after interval end") + .atMost(2 * interval, TimeUnit.MILLISECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + ObjectNode result = getLatestTelemetry(device.getId(), "consumption", "avgConsumption"); + assertThat(result).isNotNull(); + assertThat(result.get("consumption").get(0).get("value").asText()).isEqualTo("400"); + assertThat(result.get("avgConsumption").get(0).get("value").asText()).isEqualTo("133"); + }); + + postTelemetry(device.getId(), String.format("{\"ts\": \"%s\", \"values\": {\"energy\":500}}", currentIntervalStartTs + 4500L)); + + await().alias("update telemetry that belongs to future interval -> check aggregation ") + .atMost(3 * interval, TimeUnit.MILLISECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + ObjectNode result = getLatestTelemetry(device.getId(), "consumption", "avgConsumption"); + assertThat(result).isNotNull(); + assertThat(result.get("consumption").get(0).get("value").asText()).isEqualTo("500"); + assertThat(result.get("consumption").get(0).get("ts").asLong()).isEqualTo(currentIntervalStartTs + 4000L); + assertThat(result.get("avgConsumption").get(0).get("value").asText()).isEqualTo("500"); + assertThat(result.get("avgConsumption").get(0).get("ts").asLong()).isEqualTo(currentIntervalStartTs + 4000L); + }); + } + private CalculatedField createConsumptionCF(EntityId entityId, AggInterval aggInterval, Watermark watermark) { Map arguments = new HashMap<>(); Argument argument = new Argument(); From 2ee94a616f2cb3f2658b912e40c9023a271ac8c9 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 24 Dec 2025 16:36:08 +0200 Subject: [PATCH 24/47] UI: Add cf details panel tabs refactor edit value --- .../alarm-rules/alarm-rules-table-config.ts | 2 +- .../calculated-field.component.html | 259 +++++++----------- .../calculated-field.component.scss | 36 ++- .../calculated-field.component.ts | 52 ++-- .../calculated-field.module.ts | 3 +- .../calculated-fields-table-config.ts | 28 +- .../entity-aggregation-component.component.ts | 10 +- .../geofencing-configuration.component.ts | 4 +- .../calculated-field-output.component.ts | 2 +- .../propagation-configuration.component.ts | 6 +- .../simple-configuration.component.ts | 1 - .../entity/entity-tabs.component.ts | 10 +- .../components/event/event-table-config.ts | 16 +- .../components/event/event-table.component.ts | 24 +- .../home/dialogs/events-dialog.component.html | 1 + .../home/dialogs/events-dialog.component.ts | 6 +- ...ule.ts => calculated-field-page.module.ts} | 10 +- .../calculated-fields-routing.module.ts | 4 +- .../calculated-fields-tabs.component.html | 30 ++ .../calculated-fields-tabs.component.ts | 49 ++++ .../modules/home/pages/home-pages.module.ts | 4 +- .../shared/models/calculated-field.models.ts | 6 + .../assets/locale/locale.constant-en_US.json | 1 + 23 files changed, 342 insertions(+), 222 deletions(-) rename ui-ngx/src/app/modules/home/pages/calculated-fields/{calculated-fields.module.ts => calculated-field-page.module.ts} (77%) create mode 100644 ui-ngx/src/app/modules/home/pages/calculated-fields/calculated-fields-tabs.component.html create mode 100644 ui-ngx/src/app/modules/home/pages/calculated-fields/calculated-fields-tabs.component.ts diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table-config.ts b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table-config.ts index 3d1c066839..b4082fb75c 100644 --- a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table-config.ts +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table-config.ts @@ -288,7 +288,7 @@ export class AlarmRulesTableConfig extends EntityTableConfig false + debugActionDisabled: true } }) .afterClosed() diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.html b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.html index 10ab150b69..105c1c74a0 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.html +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.html @@ -20,178 +20,125 @@ [disabled]="(isLoading$ | async)" (click)="onEntityAction($event, 'open')" [class.!hidden]="isEdit || isDetailsPage"> - {{'common.open-details-page' | translate }} + {{ 'common.open-details-page' | translate }} - - - - - - - - - - + + + + + + + + + +
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
{{ 'common.general' | translate }}
-
- - {{ 'entity-field.title' | translate }} - - @if (entityForm.get('name').errors && entityForm.get('name').touched) { - - @if (entityForm.get('name').hasError('required')) { - {{ 'common.hint.title-required' | translate }} - } @else if (entityForm.get('name').hasError('pattern')) { - {{ 'common.hint.title-pattern' | translate }} - } @else if (entityForm.get('name').hasError('maxlength')) { - {{ 'common.hint.title-max-length' | translate }} - } - - } - - - -
- - - - {{ 'common.type' | translate }} - - @for (type of fieldTypes; track type) { - {{ CalculatedFieldTypeTranslations.get(type).name | translate}} - } - - @if (CalculatedFieldTypeTranslations.get(entityForm.get('type').value).hint) { - {{ CalculatedFieldTypeTranslations.get(entityForm.get('type').value).hint | translate }} +
+
+
+
{{ 'common.general' | translate }}
+
+ + {{ 'entity-field.title' | translate }} + + @if (entityForm.get('name').errors && entityForm.get('name').touched) { + + @if (entityForm.get('name').hasError('required')) { + {{ 'common.hint.title-required' | translate }} + } @else if (entityForm.get('name').hasError('pattern')) { + {{ 'common.hint.title-pattern' | translate }} + } @else if (entityForm.get('name').hasError('maxlength')) { + {{ 'common.hint.title-max-length' | translate }} + } + } + +
- @switch (entityForm.get('type').value) { - @case (CalculatedFieldType.GEOFENCING) { - - } - @case (CalculatedFieldType.PROPAGATION) { - - } - @case (CalculatedFieldType.RELATED_ENTITIES_AGGREGATION) { - - } - @case (CalculatedFieldType.ENTITY_AGGREGATION) { - - } - @default { - + + + + {{ 'common.type' | translate }} + + @for (type of fieldTypes; track type) { + {{ CalculatedFieldTypeTranslations.get(type).name | translate }} + } + + @if (CalculatedFieldTypeTranslations.get(entityForm.get('type').value).hint) { + {{ CalculatedFieldTypeTranslations.get(entityForm.get('type').value).hint | translate }} } - } +
+ @switch (entityForm.get('type').value) { + @case (CalculatedFieldType.GEOFENCING) { + + } + @case (CalculatedFieldType.PROPAGATION) { + + } + @case (CalculatedFieldType.RELATED_ENTITIES_AGGREGATION) { + + } + @case (CalculatedFieldType.ENTITY_AGGREGATION) { + + } + @default { + + } + }
diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.scss b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.scss index 7cfc6dfef2..5bcd2552fc 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.scss +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.scss @@ -13,6 +13,40 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -:host { +:host ::ng-deep { + .tbel-script-lang-chip { + line-height: 20px; + font-size: 14px; + font-weight: 500; + color: white; + border-radius: 100px; + width: 70px; + min-width: 70px; + display: flex; + justify-content: center; + margin-top: 2px; + margin-right: 4px; + } + .tb-js-func { + .ace_tb { + &.ace_calculated-field { + &-ctx { + color: #C52F00; + } + &-args { + color: #185F2A; + } + &-key { + color: #c24c1a; + } + &-time-window, &-values, &-func, &-value, &-ts, &-latestTs { + color: #7214D0; + } + &-start-ts, &-end-ts { + color: #2CAA00; + } + } + } + } } diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.ts index 0b62d9bb96..308e350116 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.ts @@ -18,17 +18,17 @@ import { ChangeDetectorRef, Component, DestroyRef, Inject, Input } from '@angula import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { EntityComponent } from '../../components/entity/entity.component'; -import { FormBuilder, FormGroup, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; -import { AliasEntityType, EntityType } from '@shared/models/entity-type.models'; -import { NULL_UUID } from '@shared/models/id/has-uuid'; -import { ActionNotificationShow } from '@core/notification/notification.actions'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { EntityType } from '@shared/models/entity-type.models'; import { TranslateService } from '@ngx-translate/core'; -import { AssetInfo } from '@app/shared/models/asset.models'; -import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; import { CalculatedFieldConfiguration, - CalculatedFieldInfo, calculatedFieldsEntityTypeList, - CalculatedFieldType, calculatedFieldTypes, CalculatedFieldTypeTranslations, OutputStrategyType + CalculatedFieldInfo, + calculatedFieldsEntityTypeList, + CalculatedFieldType, + calculatedFieldTypes, + CalculatedFieldTypeTranslations, + OutputStrategyType } from '@shared/models/calculated-field.models'; import { oneSpaceInsideRegex } from '@shared/models/regex.constants'; import { isDefined } from '@core/utils'; @@ -36,13 +36,14 @@ import { EntityId } from '@shared/models/id/entity-id'; import { pairwise, switchMap } from 'rxjs/operators'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { BaseData } from '@shared/models/base-data'; -import { Observable, of } from 'rxjs'; +import { Observable } from 'rxjs'; import { CalculatedFieldsService } from '@core/http/calculated-fields.service'; import { getCurrentAuthUser } from '@core/auth/auth.selectors'; import { CalculatedFieldsTableConfig, CalculatedFieldsTableEntity } from '@home/components/calculated-fields/calculated-fields-table-config'; +import { TenantId } from '@shared/models/id/tenant-id'; @Component({ selector: 'tb-calculated-field', @@ -60,7 +61,7 @@ export class CalculatedFieldComponent extends EntityComponent this.entitiesTableConfig.additionalDebugActionConfig.action( + { id: this.entity.id, ...this.entityFormValue() }, false, + (expression) => { + if (expression) { + this.entityForm.get('configuration').setValue({...this.entityFormValue().configuration, expression}); + this.entityForm.get('configuration').markAsDirty(); + } + }), + }; get entityId(): EntityId { return this.entityForm.get('entityId').value; @@ -114,9 +123,11 @@ export class CalculatedFieldComponent extends EntityComponent { - if (![CalculatedFieldType.SIMPLE, CalculatedFieldType.SCRIPT].includes(prevType) || - ![CalculatedFieldType.SIMPLE, CalculatedFieldType.SCRIPT].includes(nextType)) { - form.get('configuration').setValue(({} as CalculatedFieldConfiguration), {emitEvent: false}); + if (this.isEditValue) { + if (![CalculatedFieldType.SIMPLE, CalculatedFieldType.SCRIPT].includes(prevType) || + ![CalculatedFieldType.SIMPLE, CalculatedFieldType.SCRIPT].includes(nextType)) { + form.get('configuration').setValue(({} as CalculatedFieldConfiguration), {emitEvent: false}); + } } }); return form; @@ -129,8 +140,11 @@ export class CalculatedFieldComponent extends EntityComponent this.entityForm.get('type').updateValueAndValidity({onlySelf: true})); + this.entityForm.patchValue({ type }, {emitEvent: false}); + setTimeout(() => { + this.entityForm.patchValue({ configuration, debugSettings, entityId, ...value }, {emitEvent: false}); + this.entityForm.get('type').updateValueAndValidity({onlySelf: true}); + }); if (!entityId) { this.entityForm.get('configuration').disable({emitEvent: false}); this.disabledConfiguration = true; @@ -138,7 +152,7 @@ export class CalculatedFieldComponent extends EntityComponent { - const calculatedFieldId = this.entityId?.id; + const calculatedFieldId = this.entity?.id?.id; if (calculatedFieldId) { return this.calculatedFieldsService.getLatestCalculatedFieldDebugEvent(calculatedFieldId, {ignoreLoading: true}) .pipe( diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.module.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.module.ts index 8a9241970d..2bcfcd7a1b 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.module.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.module.ts @@ -59,7 +59,7 @@ import { CalculatedFieldComponent } from '@home/components/calculated-fields/cal CalculatedFieldTestArgumentsComponent, CalculatedFieldsHeaderComponent, CalculatedFieldsFilterConfigComponent, - CalculatedFieldComponent + CalculatedFieldComponent, ], imports: [ CommonModule, @@ -74,6 +74,7 @@ import { CalculatedFieldComponent } from '@home/components/calculated-fields/cal exports: [ CalculatedFieldDialogComponent, CalculatedFieldScriptTestDialogComponent, + CalculatedFieldComponent, ] }) export class CalculatedFieldsModule {} diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table-config.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table-config.ts index e56944e00f..5841d9e511 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table-config.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table-config.ts @@ -46,6 +46,7 @@ import { CalculatedFieldsQuery, CalculatedFieldType, CalculatedFieldTypeTranslations, + debugCfActionEnabled, getCalculatedFieldArgumentsEditorCompleter, getCalculatedFieldArgumentsHighlights, PropagationWithExpression, @@ -62,7 +63,7 @@ import { EntityDebugSettingsService } from '@home/components/entity/debug/entity import { DatePipe } from '@angular/common'; import { UtilsService } from "@core/services/utils.service"; import { ActionNotificationShow } from "@core/notification/notification.actions"; -import { CalculatedFieldEventBody, DebugEventType, Event as DebugEvent, EventType } from '@shared/models/event.models'; +import { CalculatedFieldEventBody, DebugEventType, EventType } from '@shared/models/event.models'; import { EventsDialogComponent, EventsDialogData } from '@home/dialogs/events-dialog.component'; import { CalculatedFieldsHeaderComponent @@ -70,6 +71,7 @@ import { import { EntityAction } from '@home/models/entity/entity-component.models'; import { CalculatedFieldComponent } from '@home/components/calculated-fields/calculated-field.component'; import { Router } from '@angular/router'; +import { CalculatedFieldsTabsComponent } from '@home/pages/calculated-fields/calculated-fields-tabs.component'; export type CalculatedFieldsTableEntity = CalculatedField | CalculatedFieldInfo; @@ -78,7 +80,9 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig this.openDebugEventsDialog.call(this, null, calculatedField), + action: (calculatedField: CalculatedFieldsTableEntity, + openCalculatedFieldEdit = true, + afterCloseCallback?: (expression: string) => void) => this.openDebugEventsDialog.call(this, null, calculatedField, openCalculatedFieldEdit, afterCloseCallback), }; calculatedFieldFilterConfig: CalculatedFieldsQuery; @@ -104,6 +108,7 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig this.fetchCalculatedFields(pageLink); this.addEntity = this.getCalculatedFieldDialog.bind(this); + this.saveEntity = (cf) => this.calculatedFieldsService.saveCalculatedField(cf); this.loadEntity = id => this.calculatedFieldsService.getCalculatedFieldById(id.id); this.deleteEntityTitle = (field) => this.translate.instant('calculated-fields.delete-title', {title: field.name}); this.deleteEntityContent = () => this.translate.instant('calculated-fields.delete-text'); @@ -253,17 +259,11 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig void ): void { $event?.stopPropagation(); - const debugActionEnabledFn = (event: DebugEvent) => { - return (calculatedField.type === CalculatedFieldType.SCRIPT || - (calculatedField.type === CalculatedFieldType.PROPAGATION && - calculatedField.configuration.applyExpressionToResolvedArguments) - ) && !!(event as DebugEvent).body.arguments; - }; const onDebugEventSelected = (event: CalculatedFieldEventBody, dialogRef: MatDialogRef) => { - this.getTestScriptDialog(calculatedField, JSON.parse(event.arguments)) + this.getTestScriptDialog(calculatedField, JSON.parse(event.arguments), openCalculatedFieldEdit) .subscribe(expression => dialogRef.close(expression)); }; @@ -278,11 +278,15 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig { + if (afterCloseCallback) { + afterCloseCallback(value) + } + }); } private exportCalculatedField($event: Event, calculatedField: CalculatedFieldsTableEntity): void { diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/entity-aggregation-configuration/entity-aggregation-component.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/entity-aggregation-configuration/entity-aggregation-component.component.ts index e3655f5370..6f281b8e25 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/entity-aggregation-configuration/entity-aggregation-component.component.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/entity-aggregation-configuration/entity-aggregation-component.component.ts @@ -182,10 +182,12 @@ export class EntityAggregationComponentComponent implements ControlValueAccessor interval: {...value.interval, allowOffsetSec: isDefinedAndNotNull(value?.interval?.offsetSec)} } this.entityAggregationConfiguration.patchValue(data, {emitEvent: false}); - this.checkAggIntervalType(this.entityAggregationConfiguration.get('interval.type').value); - this.checkIntervalDuration(this.entityAggregationConfiguration.get('interval.allowOffsetSec').value); - this.checkWatermark(this.entityAggregationConfiguration.get('allowWatermark').value); - this.checkProduceIntermediate(); + if (this.entityAggregationConfiguration.enabled) { + this.checkAggIntervalType(this.entityAggregationConfiguration.get('interval.type').value); + this.checkIntervalDuration(this.entityAggregationConfiguration.get('interval.allowOffsetSec').value); + this.checkWatermark(this.entityAggregationConfiguration.get('allowWatermark').value); + this.checkProduceIntermediate(); + } this.updatedOffsetHint(); setTimeout(() => { this.entityAggregationConfiguration.get('arguments').updateValueAndValidity({onlySelf: true}); diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/geofencing-configuration/geofencing-configuration.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/geofencing-configuration/geofencing-configuration.component.ts index 3406922b9e..9df60b6094 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/geofencing-configuration/geofencing-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/geofencing-configuration/geofencing-configuration.component.ts @@ -124,7 +124,9 @@ export class GeofencingConfigurationComponent implements ControlValueAccessor, V writeValue(config: CalculatedFieldGeofencingConfiguration): void { this.geofencingConfiguration.patchValue(config, {emitEvent: false}); this.checkRelatedEntity(this.geofencingConfiguration.get('zoneGroups').value); - this.checkScheduledUpdateEnabled(this.geofencingConfiguration.get('scheduledUpdateEnabled').value); + if (this.geofencingConfiguration.enabled) { + this.checkScheduledUpdateEnabled(this.geofencingConfiguration.get('scheduledUpdateEnabled').value); + } } registerOnChange(fn: (config: CalculatedFieldGeofencingConfiguration) => void): void { diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/output/calculated-field-output.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/output/calculated-field-output.component.ts index c254dcd2b4..00fea861f6 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/output/calculated-field-output.component.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/output/calculated-field-output.component.ts @@ -148,7 +148,7 @@ export class CalculatedFieldOutputComponent implements ControlValueAccessor, Val for (const propName of Object.keys(changes)) { const change = changes[propName]; if (change.currentValue !== change.previousValue) { - if (propName === 'simpleMode') { + if (propName === 'simpleMode' && !this.disabled) { this.updatedFormWithMode(); if (!change.firstChange) { this.outputForm.updateValueAndValidity(); diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/propagation-configuration/propagation-configuration.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/propagation-configuration/propagation-configuration.component.ts index 1c493148b3..33acbcf86d 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/propagation-configuration/propagation-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/propagation-configuration/propagation-configuration.component.ts @@ -131,9 +131,11 @@ export class PropagationConfigurationComponent implements ControlValueAccessor, writeValue(value: PropagationWithExpression): void { value.expression = value.expression ?? calculatedFieldDefaultScript; this.propagateConfiguration.patchValue(value, {emitEvent: false}); - this.updatedFormWithScript(); + if (!this.disabled) { + this.updatedFormWithScript(); + } setTimeout(() => { - this.propagateConfiguration.get('arguments').updateValueAndValidity({onlySelf: true}); + this.propagateConfiguration.get('arguments').updateValueAndValidity({onlySelf: true, emitEvent: false}); }); } diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/simple-configuration/simple-configuration.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/simple-configuration/simple-configuration.component.ts index 2771795635..ec07fcae03 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/simple-configuration/simple-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/simple-configuration/simple-configuration.component.ts @@ -109,7 +109,6 @@ export class SimpleConfigurationComponent implements ControlValueAccessor, Valid private propagateChange: (config: SimpeConfiguration) => void = () => { }; constructor(private fb: FormBuilder) { - this.updatedFormWithScript(); this.simpleConfiguration.get('output').valueChanges.pipe( takeUntilDestroyed(), ).subscribe(() => { diff --git a/ui-ngx/src/app/modules/home/components/entity/entity-tabs.component.ts b/ui-ngx/src/app/modules/home/components/entity/entity-tabs.component.ts index 3d297f0317..066ef268e5 100644 --- a/ui-ngx/src/app/modules/home/components/entity/entity-tabs.component.ts +++ b/ui-ngx/src/app/modules/home/components/entity/entity-tabs.component.ts @@ -16,7 +16,7 @@ import { BaseData, HasId } from '@shared/models/base-data'; import { PageComponent } from '@shared/components/page.component'; -import { AfterViewInit, Directive, Input, OnInit, QueryList, ViewChildren } from '@angular/core'; +import { AfterViewInit, Directive, inject, Input, OnInit, QueryList, ViewChildren } from '@angular/core'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; @@ -100,9 +100,11 @@ export abstract class EntityTabsComponent, entityTabsChanged = this.entityTabsSubject.asObservable(); - protected constructor(protected store: Store) { - super(store); - this.authUser = getCurrentAuthUser(store); + protected store: Store = inject(Store); + + protected constructor(...args: unknown[]) { + super(); + this.authUser = getCurrentAuthUser(this.store); } ngOnInit() { diff --git a/ui-ngx/src/app/modules/home/components/event/event-table-config.ts b/ui-ngx/src/app/modules/home/components/event/event-table-config.ts index 03fc641735..accc3d7060 100644 --- a/ui-ngx/src/app/modules/home/components/event/event-table-config.ts +++ b/ui-ngx/src/app/modules/home/components/event/event-table-config.ts @@ -22,7 +22,14 @@ import { EntityTableColumn, EntityTableConfig } from '@home/models/entity/entities-table-config.models'; -import { DebugEventType, Event, EventBody, EventType, FilterEventBody } from '@shared/models/event.models'; +import { + DebugEventType, + Event as DebugEvent, + Event, + EventBody, + EventType, + FilterEventBody +} from '@shared/models/event.models'; import { TimePageLink } from '@shared/models/page/page-link'; import { TranslateService } from '@ngx-translate/core'; import { DatePipe } from '@angular/common'; @@ -95,7 +102,8 @@ export class EventTableConfig extends EntityTableConfig { private store: Store, public testButtonLabel?: string, private debugEventSelected?: EventEmitter, - public hideClearEventAction = false) { + public hideClearEventAction = false, + public disableDebugEventAction = false) { super(); this.loadDataOnInit = false; this.tableTitle = ''; @@ -453,7 +461,7 @@ export class EventTableConfig extends EntityTableConfig { this.cellActionDescriptors.push({ name: this.translate.instant('rulenode.test-with-this-message', {test: this.translate.instant(this.testButtonLabel)}), icon: 'bug_report', - isEnabled: (entity) => entity.body.type === 'IN' || entity.body.error !== undefined, + isEnabled: (entity) => (entity.body.type === 'IN' || entity.body.error !== undefined) && !this.disableDebugEventAction, onAction: ($event, entity) => { this.debugEventSelected.next(entity.body); } @@ -464,7 +472,7 @@ export class EventTableConfig extends EntityTableConfig { this.cellActionDescriptors.push({ name: this.translate.instant('calculated-fields.test-with-this-message'), icon: 'bug_report', - isEnabled: () => true, + isEnabled: (event) => !this.disableDebugEventAction && !!(event as DebugEvent).body.arguments, onAction: (_, entity) => this.debugEventSelected.next(entity.body) }); break; diff --git a/ui-ngx/src/app/modules/home/components/event/event-table.component.ts b/ui-ngx/src/app/modules/home/components/event/event-table.component.ts index 53cb8e1b96..7011c23449 100644 --- a/ui-ngx/src/app/modules/home/components/event/event-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/event/event-table.component.ts @@ -17,10 +17,12 @@ import { AfterViewInit, ChangeDetectorRef, - Component, EventEmitter, + Component, + EventEmitter, Input, OnDestroy, - OnInit, Output, + OnInit, + Output, ViewChild, ViewContainerRef } from '@angular/core'; @@ -61,6 +63,21 @@ export class EventTableComponent implements OnInit, AfterViewInit, OnDestroy { @Input() hideClearEventAction: boolean = false; + private disableDebugEventActionValue = false; + + get disableDebugEventAction() { + return this.disableDebugEventActionValue; + } + + @Input() + set disableDebugEventAction(value) { + this.disableDebugEventActionValue = value; + if (this.eventTableConfig) { + this.eventTableConfig.disableDebugEventAction = this.disableDebugEventAction; + this.eventTableConfig.updateCellAction(); + } + }; + activeValue = false; dirtyValue = false; entityIdValue: EntityId; @@ -151,7 +168,8 @@ export class EventTableComponent implements OnInit, AfterViewInit, OnDestroy { this.store, this.functionTestButtonLabel, this.debugEventSelected, - this.hideClearEventAction + this.hideClearEventAction, + this.disableDebugEventAction ); } diff --git a/ui-ngx/src/app/modules/home/dialogs/events-dialog.component.html b/ui-ngx/src/app/modules/home/dialogs/events-dialog.component.html index 655c25545d..ab48435b2d 100644 --- a/ui-ngx/src/app/modules/home/dialogs/events-dialog.component.html +++ b/ui-ngx/src/app/modules/home/dialogs/events-dialog.component.html @@ -34,6 +34,7 @@ [tenantId]="data.tenantId" [entityId]="data.entityId" [functionTestButtonLabel]="data.functionTestButtonLabel ?? ''" + [disableDebugEventAction]="data.debugActionDisabled" (debugEventSelected)="this.onDebugEventSelected($event)" />
diff --git a/ui-ngx/src/app/modules/home/dialogs/events-dialog.component.ts b/ui-ngx/src/app/modules/home/dialogs/events-dialog.component.ts index d5339de3b2..8008e74523 100644 --- a/ui-ngx/src/app/modules/home/dialogs/events-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/dialogs/events-dialog.component.ts @@ -23,7 +23,6 @@ import { AppState } from '@core/core.state'; import { Router } from '@angular/router'; import { DebugEventType, EventType } from '@shared/models/event.models'; import { EntityId } from '@shared/models/id/entity-id'; -import { BaseData, HasId } from '@shared/models/base-data'; export interface EventsDialogData { title: string; @@ -34,7 +33,7 @@ export interface EventsDialogData { disabledEventTypes?: Array; functionTestButtonLabel?: string; onDebugEventSelected?: (event: any, dialogRef: MatDialogRef) => void; - debugActionEnabledFn?: (event: BaseData) => boolean; + debugActionDisabled?: boolean; } @Component({ @@ -54,9 +53,6 @@ export class EventsDialogComponent extends DialogComponent 0) { - this.eventsTable.entitiesTable.cellActionDescriptors[0].isEnabled = this.data.debugActionEnabledFn; - } this.eventsTable.entitiesTable.updateData(); } diff --git a/ui-ngx/src/app/modules/home/pages/calculated-fields/calculated-fields.module.ts b/ui-ngx/src/app/modules/home/pages/calculated-fields/calculated-field-page.module.ts similarity index 77% rename from ui-ngx/src/app/modules/home/pages/calculated-fields/calculated-fields.module.ts rename to ui-ngx/src/app/modules/home/pages/calculated-fields/calculated-field-page.module.ts index 01ed3577e8..f0672c2e5e 100644 --- a/ui-ngx/src/app/modules/home/pages/calculated-fields/calculated-fields.module.ts +++ b/ui-ngx/src/app/modules/home/pages/calculated-fields/calculated-field-page.module.ts @@ -20,15 +20,19 @@ import { SharedModule } from '@shared/shared.module'; import { HomeDialogsModule } from '../../dialogs/home-dialogs.module'; import { HomeComponentsModule } from '@modules/home/components/home-components.module'; import { CalculatedFieldsRoutingModule } from '@home/pages/calculated-fields/calculated-fields-routing.module'; +import { CalculatedFieldsModule } from '@home/components/calculated-fields/calculated-field.module'; +import { CalculatedFieldsTabsComponent } from '@home/pages/calculated-fields/calculated-fields-tabs.component'; @NgModule({ - declarations: [], + declarations: [ + CalculatedFieldsTabsComponent + ], imports: [ CommonModule, SharedModule, HomeComponentsModule, HomeDialogsModule, - CalculatedFieldsRoutingModule + CalculatedFieldsRoutingModule, ] }) -export class CalculatedFieldsModule { } \ No newline at end of file +export class CalculatedFieldPageModule { } \ No newline at end of file diff --git a/ui-ngx/src/app/modules/home/pages/calculated-fields/calculated-fields-routing.module.ts b/ui-ngx/src/app/modules/home/pages/calculated-fields/calculated-fields-routing.module.ts index 03be3d86f6..e703445155 100644 --- a/ui-ngx/src/app/modules/home/pages/calculated-fields/calculated-fields-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/calculated-fields/calculated-fields-routing.module.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { DestroyRef, inject, NgModule, Renderer2 } from '@angular/core'; +import { DestroyRef, inject, NgModule } from '@angular/core'; import { ActivatedRouteSnapshot, ResolveFn, Router, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { Authority } from '@shared/models/authority.enum'; import { MenuId } from '@core/services/menu.models'; @@ -63,7 +63,7 @@ export const CalculatedFieldsTableConfigResolver: ResolveFn +@if (entity) { + + + +} diff --git a/ui-ngx/src/app/modules/home/pages/calculated-fields/calculated-fields-tabs.component.ts b/ui-ngx/src/app/modules/home/pages/calculated-fields/calculated-fields-tabs.component.ts new file mode 100644 index 0000000000..0c41ddc33d --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/calculated-fields/calculated-fields-tabs.component.ts @@ -0,0 +1,49 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component } from '@angular/core'; +import { EntityTabsComponent } from '../../components/entity/entity-tabs.component'; +import { CalculatedFieldEventBody, DebugEventType, EventType } from '@shared/models/event.models'; +import type { + CalculatedFieldsTableConfig, + CalculatedFieldsTableEntity +} from '@home/components/calculated-fields/calculated-fields-table-config'; +import { debugCfActionEnabled } from '@shared/models/calculated-field.models'; + +@Component({ + selector: 'tb-calculated-fields-tabs', + templateUrl: './calculated-fields-tabs.component.html', + styleUrls: [] +}) +export class CalculatedFieldsTabsComponent extends EntityTabsComponent { + + readonly DebugEventType = DebugEventType; + readonly EventType = EventType; + + constructor() { + super(); + } + + get debugActionDisabled(): boolean { + return !debugCfActionEnabled(this.entity); + }; + + onDebugEventSelected(event: CalculatedFieldEventBody) { + (this.entitiesTableConfig as CalculatedFieldsTableConfig).getTestScriptDialog(this.entity, JSON.parse(event.arguments)) + .subscribe((expression) => { + }); + }; +} diff --git a/ui-ngx/src/app/modules/home/pages/home-pages.module.ts b/ui-ngx/src/app/modules/home/pages/home-pages.module.ts index c43b89d141..d84b646525 100644 --- a/ui-ngx/src/app/modules/home/pages/home-pages.module.ts +++ b/ui-ngx/src/app/modules/home/pages/home-pages.module.ts @@ -27,7 +27,7 @@ import { UserModule } from '@modules/home/pages/user/user.module'; import { DeviceModule } from '@modules/home/pages/device/device.module'; import { AssetModule } from '@modules/home/pages/asset/asset.module'; import { EntityViewModule } from '@modules/home/pages/entity-view/entity-view.module'; -import { CalculatedFieldsModule } from '@home/pages/calculated-fields/calculated-fields.module'; +import { CalculatedFieldPageModule } from '@home/pages/calculated-fields/calculated-field-page.module'; import { RuleChainModule } from '@modules/home/pages/rulechain/rulechain.module'; import { WidgetLibraryModule } from '@modules/home/pages/widget/widget-library.module'; import { DashboardModule } from '@modules/home/pages/dashboard/dashboard.module'; @@ -70,7 +70,7 @@ import { AiModelModule } from '@home/pages/ai-model/ai-model.module'; EdgeModule, EntityViewModule, CustomerModule, - CalculatedFieldsModule, + CalculatedFieldPageModule, RuleChainModule, WidgetLibraryModule, DashboardModule, diff --git a/ui-ngx/src/app/shared/models/calculated-field.models.ts b/ui-ngx/src/app/shared/models/calculated-field.models.ts index 825c08a69f..30f5a27686 100644 --- a/ui-ngx/src/app/shared/models/calculated-field.models.ts +++ b/ui-ngx/src/app/shared/models/calculated-field.models.ts @@ -540,6 +540,12 @@ export const getCalculatedFieldCurrentEntityFilter = (entityName: string, entity } } +export const debugCfActionEnabled = (cf: CalculatedField) => { + return (cf.type === CalculatedFieldType.SCRIPT || + (cf.type === CalculatedFieldType.PROPAGATION && cf.configuration.applyExpressionToResolvedArguments) + ); +} + export interface CalculatedFieldArgumentValueBase { argumentName: string; type: ArgumentType; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 73a8bbf23f..8c2ecc2890 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -1221,6 +1221,7 @@ "calculated-field-filter-title": "Calculated field filter", "filter-title": "Filter", "calculated-field-types": "Calculated field types", + "events": "Events", "any-type": "Any type", "metrics": { "metrics": "Metrics", From c50b24d014bead4905627a7e2e6269020e97a73c Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 24 Dec 2025 19:12:06 +0200 Subject: [PATCH 25/47] UI: Extract same method in cf for service --- .../calculated-field-form.service.ts | 94 +++++++++++++++++++ .../calculated-field.component.ts | 74 ++++----------- .../calculated-field-dialog.component.html | 30 ++---- .../calculated-field-dialog.component.ts | 78 ++++----------- .../entity/entity-table-component.models.ts | 9 +- .../calculated-fields-tabs.component.ts | 10 +- .../entity/entity-select.component.ts | 30 ++++-- 7 files changed, 179 insertions(+), 146 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field-form.service.ts diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field-form.service.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field-form.service.ts new file mode 100644 index 0000000000..6ef02f0cf8 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field-form.service.ts @@ -0,0 +1,94 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { DestroyRef, inject, Injectable } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { pairwise, switchMap } from 'rxjs/operators'; +import { Observable } from 'rxjs'; +import { + CalculatedField, + CalculatedFieldConfiguration, + CalculatedFieldEventArguments, + CalculatedFieldType, + OutputStrategyType +} from '@shared/models/calculated-field.models'; +import { oneSpaceInsideRegex } from '@shared/models/regex.constants'; +import { isDefined } from '@core/utils'; +import { CalculatedFieldsService } from '@core/http/calculated-fields.service'; +import { CalculatedFieldsTableEntity } from '@home/components/calculated-fields/calculated-fields-table-config'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { EntityType } from '@shared/models/entity-type.models'; + +@Injectable({ providedIn: 'root' }) +export class CalculatedFieldFormService { + private fb = inject(FormBuilder); + private calculatedFieldsService = inject(CalculatedFieldsService); + + buildForm(): FormGroup { + return this.fb.group({ + name: ['', [Validators.required, Validators.pattern(oneSpaceInsideRegex), Validators.maxLength(255)]], + entityId: [{type: EntityType.DEVICE_PROFILE, id: null}, Validators.required], + type: [CalculatedFieldType.SIMPLE], + debugSettings: [], + configuration: this.fb.control({} as CalculatedFieldConfiguration), + }); + } + + setupTypeChange(form: FormGroup, destroyRef: DestroyRef, isEditActive?: () => boolean): void { + form.get('type').valueChanges.pipe( + pairwise(), + takeUntilDestroyed(destroyRef) + ).subscribe(([prevType, nextType]) => { + const shouldCheck = isEditActive ? isEditActive() : true; + if (shouldCheck) { + if (![CalculatedFieldType.SIMPLE, CalculatedFieldType.SCRIPT].includes(prevType) || + ![CalculatedFieldType.SIMPLE, CalculatedFieldType.SCRIPT].includes(nextType)) { + form.get('configuration').setValue({} as CalculatedFieldConfiguration, { emitEvent: false }); + } + } + }); + } + + prepareConfig(configuration: CalculatedFieldConfiguration): CalculatedFieldConfiguration { + const config = configuration || ({} as CalculatedFieldConfiguration); + if (config.type !== CalculatedFieldType.ALARM) { + if (isDefined(config?.output) && !config?.output?.strategy) { + config.output.strategy = { type: OutputStrategyType.RULE_CHAIN }; + } + } + return config; + } + + testScript( + calculatedFieldId: string, + formValue: CalculatedField, + testDialogFn: (calculatedField: CalculatedFieldsTableEntity, argumentsObj?: CalculatedFieldEventArguments, openCalculatedFieldEdit?: boolean, expression?: string) => Observable, + destroyRef: DestroyRef, + expression?: string, + ): Observable { + if (calculatedFieldId) { + return this.calculatedFieldsService.getLatestCalculatedFieldDebugEvent(calculatedFieldId, {ignoreLoading: true}) + .pipe( + switchMap(event => { + const args = event?.arguments ? JSON.parse(event.arguments) : null; + return testDialogFn(formValue, args, false, expression); + }), + takeUntilDestroyed(destroyRef) + ); + } + return testDialogFn(formValue, null, false, expression); + } +} \ No newline at end of file diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.ts index 308e350116..01d7a8de7a 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.ts @@ -14,11 +14,11 @@ /// limitations under the License. /// -import { ChangeDetectorRef, Component, DestroyRef, Inject, Input } from '@angular/core'; +import { ChangeDetectorRef, Component, DestroyRef, inject, Inject, Input } from '@angular/core'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { EntityComponent } from '../../components/entity/entity.component'; -import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { FormBuilder, FormGroup } from '@angular/forms'; import { EntityType } from '@shared/models/entity-type.models'; import { TranslateService } from '@ngx-translate/core'; import { @@ -27,23 +27,18 @@ import { calculatedFieldsEntityTypeList, CalculatedFieldType, calculatedFieldTypes, - CalculatedFieldTypeTranslations, - OutputStrategyType + CalculatedFieldTypeTranslations } from '@shared/models/calculated-field.models'; -import { oneSpaceInsideRegex } from '@shared/models/regex.constants'; -import { isDefined } from '@core/utils'; import { EntityId } from '@shared/models/id/entity-id'; -import { pairwise, switchMap } from 'rxjs/operators'; -import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { BaseData } from '@shared/models/base-data'; import { Observable } from 'rxjs'; -import { CalculatedFieldsService } from '@core/http/calculated-fields.service'; import { getCurrentAuthUser } from '@core/auth/auth.selectors'; import { CalculatedFieldsTableConfig, CalculatedFieldsTableEntity } from '@home/components/calculated-fields/calculated-fields-table-config'; import { TenantId } from '@shared/models/id/tenant-id'; +import { CalculatedFieldFormService } from '@home/components/calculated-fields/calculated-field-form.service'; @Component({ selector: 'tb-calculated-field', @@ -68,14 +63,15 @@ export class CalculatedFieldComponent extends EntityComponent, protected translate: TranslateService, @Inject('entity') protected entityValue: CalculatedFieldInfo, @Inject('entitiesTableConfig') protected entitiesTableConfigValue: CalculatedFieldsTableConfig, protected fb: FormBuilder, - protected cd: ChangeDetectorRef, - private destroyRef: DestroyRef, - private calculatedFieldsService: CalculatedFieldsService) { + protected cd: ChangeDetectorRef) { super(store, fb, entityValue, entitiesTableConfigValue, cd); } @@ -111,60 +107,30 @@ export class CalculatedFieldComponent extends EntityComponent({} as CalculatedFieldConfiguration), - }); - form.get('type').valueChanges.pipe( - pairwise(), - takeUntilDestroyed(this.destroyRef) - ).subscribe(([prevType, nextType]) => { - if (this.isEditValue) { - if (![CalculatedFieldType.SIMPLE, CalculatedFieldType.SCRIPT].includes(prevType) || - ![CalculatedFieldType.SIMPLE, CalculatedFieldType.SCRIPT].includes(nextType)) { - form.get('configuration').setValue(({} as CalculatedFieldConfiguration), {emitEvent: false}); - } - } - }); + buildForm(_entity?: CalculatedFieldInfo): FormGroup { + const form = inject(CalculatedFieldFormService).buildForm(); + inject(CalculatedFieldFormService).setupTypeChange(form, inject(DestroyRef), () => this.isEditValue); return form; } updateForm(entity: CalculatedFieldInfo) { const { configuration = {} as CalculatedFieldConfiguration, type = CalculatedFieldType.SIMPLE, debugSettings = { failuresEnabled: true, allEnabled: true }, entityId = this.entityId, ...value } = entity ?? {}; - if (configuration.type !== CalculatedFieldType.ALARM) { - if (isDefined(configuration?.output) && !configuration?.output?.strategy) { - configuration.output.strategy = {type: OutputStrategyType.RULE_CHAIN}; - } - } + const preparedConfig = this.cfFormService.prepareConfig(configuration); this.entityForm.patchValue({ type }, {emitEvent: false}); setTimeout(() => { - this.entityForm.patchValue({ configuration, debugSettings, entityId, ...value }, {emitEvent: false}); + this.entityForm.patchValue({ configuration: preparedConfig, debugSettings, entityId, ...value }, {emitEvent: false}); this.entityForm.get('type').updateValueAndValidity({onlySelf: true}); }); - if (!entityId) { - this.entityForm.get('configuration').disable({emitEvent: false}); - this.disabledConfiguration = true; - } } onTestScript(expression?: string): Observable { - const calculatedFieldId = this.entity?.id?.id; - if (calculatedFieldId) { - return this.calculatedFieldsService.getLatestCalculatedFieldDebugEvent(calculatedFieldId, {ignoreLoading: true}) - .pipe( - switchMap(event => { - const args = event?.arguments ? JSON.parse(event.arguments) : null; - return this.entitiesTableConfig.getTestScriptDialog(this.entityFormValue(), args, false, expression); - }), - takeUntilDestroyed(this.destroyRef) - ) - } - - return this.entitiesTableConfig.getTestScriptDialog(this.entityFormValue(), null, false, expression); + return this.cfFormService.testScript( + this.entity?.id?.id, + this.entityFormValue(), + this.entitiesTableConfig.getTestScriptDialog.bind(this.entitiesTableConfig), + this.destroyRef, + expression + ); } updateFormState() { diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.html b/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.html index 3d669b02c6..24dd114963 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.html @@ -56,29 +56,13 @@ />
@if (!data.entityId) { -
- - - @if (fieldFormGroup.get('entityId.entityType').value) { - - } -
+ + } {{ 'common.type' | translate }} diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.ts index 4d2df6e416..3370e65f5b 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.ts @@ -14,11 +14,10 @@ /// limitations under the License. /// -import { Component, DestroyRef, Inject, ViewChild, ViewEncapsulation } from '@angular/core'; +import { Component, DestroyRef, Inject, ViewEncapsulation } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; -import { FormBuilder, Validators } from '@angular/forms'; import { Router } from '@angular/router'; import { DialogComponent } from '@shared/components/dialog.component'; import { @@ -28,21 +27,18 @@ import { CalculatedFieldTestScriptFn, CalculatedFieldType, calculatedFieldTypes, - CalculatedFieldTypeTranslations, - OutputStrategyType + CalculatedFieldTypeTranslations } from '@shared/models/calculated-field.models'; -import { oneSpaceInsideRegex } from '@shared/models/regex.constants'; -import { AliasEntityType, EntityType } from '@shared/models/entity-type.models'; -import { pairwise, switchMap } from 'rxjs/operators'; +import { EntityType } from '@shared/models/entity-type.models'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { CalculatedFieldsService } from '@core/http/calculated-fields.service'; import { Observable } from 'rxjs'; import { EntityId } from '@shared/models/id/entity-id'; import { AdditionalDebugActionConfig } from '@home/components/entity/debug/entity-debug-settings.model'; -import { deepTrim, isDefined } from '@core/utils'; -import { EntityTypeSelectComponent } from '@shared/components/entity/entity-type-select.component'; -import { EntityAutocompleteComponent } from '@shared/components/entity/entity-autocomplete.component'; +import { deepTrim } from '@core/utils'; import { BaseData } from '@shared/models/base-data'; +import { CalculatedFieldFormService } from '@home/components/calculated-fields/calculated-field-form.service'; +import { FormGroup } from '@angular/forms'; export interface CalculatedFieldDialogData { value?: CalculatedField; @@ -64,16 +60,7 @@ export interface CalculatedFieldDialogData { }) export class CalculatedFieldDialogComponent extends DialogComponent { - fieldFormGroup = this.fb.group({ - name: ['', [Validators.required, Validators.pattern(oneSpaceInsideRegex), Validators.maxLength(255)]], - entityId: this.fb.group({ - entityType: this.fb.control(EntityType.DEVICE_PROFILE, Validators.required), - id: [null as null | string, Validators.required], - }), - type: [CalculatedFieldType.SIMPLE], - debugSettings: [], - configuration: this.fb.control({} as CalculatedFieldConfiguration), - }); + fieldFormGroup: FormGroup; additionalDebugActionConfig = this.data.value?.id ? { ...this.data.additionalDebugActionConfig, @@ -91,18 +78,16 @@ export class CalculatedFieldDialogComponent extends DialogComponent, protected router: Router, @Inject(MAT_DIALOG_DATA) public data: CalculatedFieldDialogData, protected dialogRef: MatDialogRef, private calculatedFieldsService: CalculatedFieldsService, private destroyRef: DestroyRef, - private fb: FormBuilder) { + private cfFormService: CalculatedFieldFormService) { super(store, router, dialogRef); - this.observeType(); + this.fieldFormGroup = this.cfFormService.buildForm(); + this.cfFormService.setupTypeChange(this.fieldFormGroup, this.destroyRef); this.applyDialogData(); if (this.data.isDirty) { @@ -110,7 +95,7 @@ export class CalculatedFieldDialogComponent extends DialogComponent { this.disabledConfiguration = !entityId; @@ -142,24 +127,17 @@ export class CalculatedFieldDialogComponent extends DialogComponent { - const calculatedFieldId = this.data.value?.id?.id; - if (calculatedFieldId) { - return this.calculatedFieldsService.getLatestCalculatedFieldDebugEvent(calculatedFieldId, {ignoreLoading: true}) - .pipe( - switchMap(event => { - const args = event?.arguments ? JSON.parse(event.arguments) : null; - return this.data.getTestScriptDialogFn(this.fromGroupValue, args, false, expression); - }), - takeUntilDestroyed(this.destroyRef) - ) - } - return this.data.getTestScriptDialogFn(this.fromGroupValue, null, false, expression); + return this.cfFormService.testScript( + this.data.value?.id?.id, + this.fromGroupValue, + this.data.getTestScriptDialogFn, + this.destroyRef, + expression + ); } changeEntity(entity: BaseData): void { @@ -172,28 +150,12 @@ export class CalculatedFieldDialogComponent extends DialogComponent this.fieldFormGroup.get('type').updateValueAndValidity({onlySelf: true})); if (!this.data.entityId) { this.fieldFormGroup.get('configuration').disable({emitEvent: false}); this.disabledConfiguration = true; } } - - private observeType(): void { - this.fieldFormGroup.get('type').valueChanges.pipe( - pairwise(), - takeUntilDestroyed(this.destroyRef) - ).subscribe(([prevType, nextType]) => { - if (![CalculatedFieldType.SIMPLE, CalculatedFieldType.SCRIPT].includes(prevType) || - ![CalculatedFieldType.SIMPLE, CalculatedFieldType.SCRIPT].includes(nextType)) { - this.fieldFormGroup.get('configuration').setValue(({} as CalculatedFieldConfiguration), {emitEvent: false}); - } - }); - } } diff --git a/ui-ngx/src/app/modules/home/models/entity/entity-table-component.models.ts b/ui-ngx/src/app/modules/home/models/entity/entity-table-component.models.ts index e3d0bb19e6..9e2d22f8a9 100644 --- a/ui-ngx/src/app/modules/home/models/entity/entity-table-component.models.ts +++ b/ui-ngx/src/app/modules/home/models/entity/entity-table-component.models.ts @@ -20,18 +20,22 @@ import { SafeHtml } from '@angular/platform-browser'; import { PageLink } from '@shared/models/page/page-link'; import { Timewindow } from '@shared/models/time/time.models'; import { EntitiesDataSource } from '@home/models/datasource/entity-datasource'; -import { ElementRef, EventEmitter, Renderer2, ViewContainerRef } from '@angular/core'; +import { ElementRef, EventEmitter, ViewContainerRef } from '@angular/core'; import { TbAnchorComponent } from '@shared/components/tb-anchor.component'; import { MatPaginator } from '@angular/material/paginator'; import { MatSort } from '@angular/material/sort'; import { EntityAction } from '@home/models/entity/entity-component.models'; import { - CellActionDescriptor, EntityActionTableColumn, EntityColumn, EntityTableColumn, + CellActionDescriptor, + EntityActionTableColumn, + EntityColumn, + EntityTableColumn, EntityTableConfig, GroupActionDescriptor, HeaderActionDescriptor } from '@home/models/entity/entities-table-config.models'; import { ActivatedRoute } from '@angular/router'; +import type { EntityDetailsPanelComponent } from '@home/components/entity/entity-details-panel.component'; export type EntitiesTableAction = 'add'; @@ -64,6 +68,7 @@ export interface IEntitiesTableComponent { paginator: MatPaginator; sort: MatSort; route: ActivatedRoute; + entityDetailsPanel: EntityDetailsPanelComponent; viewContainerRef: ViewContainerRef; addEnabled(): boolean; diff --git a/ui-ngx/src/app/modules/home/pages/calculated-fields/calculated-fields-tabs.component.ts b/ui-ngx/src/app/modules/home/pages/calculated-fields/calculated-fields-tabs.component.ts index 0c41ddc33d..54e26db1be 100644 --- a/ui-ngx/src/app/modules/home/pages/calculated-fields/calculated-fields-tabs.component.ts +++ b/ui-ngx/src/app/modules/home/pages/calculated-fields/calculated-fields-tabs.component.ts @@ -42,8 +42,16 @@ export class CalculatedFieldsTabsComponent extends EntityTabsComponent { + (this.entitiesTableConfig as CalculatedFieldsTableConfig).getTable(); + const entityDetailsPanel = this.entitiesTableConfig.getTable().entityDetailsPanel; + entityDetailsPanel.onToggleEditMode(true); + entityDetailsPanel.selectedTab = 0; + setTimeout(() => { + entityDetailsPanel.detailsForm.get('configuration').setValue({...this.entity.configuration, expression}); + entityDetailsPanel.detailsForm.get('configuration').markAsDirty(); + }); }); }; } diff --git a/ui-ngx/src/app/shared/components/entity/entity-select.component.ts b/ui-ngx/src/app/shared/components/entity/entity-select.component.ts index fbfb7ae453..57d2380705 100644 --- a/ui-ngx/src/app/shared/components/entity/entity-select.component.ts +++ b/ui-ngx/src/app/shared/components/entity/entity-select.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { AfterViewInit, Component, DestroyRef, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core'; +import { Component, DestroyRef, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; @@ -38,7 +38,7 @@ import { BaseData } from '@shared/models/base-data'; multi: true }] }) -export class EntitySelectComponent implements ControlValueAccessor, OnInit, AfterViewInit { +export class EntitySelectComponent implements ControlValueAccessor, OnInit { entitySelectFormGroup: UntypedFormGroup; @@ -70,6 +70,9 @@ export class EntitySelectComponent implements ControlValueAccessor, OnInit, Afte @Input() filterAllowedEntityTypes: boolean; + @Input() + defaultEntityType: AliasEntityType | EntityType; + @Output() entityChanged = new EventEmitter>(); @@ -81,8 +84,6 @@ export class EntitySelectComponent implements ControlValueAccessor, OnInit, Afte AliasEntityType.CURRENT_TENANT, AliasEntityType.CURRENT_USER, AliasEntityType.CURRENT_USER_OWNER ]); - private readonly defaultEntityType: EntityType | AliasEntityType = null; - private propagateChange = (v: any) => { }; constructor(private store: Store, @@ -93,15 +94,18 @@ export class EntitySelectComponent implements ControlValueAccessor, OnInit, Afte const entityTypes = this.entityService.prepareAllowedEntityTypesList(this.allowedEntityTypes, this.useAliasEntityTypes); + + let defaultEntityType: EntityType | AliasEntityType = null + if (entityTypes.length === 1) { this.displayEntityTypeSelect = false; - this.defaultEntityType = entityTypes[0]; + defaultEntityType = entityTypes[0]; } else { this.displayEntityTypeSelect = true; } this.entitySelectFormGroup = this.fb.group({ - entityType: [this.defaultEntityType], + entityType: [defaultEntityType], entityId: [null] }); } @@ -133,9 +137,19 @@ export class EntitySelectComponent implements ControlValueAccessor, OnInit, Afte if (additionNullUIIDEntityTypes.length > 0) { additionNullUIIDEntityTypes.forEach((entityType) => this.entityTypeNullUUID.add(entityType)); } - } - ngAfterViewInit(): void { + if (this.filterAllowedEntityTypes === false) { + if (this.allowedEntityTypes?.length === 1) { + this.displayEntityTypeSelect = false; + this.entitySelectFormGroup.get('entityType').setValue(this.allowedEntityTypes[0]) + } else { + this.displayEntityTypeSelect = true; + } + } + + if (this.defaultEntityType) { + this.entitySelectFormGroup.get('entityType').setValue(this.defaultEntityType); + } } setDisabledState(isDisabled: boolean): void { From f2dadd07914d6c327756160f0c665fce8597a786 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Thu, 25 Dec 2025 11:52:53 +0200 Subject: [PATCH 26/47] Version set to 4.2.1.2-SNAPSHOT --- application/pom.xml | 2 +- .../service/install/DefaultDatabaseSchemaSettingsService.java | 2 +- common/actor/pom.xml | 2 +- common/cache/pom.xml | 2 +- common/cluster-api/pom.xml | 2 +- common/coap-server/pom.xml | 2 +- common/dao-api/pom.xml | 2 +- common/data/pom.xml | 2 +- common/discovery-api/pom.xml | 2 +- common/edge-api/pom.xml | 2 +- common/edqs/pom.xml | 2 +- common/message/pom.xml | 2 +- common/pom.xml | 2 +- common/proto/pom.xml | 2 +- common/queue/pom.xml | 2 +- common/script/pom.xml | 2 +- common/script/remote-js-client/pom.xml | 2 +- common/script/script-api/pom.xml | 2 +- common/stats/pom.xml | 2 +- common/transport/coap/pom.xml | 2 +- common/transport/http/pom.xml | 2 +- common/transport/lwm2m/pom.xml | 2 +- common/transport/mqtt/pom.xml | 2 +- common/transport/pom.xml | 2 +- common/transport/snmp/pom.xml | 2 +- common/transport/transport-api/pom.xml | 2 +- common/util/pom.xml | 2 +- common/version-control/pom.xml | 2 +- dao/pom.xml | 2 +- edqs/pom.xml | 2 +- monitoring/pom.xml | 2 +- msa/black-box-tests/pom.xml | 2 +- msa/edqs/pom.xml | 2 +- msa/js-executor/package.json | 2 +- msa/js-executor/pom.xml | 2 +- msa/monitoring/pom.xml | 2 +- msa/pom.xml | 2 +- msa/tb-node/pom.xml | 2 +- msa/tb/pom.xml | 2 +- msa/transport/coap/pom.xml | 2 +- msa/transport/http/pom.xml | 2 +- msa/transport/lwm2m/pom.xml | 2 +- msa/transport/mqtt/pom.xml | 2 +- msa/transport/pom.xml | 2 +- msa/transport/snmp/pom.xml | 2 +- msa/vc-executor-docker/pom.xml | 2 +- msa/vc-executor/pom.xml | 2 +- msa/web-ui/package.json | 2 +- msa/web-ui/pom.xml | 2 +- netty-mqtt/pom.xml | 4 ++-- pom.xml | 2 +- rest-client/pom.xml | 2 +- rule-engine/pom.xml | 2 +- rule-engine/rule-engine-api/pom.xml | 2 +- rule-engine/rule-engine-components/pom.xml | 2 +- tools/pom.xml | 2 +- transport/coap/pom.xml | 2 +- transport/http/pom.xml | 2 +- transport/lwm2m/pom.xml | 2 +- transport/mqtt/pom.xml | 2 +- transport/pom.xml | 2 +- transport/snmp/pom.xml | 2 +- ui-ngx/package.json | 2 +- ui-ngx/pom.xml | 2 +- 64 files changed, 65 insertions(+), 65 deletions(-) diff --git a/application/pom.xml b/application/pom.xml index 47772dfa97..9f3b79b3f5 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT thingsboard application diff --git a/application/src/main/java/org/thingsboard/server/service/install/DefaultDatabaseSchemaSettingsService.java b/application/src/main/java/org/thingsboard/server/service/install/DefaultDatabaseSchemaSettingsService.java index 44aa14b782..500ad60df0 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/DefaultDatabaseSchemaSettingsService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/DefaultDatabaseSchemaSettingsService.java @@ -30,7 +30,7 @@ public class DefaultDatabaseSchemaSettingsService implements DatabaseSchemaSetti // This list should include all versions that are compatible for the upgrade in 4 digits format (like 4.2.0.0, etc.). // The compatibility cycle usually breaks when we have some scripts written in Java that may not work after a new release. - private static final List SUPPORTED_VERSIONS_FOR_UPGRADE = List.of("4.2.0.0", "4.2.1.0"); + private static final List SUPPORTED_VERSIONS_FOR_UPGRADE = List.of("4.2.1.0"); private final ProjectInfo projectInfo; private final JdbcTemplate jdbcTemplate; diff --git a/common/actor/pom.xml b/common/actor/pom.xml index 6411934f7d..68d05cecba 100644 --- a/common/actor/pom.xml +++ b/common/actor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/cache/pom.xml b/common/cache/pom.xml index a43009b6e1..3d42e467ba 100644 --- a/common/cache/pom.xml +++ b/common/cache/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/cluster-api/pom.xml b/common/cluster-api/pom.xml index 9a75a6b0c3..f9b0b60e50 100644 --- a/common/cluster-api/pom.xml +++ b/common/cluster-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/coap-server/pom.xml b/common/coap-server/pom.xml index 1950a9ecc2..fd53b3dfc7 100644 --- a/common/coap-server/pom.xml +++ b/common/coap-server/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/dao-api/pom.xml b/common/dao-api/pom.xml index 8b0ad31e23..fe6f59503b 100644 --- a/common/dao-api/pom.xml +++ b/common/dao-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/data/pom.xml b/common/data/pom.xml index a574a6ed07..e8bd87aa19 100644 --- a/common/data/pom.xml +++ b/common/data/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/discovery-api/pom.xml b/common/discovery-api/pom.xml index 9e65abf10a..2c62c714f1 100644 --- a/common/discovery-api/pom.xml +++ b/common/discovery-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/edge-api/pom.xml b/common/edge-api/pom.xml index 208e1e7b55..704dc7759b 100644 --- a/common/edge-api/pom.xml +++ b/common/edge-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/edqs/pom.xml b/common/edqs/pom.xml index 677f5ad5ae..bd2b96da15 100644 --- a/common/edqs/pom.xml +++ b/common/edqs/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/message/pom.xml b/common/message/pom.xml index f70bc260cf..a3a4d2e38e 100644 --- a/common/message/pom.xml +++ b/common/message/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/pom.xml b/common/pom.xml index 5467f452d2..76aa577782 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT thingsboard common diff --git a/common/proto/pom.xml b/common/proto/pom.xml index bab2f8d12b..875904824b 100644 --- a/common/proto/pom.xml +++ b/common/proto/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/queue/pom.xml b/common/queue/pom.xml index 19368c1345..f88baefc6e 100644 --- a/common/queue/pom.xml +++ b/common/queue/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/script/pom.xml b/common/script/pom.xml index 4b7a812737..cb4d0c2ac2 100644 --- a/common/script/pom.xml +++ b/common/script/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/script/remote-js-client/pom.xml b/common/script/remote-js-client/pom.xml index 45d4698efe..e2dffb44a6 100644 --- a/common/script/remote-js-client/pom.xml +++ b/common/script/remote-js-client/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT script org.thingsboard.common.script diff --git a/common/script/script-api/pom.xml b/common/script/script-api/pom.xml index 3bc2168dfe..c10c49b279 100644 --- a/common/script/script-api/pom.xml +++ b/common/script/script-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT script org.thingsboard.common.script diff --git a/common/stats/pom.xml b/common/stats/pom.xml index 66011657d8..8e896767c7 100644 --- a/common/stats/pom.xml +++ b/common/stats/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/transport/coap/pom.xml b/common/transport/coap/pom.xml index e9d5dcfc1a..29bf5c7567 100644 --- a/common/transport/coap/pom.xml +++ b/common/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/http/pom.xml b/common/transport/http/pom.xml index caff518fe2..3b033bb741 100644 --- a/common/transport/http/pom.xml +++ b/common/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/lwm2m/pom.xml b/common/transport/lwm2m/pom.xml index 2bec8f7bcb..8c4e9e79ff 100644 --- a/common/transport/lwm2m/pom.xml +++ b/common/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/mqtt/pom.xml b/common/transport/mqtt/pom.xml index 18ff4e96c3..7012f22bb4 100644 --- a/common/transport/mqtt/pom.xml +++ b/common/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/pom.xml b/common/transport/pom.xml index feaef2bcce..d6f1761a40 100644 --- a/common/transport/pom.xml +++ b/common/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/transport/snmp/pom.xml b/common/transport/snmp/pom.xml index da5093349d..522034b9c8 100644 --- a/common/transport/snmp/pom.xml +++ b/common/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard.common - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT transport diff --git a/common/transport/transport-api/pom.xml b/common/transport/transport-api/pom.xml index 1cb128b439..34de935e17 100644 --- a/common/transport/transport-api/pom.xml +++ b/common/transport/transport-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/util/pom.xml b/common/util/pom.xml index 6509f154ad..2e1eb18478 100644 --- a/common/util/pom.xml +++ b/common/util/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/version-control/pom.xml b/common/version-control/pom.xml index 939d0d47ad..1930c026b5 100644 --- a/common/version-control/pom.xml +++ b/common/version-control/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT common org.thingsboard.common diff --git a/dao/pom.xml b/dao/pom.xml index 5044c0edfb..36e5bad26d 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT thingsboard dao diff --git a/edqs/pom.xml b/edqs/pom.xml index ef91e0bed4..2f5e513f16 100644 --- a/edqs/pom.xml +++ b/edqs/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT thingsboard edqs diff --git a/monitoring/pom.xml b/monitoring/pom.xml index 3fb4ba5314..1b67c4c397 100644 --- a/monitoring/pom.xml +++ b/monitoring/pom.xml @@ -21,7 +21,7 @@ 4.0.0 org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT thingsboard diff --git a/msa/black-box-tests/pom.xml b/msa/black-box-tests/pom.xml index 6800c0c4cf..0d560720c8 100644 --- a/msa/black-box-tests/pom.xml +++ b/msa/black-box-tests/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/edqs/pom.xml b/msa/edqs/pom.xml index 62374eb2bc..a40ef45fff 100644 --- a/msa/edqs/pom.xml +++ b/msa/edqs/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/js-executor/package.json b/msa/js-executor/package.json index 3f4370b642..3c77172f4e 100644 --- a/msa/js-executor/package.json +++ b/msa/js-executor/package.json @@ -1,7 +1,7 @@ { "name": "thingsboard-js-executor", "private": true, - "version": "4.2.1.1", + "version": "4.2.1.2", "description": "ThingsBoard JavaScript Executor Microservice", "main": "server.ts", "bin": "server.js", diff --git a/msa/js-executor/pom.xml b/msa/js-executor/pom.xml index 3aaf380e2f..99cef90704 100644 --- a/msa/js-executor/pom.xml +++ b/msa/js-executor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/monitoring/pom.xml b/msa/monitoring/pom.xml index b81abce27d..cd7615ae6e 100644 --- a/msa/monitoring/pom.xml +++ b/msa/monitoring/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT msa diff --git a/msa/pom.xml b/msa/pom.xml index 5eff7cc06d..29299d7b9a 100644 --- a/msa/pom.xml +++ b/msa/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT thingsboard msa diff --git a/msa/tb-node/pom.xml b/msa/tb-node/pom.xml index e473e1dfdf..0be77548a2 100644 --- a/msa/tb-node/pom.xml +++ b/msa/tb-node/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/tb/pom.xml b/msa/tb/pom.xml index 9db071c9ae..e46152fb62 100644 --- a/msa/tb/pom.xml +++ b/msa/tb/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/transport/coap/pom.xml b/msa/transport/coap/pom.xml index 098e3263b8..7b9167980d 100644 --- a/msa/transport/coap/pom.xml +++ b/msa/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/http/pom.xml b/msa/transport/http/pom.xml index 3acbeb7a2d..e56c364c72 100644 --- a/msa/transport/http/pom.xml +++ b/msa/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/lwm2m/pom.xml b/msa/transport/lwm2m/pom.xml index bc6d0d004e..7a2fc4b007 100644 --- a/msa/transport/lwm2m/pom.xml +++ b/msa/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/mqtt/pom.xml b/msa/transport/mqtt/pom.xml index ef20c96f26..7c0c77725d 100644 --- a/msa/transport/mqtt/pom.xml +++ b/msa/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/pom.xml b/msa/transport/pom.xml index 76cec909da..e1d53efaf4 100644 --- a/msa/transport/pom.xml +++ b/msa/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/transport/snmp/pom.xml b/msa/transport/snmp/pom.xml index 348880019c..ba25ffa004 100644 --- a/msa/transport/snmp/pom.xml +++ b/msa/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard.msa transport - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT org.thingsboard.msa.transport diff --git a/msa/vc-executor-docker/pom.xml b/msa/vc-executor-docker/pom.xml index b1c11efe0a..134c3d8ce5 100644 --- a/msa/vc-executor-docker/pom.xml +++ b/msa/vc-executor-docker/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/vc-executor/pom.xml b/msa/vc-executor/pom.xml index a59a8aa823..c487ddc721 100644 --- a/msa/vc-executor/pom.xml +++ b/msa/vc-executor/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/web-ui/package.json b/msa/web-ui/package.json index 97d69546bc..44aade2d73 100644 --- a/msa/web-ui/package.json +++ b/msa/web-ui/package.json @@ -1,7 +1,7 @@ { "name": "thingsboard-web-ui", "private": true, - "version": "4.2.1.1", + "version": "4.2.1.2", "description": "ThingsBoard Web UI Microservice", "main": "server.ts", "bin": "server.js", diff --git a/msa/web-ui/pom.xml b/msa/web-ui/pom.xml index 70200eddb3..36e53ed52c 100644 --- a/msa/web-ui/pom.xml +++ b/msa/web-ui/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT msa org.thingsboard.msa diff --git a/netty-mqtt/pom.xml b/netty-mqtt/pom.xml index c0743024ea..99261b714d 100644 --- a/netty-mqtt/pom.xml +++ b/netty-mqtt/pom.xml @@ -19,11 +19,11 @@ 4.0.0 org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT thingsboard netty-mqtt - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT jar Netty MQTT Client diff --git a/pom.xml b/pom.xml index 7f8ef8074a..9edccba7f7 100755 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT pom Thingsboard diff --git a/rest-client/pom.xml b/rest-client/pom.xml index eddcd84d9a..3ee5321627 100644 --- a/rest-client/pom.xml +++ b/rest-client/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT thingsboard rest-client diff --git a/rule-engine/pom.xml b/rule-engine/pom.xml index 99372b9703..d89c238559 100644 --- a/rule-engine/pom.xml +++ b/rule-engine/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT thingsboard rule-engine diff --git a/rule-engine/rule-engine-api/pom.xml b/rule-engine/rule-engine-api/pom.xml index c52fb92594..5f48404b91 100644 --- a/rule-engine/rule-engine-api/pom.xml +++ b/rule-engine/rule-engine-api/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT rule-engine org.thingsboard.rule-engine diff --git a/rule-engine/rule-engine-components/pom.xml b/rule-engine/rule-engine-components/pom.xml index f582f1277a..e307d9a57d 100644 --- a/rule-engine/rule-engine-components/pom.xml +++ b/rule-engine/rule-engine-components/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT rule-engine org.thingsboard.rule-engine diff --git a/tools/pom.xml b/tools/pom.xml index 783dc1ee73..bd16603596 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT thingsboard tools diff --git a/transport/coap/pom.xml b/transport/coap/pom.xml index f43d31e35b..681c75b102 100644 --- a/transport/coap/pom.xml +++ b/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/http/pom.xml b/transport/http/pom.xml index 7e55ae3db5..b338a4b4ea 100644 --- a/transport/http/pom.xml +++ b/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/lwm2m/pom.xml b/transport/lwm2m/pom.xml index f64f8b4aa4..4d5d621a08 100644 --- a/transport/lwm2m/pom.xml +++ b/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/mqtt/pom.xml b/transport/mqtt/pom.xml index 0bcf68cbb8..336265ff00 100644 --- a/transport/mqtt/pom.xml +++ b/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/pom.xml b/transport/pom.xml index 1d3755a766..a4b9c9c630 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT thingsboard transport diff --git a/transport/snmp/pom.xml b/transport/snmp/pom.xml index 5ea39a4066..a361d85838 100644 --- a/transport/snmp/pom.xml +++ b/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT transport diff --git a/ui-ngx/package.json b/ui-ngx/package.json index 74b4bbbb9c..b17c3e1d35 100644 --- a/ui-ngx/package.json +++ b/ui-ngx/package.json @@ -1,6 +1,6 @@ { "name": "thingsboard", - "version": "4.2.1.1", + "version": "4.2.1.2", "scripts": { "ng": "ng", "start": "node --max_old_space_size=8048 ./node_modules/@angular/cli/bin/ng serve --configuration development --host 0.0.0.0 --open", diff --git a/ui-ngx/pom.xml b/ui-ngx/pom.xml index d5bd48de78..54b8adc93a 100644 --- a/ui-ngx/pom.xml +++ b/ui-ngx/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.1-RC + 4.2.1.2-SNAPSHOT thingsboard org.thingsboard From 5e8ca1ca46099ca94151a5b32c9c7bef099816a7 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Thu, 25 Dec 2025 11:59:03 +0200 Subject: [PATCH 27/47] TMP version set to 4.2.1.2-SNAPSHOT --- application/pom.xml | 2 +- common/actor/pom.xml | 2 +- common/cache/pom.xml | 2 +- common/cluster-api/pom.xml | 2 +- common/coap-server/pom.xml | 2 +- common/dao-api/pom.xml | 2 +- common/data/pom.xml | 2 +- common/discovery-api/pom.xml | 2 +- common/edge-api/pom.xml | 2 +- common/edqs/pom.xml | 2 +- common/message/pom.xml | 2 +- common/pom.xml | 2 +- common/proto/pom.xml | 2 +- common/queue/pom.xml | 2 +- common/script/pom.xml | 2 +- common/script/remote-js-client/pom.xml | 2 +- common/script/script-api/pom.xml | 2 +- common/stats/pom.xml | 2 +- common/transport/coap/pom.xml | 2 +- common/transport/http/pom.xml | 2 +- common/transport/lwm2m/pom.xml | 2 +- common/transport/mqtt/pom.xml | 2 +- common/transport/pom.xml | 2 +- common/transport/snmp/pom.xml | 2 +- common/transport/transport-api/pom.xml | 2 +- common/util/pom.xml | 2 +- common/version-control/pom.xml | 2 +- dao/pom.xml | 2 +- edqs/pom.xml | 2 +- monitoring/pom.xml | 2 +- msa/black-box-tests/pom.xml | 2 +- msa/edqs/pom.xml | 2 +- msa/js-executor/pom.xml | 2 +- msa/monitoring/pom.xml | 2 +- msa/pom.xml | 2 +- msa/tb-node/pom.xml | 2 +- msa/tb/pom.xml | 2 +- msa/transport/coap/pom.xml | 2 +- msa/transport/http/pom.xml | 2 +- msa/transport/lwm2m/pom.xml | 2 +- msa/transport/mqtt/pom.xml | 2 +- msa/transport/pom.xml | 2 +- msa/transport/snmp/pom.xml | 2 +- msa/vc-executor-docker/pom.xml | 2 +- msa/vc-executor/pom.xml | 2 +- msa/web-ui/pom.xml | 2 +- netty-mqtt/pom.xml | 4 ++-- pom.xml | 2 +- rest-client/pom.xml | 2 +- rule-engine/pom.xml | 2 +- rule-engine/rule-engine-api/pom.xml | 2 +- rule-engine/rule-engine-components/pom.xml | 2 +- tools/pom.xml | 2 +- transport/coap/pom.xml | 2 +- transport/http/pom.xml | 2 +- transport/lwm2m/pom.xml | 2 +- transport/mqtt/pom.xml | 2 +- transport/pom.xml | 2 +- transport/snmp/pom.xml | 2 +- ui-ngx/pom.xml | 2 +- 60 files changed, 61 insertions(+), 61 deletions(-) diff --git a/application/pom.xml b/application/pom.xml index 7d304226ad..9f3b79b3f5 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT thingsboard application diff --git a/common/actor/pom.xml b/common/actor/pom.xml index 1ce65906da..68d05cecba 100644 --- a/common/actor/pom.xml +++ b/common/actor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/cache/pom.xml b/common/cache/pom.xml index bc2fbd8bda..3d42e467ba 100644 --- a/common/cache/pom.xml +++ b/common/cache/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/cluster-api/pom.xml b/common/cluster-api/pom.xml index cbb082d26a..f9b0b60e50 100644 --- a/common/cluster-api/pom.xml +++ b/common/cluster-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/coap-server/pom.xml b/common/coap-server/pom.xml index d2c5ffc7ab..fd53b3dfc7 100644 --- a/common/coap-server/pom.xml +++ b/common/coap-server/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/dao-api/pom.xml b/common/dao-api/pom.xml index 6904d69aa3..fe6f59503b 100644 --- a/common/dao-api/pom.xml +++ b/common/dao-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/data/pom.xml b/common/data/pom.xml index 921135f811..e8bd87aa19 100644 --- a/common/data/pom.xml +++ b/common/data/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/discovery-api/pom.xml b/common/discovery-api/pom.xml index 667c140615..2c62c714f1 100644 --- a/common/discovery-api/pom.xml +++ b/common/discovery-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/edge-api/pom.xml b/common/edge-api/pom.xml index 87068df36c..704dc7759b 100644 --- a/common/edge-api/pom.xml +++ b/common/edge-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/edqs/pom.xml b/common/edqs/pom.xml index aec29584e4..bd2b96da15 100644 --- a/common/edqs/pom.xml +++ b/common/edqs/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/message/pom.xml b/common/message/pom.xml index 7d4487f74a..a3a4d2e38e 100644 --- a/common/message/pom.xml +++ b/common/message/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/pom.xml b/common/pom.xml index 72ec1ee9a6..76aa577782 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT thingsboard common diff --git a/common/proto/pom.xml b/common/proto/pom.xml index 7230d20ce1..875904824b 100644 --- a/common/proto/pom.xml +++ b/common/proto/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/queue/pom.xml b/common/queue/pom.xml index 9885975417..f88baefc6e 100644 --- a/common/queue/pom.xml +++ b/common/queue/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/script/pom.xml b/common/script/pom.xml index efa5efafca..cb4d0c2ac2 100644 --- a/common/script/pom.xml +++ b/common/script/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/script/remote-js-client/pom.xml b/common/script/remote-js-client/pom.xml index 189d86d3bf..e2dffb44a6 100644 --- a/common/script/remote-js-client/pom.xml +++ b/common/script/remote-js-client/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.3.0-RC + 4.2.1.2-SNAPSHOT script org.thingsboard.common.script diff --git a/common/script/script-api/pom.xml b/common/script/script-api/pom.xml index c80e51a60f..c10c49b279 100644 --- a/common/script/script-api/pom.xml +++ b/common/script/script-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.3.0-RC + 4.2.1.2-SNAPSHOT script org.thingsboard.common.script diff --git a/common/stats/pom.xml b/common/stats/pom.xml index d1bb0a80c3..8e896767c7 100644 --- a/common/stats/pom.xml +++ b/common/stats/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/transport/coap/pom.xml b/common/transport/coap/pom.xml index dbabba721e..29bf5c7567 100644 --- a/common/transport/coap/pom.xml +++ b/common/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.3.0-RC + 4.2.1.2-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/http/pom.xml b/common/transport/http/pom.xml index 475effe638..3b033bb741 100644 --- a/common/transport/http/pom.xml +++ b/common/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.3.0-RC + 4.2.1.2-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/lwm2m/pom.xml b/common/transport/lwm2m/pom.xml index d0831647e8..8c4e9e79ff 100644 --- a/common/transport/lwm2m/pom.xml +++ b/common/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.3.0-RC + 4.2.1.2-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/mqtt/pom.xml b/common/transport/mqtt/pom.xml index 3ba19f00e5..7012f22bb4 100644 --- a/common/transport/mqtt/pom.xml +++ b/common/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.3.0-RC + 4.2.1.2-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/pom.xml b/common/transport/pom.xml index b56fbbc428..d6f1761a40 100644 --- a/common/transport/pom.xml +++ b/common/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/transport/snmp/pom.xml b/common/transport/snmp/pom.xml index b858b8ff5f..522034b9c8 100644 --- a/common/transport/snmp/pom.xml +++ b/common/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard.common - 4.3.0-RC + 4.2.1.2-SNAPSHOT transport diff --git a/common/transport/transport-api/pom.xml b/common/transport/transport-api/pom.xml index db34b1d21f..34de935e17 100644 --- a/common/transport/transport-api/pom.xml +++ b/common/transport/transport-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.3.0-RC + 4.2.1.2-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/util/pom.xml b/common/util/pom.xml index 83febcaef7..2e1eb18478 100644 --- a/common/util/pom.xml +++ b/common/util/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/version-control/pom.xml b/common/version-control/pom.xml index 6207dc3e72..1930c026b5 100644 --- a/common/version-control/pom.xml +++ b/common/version-control/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT common org.thingsboard.common diff --git a/dao/pom.xml b/dao/pom.xml index f0aa6b833f..36e5bad26d 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT thingsboard dao diff --git a/edqs/pom.xml b/edqs/pom.xml index 546da5902f..2f5e513f16 100644 --- a/edqs/pom.xml +++ b/edqs/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT thingsboard edqs diff --git a/monitoring/pom.xml b/monitoring/pom.xml index dbac99d9c4..1b67c4c397 100644 --- a/monitoring/pom.xml +++ b/monitoring/pom.xml @@ -21,7 +21,7 @@ 4.0.0 org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT thingsboard diff --git a/msa/black-box-tests/pom.xml b/msa/black-box-tests/pom.xml index 30873c4c50..0d560720c8 100644 --- a/msa/black-box-tests/pom.xml +++ b/msa/black-box-tests/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/edqs/pom.xml b/msa/edqs/pom.xml index 1b0084d471..a40ef45fff 100644 --- a/msa/edqs/pom.xml +++ b/msa/edqs/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/js-executor/pom.xml b/msa/js-executor/pom.xml index 50ab2286bb..99cef90704 100644 --- a/msa/js-executor/pom.xml +++ b/msa/js-executor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/monitoring/pom.xml b/msa/monitoring/pom.xml index 2e85f02671..cd7615ae6e 100644 --- a/msa/monitoring/pom.xml +++ b/msa/monitoring/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT msa diff --git a/msa/pom.xml b/msa/pom.xml index f41f7f0fc0..e72d2ccfde 100644 --- a/msa/pom.xml +++ b/msa/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT thingsboard msa diff --git a/msa/tb-node/pom.xml b/msa/tb-node/pom.xml index 29ea35dba5..0be77548a2 100644 --- a/msa/tb-node/pom.xml +++ b/msa/tb-node/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/tb/pom.xml b/msa/tb/pom.xml index fd562fc692..e46152fb62 100644 --- a/msa/tb/pom.xml +++ b/msa/tb/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/transport/coap/pom.xml b/msa/transport/coap/pom.xml index 6a08c8154c..7b9167980d 100644 --- a/msa/transport/coap/pom.xml +++ b/msa/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.3.0-RC + 4.2.1.2-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/http/pom.xml b/msa/transport/http/pom.xml index 680863481a..e56c364c72 100644 --- a/msa/transport/http/pom.xml +++ b/msa/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.3.0-RC + 4.2.1.2-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/lwm2m/pom.xml b/msa/transport/lwm2m/pom.xml index 1226a9d549..7a2fc4b007 100644 --- a/msa/transport/lwm2m/pom.xml +++ b/msa/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.3.0-RC + 4.2.1.2-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/mqtt/pom.xml b/msa/transport/mqtt/pom.xml index cfe624c69d..7c0c77725d 100644 --- a/msa/transport/mqtt/pom.xml +++ b/msa/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.3.0-RC + 4.2.1.2-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/pom.xml b/msa/transport/pom.xml index b63aec5c68..e1d53efaf4 100644 --- a/msa/transport/pom.xml +++ b/msa/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/transport/snmp/pom.xml b/msa/transport/snmp/pom.xml index b6a72efa58..ba25ffa004 100644 --- a/msa/transport/snmp/pom.xml +++ b/msa/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard.msa transport - 4.3.0-RC + 4.2.1.2-SNAPSHOT org.thingsboard.msa.transport diff --git a/msa/vc-executor-docker/pom.xml b/msa/vc-executor-docker/pom.xml index eba717cb36..134c3d8ce5 100644 --- a/msa/vc-executor-docker/pom.xml +++ b/msa/vc-executor-docker/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/vc-executor/pom.xml b/msa/vc-executor/pom.xml index 0635e69bd5..c487ddc721 100644 --- a/msa/vc-executor/pom.xml +++ b/msa/vc-executor/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/web-ui/pom.xml b/msa/web-ui/pom.xml index 1eb47413d7..36e53ed52c 100644 --- a/msa/web-ui/pom.xml +++ b/msa/web-ui/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT msa org.thingsboard.msa diff --git a/netty-mqtt/pom.xml b/netty-mqtt/pom.xml index 01bc357127..99261b714d 100644 --- a/netty-mqtt/pom.xml +++ b/netty-mqtt/pom.xml @@ -19,11 +19,11 @@ 4.0.0 org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT thingsboard netty-mqtt - 4.3.0-RC + 4.2.1.2-SNAPSHOT jar Netty MQTT Client diff --git a/pom.xml b/pom.xml index 99108196aa..9edccba7f7 100755 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT pom Thingsboard diff --git a/rest-client/pom.xml b/rest-client/pom.xml index fd5783227b..3ee5321627 100644 --- a/rest-client/pom.xml +++ b/rest-client/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT thingsboard rest-client diff --git a/rule-engine/pom.xml b/rule-engine/pom.xml index dce202bc94..d89c238559 100644 --- a/rule-engine/pom.xml +++ b/rule-engine/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT thingsboard rule-engine diff --git a/rule-engine/rule-engine-api/pom.xml b/rule-engine/rule-engine-api/pom.xml index ef6904b24c..5f48404b91 100644 --- a/rule-engine/rule-engine-api/pom.xml +++ b/rule-engine/rule-engine-api/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT rule-engine org.thingsboard.rule-engine diff --git a/rule-engine/rule-engine-components/pom.xml b/rule-engine/rule-engine-components/pom.xml index 78e6b5e684..e307d9a57d 100644 --- a/rule-engine/rule-engine-components/pom.xml +++ b/rule-engine/rule-engine-components/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT rule-engine org.thingsboard.rule-engine diff --git a/tools/pom.xml b/tools/pom.xml index d76b19ba2d..bd16603596 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT thingsboard tools diff --git a/transport/coap/pom.xml b/transport/coap/pom.xml index 4a30c5d2bb..681c75b102 100644 --- a/transport/coap/pom.xml +++ b/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/http/pom.xml b/transport/http/pom.xml index bc1c7a845d..b338a4b4ea 100644 --- a/transport/http/pom.xml +++ b/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/lwm2m/pom.xml b/transport/lwm2m/pom.xml index 8871a2b2b3..4d5d621a08 100644 --- a/transport/lwm2m/pom.xml +++ b/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/mqtt/pom.xml b/transport/mqtt/pom.xml index 2fe1cb91b2..336265ff00 100644 --- a/transport/mqtt/pom.xml +++ b/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/pom.xml b/transport/pom.xml index ad394b46f9..a4b9c9c630 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT thingsboard transport diff --git a/transport/snmp/pom.xml b/transport/snmp/pom.xml index 528538bd03..a361d85838 100644 --- a/transport/snmp/pom.xml +++ b/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT transport diff --git a/ui-ngx/pom.xml b/ui-ngx/pom.xml index f83c914409..54b8adc93a 100644 --- a/ui-ngx/pom.xml +++ b/ui-ngx/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.3.0-RC + 4.2.1.2-SNAPSHOT thingsboard org.thingsboard From c069d282315da2b63eb9241914433486a29f481d Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Thu, 25 Dec 2025 12:04:22 +0200 Subject: [PATCH 28/47] Version set to 4.3.0-RC --- application/pom.xml | 2 +- .../service/install/DefaultDatabaseSchemaSettingsService.java | 3 ++- common/actor/pom.xml | 2 +- common/cache/pom.xml | 2 +- common/cluster-api/pom.xml | 2 +- common/coap-server/pom.xml | 2 +- common/dao-api/pom.xml | 2 +- common/data/pom.xml | 2 +- common/discovery-api/pom.xml | 2 +- common/edge-api/pom.xml | 2 +- common/edqs/pom.xml | 2 +- common/message/pom.xml | 2 +- common/pom.xml | 2 +- common/proto/pom.xml | 2 +- common/queue/pom.xml | 2 +- common/script/pom.xml | 2 +- common/script/remote-js-client/pom.xml | 2 +- common/script/script-api/pom.xml | 2 +- common/stats/pom.xml | 2 +- common/transport/coap/pom.xml | 2 +- common/transport/http/pom.xml | 2 +- common/transport/lwm2m/pom.xml | 2 +- common/transport/mqtt/pom.xml | 2 +- common/transport/pom.xml | 2 +- common/transport/snmp/pom.xml | 2 +- common/transport/transport-api/pom.xml | 2 +- common/util/pom.xml | 2 +- common/version-control/pom.xml | 2 +- dao/pom.xml | 2 +- edqs/pom.xml | 2 +- monitoring/pom.xml | 2 +- msa/black-box-tests/pom.xml | 2 +- msa/edqs/pom.xml | 2 +- msa/js-executor/pom.xml | 2 +- msa/monitoring/pom.xml | 2 +- msa/pom.xml | 4 ++-- msa/tb-node/pom.xml | 2 +- msa/tb/pom.xml | 2 +- msa/transport/coap/pom.xml | 2 +- msa/transport/http/pom.xml | 2 +- msa/transport/lwm2m/pom.xml | 2 +- msa/transport/mqtt/pom.xml | 2 +- msa/transport/pom.xml | 2 +- msa/transport/snmp/pom.xml | 2 +- msa/vc-executor-docker/pom.xml | 2 +- msa/vc-executor/pom.xml | 2 +- msa/web-ui/pom.xml | 2 +- netty-mqtt/pom.xml | 4 ++-- pom.xml | 2 +- rest-client/pom.xml | 2 +- rule-engine/pom.xml | 2 +- rule-engine/rule-engine-api/pom.xml | 2 +- rule-engine/rule-engine-components/pom.xml | 2 +- tools/pom.xml | 2 +- transport/coap/pom.xml | 2 +- transport/http/pom.xml | 2 +- transport/lwm2m/pom.xml | 2 +- transport/mqtt/pom.xml | 2 +- transport/pom.xml | 2 +- transport/snmp/pom.xml | 2 +- ui-ngx/pom.xml | 2 +- 61 files changed, 64 insertions(+), 63 deletions(-) diff --git a/application/pom.xml b/application/pom.xml index 9f3b79b3f5..7d304226ad 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC thingsboard application diff --git a/application/src/main/java/org/thingsboard/server/service/install/DefaultDatabaseSchemaSettingsService.java b/application/src/main/java/org/thingsboard/server/service/install/DefaultDatabaseSchemaSettingsService.java index 500ad60df0..d65ac6ba31 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/DefaultDatabaseSchemaSettingsService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/DefaultDatabaseSchemaSettingsService.java @@ -30,7 +30,8 @@ public class DefaultDatabaseSchemaSettingsService implements DatabaseSchemaSetti // This list should include all versions that are compatible for the upgrade in 4 digits format (like 4.2.0.0, etc.). // The compatibility cycle usually breaks when we have some scripts written in Java that may not work after a new release. - private static final List SUPPORTED_VERSIONS_FOR_UPGRADE = List.of("4.2.1.0"); + // TODO: don't check the "patch" number, since upgrade is not required for patch releases + private static final List SUPPORTED_VERSIONS_FOR_UPGRADE = List.of("4.2.1.0", "4.2.1.1", "4.2.1.2"); private final ProjectInfo projectInfo; private final JdbcTemplate jdbcTemplate; diff --git a/common/actor/pom.xml b/common/actor/pom.xml index 68d05cecba..1ce65906da 100644 --- a/common/actor/pom.xml +++ b/common/actor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC common org.thingsboard.common diff --git a/common/cache/pom.xml b/common/cache/pom.xml index 3d42e467ba..bc2fbd8bda 100644 --- a/common/cache/pom.xml +++ b/common/cache/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC common org.thingsboard.common diff --git a/common/cluster-api/pom.xml b/common/cluster-api/pom.xml index f9b0b60e50..cbb082d26a 100644 --- a/common/cluster-api/pom.xml +++ b/common/cluster-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC common org.thingsboard.common diff --git a/common/coap-server/pom.xml b/common/coap-server/pom.xml index fd53b3dfc7..d2c5ffc7ab 100644 --- a/common/coap-server/pom.xml +++ b/common/coap-server/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC common org.thingsboard.common diff --git a/common/dao-api/pom.xml b/common/dao-api/pom.xml index fe6f59503b..6904d69aa3 100644 --- a/common/dao-api/pom.xml +++ b/common/dao-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC common org.thingsboard.common diff --git a/common/data/pom.xml b/common/data/pom.xml index e8bd87aa19..921135f811 100644 --- a/common/data/pom.xml +++ b/common/data/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC common org.thingsboard.common diff --git a/common/discovery-api/pom.xml b/common/discovery-api/pom.xml index 2c62c714f1..667c140615 100644 --- a/common/discovery-api/pom.xml +++ b/common/discovery-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC common org.thingsboard.common diff --git a/common/edge-api/pom.xml b/common/edge-api/pom.xml index 704dc7759b..87068df36c 100644 --- a/common/edge-api/pom.xml +++ b/common/edge-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC common org.thingsboard.common diff --git a/common/edqs/pom.xml b/common/edqs/pom.xml index bd2b96da15..aec29584e4 100644 --- a/common/edqs/pom.xml +++ b/common/edqs/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC common org.thingsboard.common diff --git a/common/message/pom.xml b/common/message/pom.xml index a3a4d2e38e..7d4487f74a 100644 --- a/common/message/pom.xml +++ b/common/message/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC common org.thingsboard.common diff --git a/common/pom.xml b/common/pom.xml index 76aa577782..72ec1ee9a6 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC thingsboard common diff --git a/common/proto/pom.xml b/common/proto/pom.xml index 875904824b..7230d20ce1 100644 --- a/common/proto/pom.xml +++ b/common/proto/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC common org.thingsboard.common diff --git a/common/queue/pom.xml b/common/queue/pom.xml index f88baefc6e..9885975417 100644 --- a/common/queue/pom.xml +++ b/common/queue/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC common org.thingsboard.common diff --git a/common/script/pom.xml b/common/script/pom.xml index cb4d0c2ac2..efa5efafca 100644 --- a/common/script/pom.xml +++ b/common/script/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC common org.thingsboard.common diff --git a/common/script/remote-js-client/pom.xml b/common/script/remote-js-client/pom.xml index e2dffb44a6..189d86d3bf 100644 --- a/common/script/remote-js-client/pom.xml +++ b/common/script/remote-js-client/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.2.1.2-SNAPSHOT + 4.3.0-RC script org.thingsboard.common.script diff --git a/common/script/script-api/pom.xml b/common/script/script-api/pom.xml index c10c49b279..c80e51a60f 100644 --- a/common/script/script-api/pom.xml +++ b/common/script/script-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.2.1.2-SNAPSHOT + 4.3.0-RC script org.thingsboard.common.script diff --git a/common/stats/pom.xml b/common/stats/pom.xml index 8e896767c7..d1bb0a80c3 100644 --- a/common/stats/pom.xml +++ b/common/stats/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC common org.thingsboard.common diff --git a/common/transport/coap/pom.xml b/common/transport/coap/pom.xml index 29bf5c7567..dbabba721e 100644 --- a/common/transport/coap/pom.xml +++ b/common/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.2.1.2-SNAPSHOT + 4.3.0-RC transport org.thingsboard.common.transport diff --git a/common/transport/http/pom.xml b/common/transport/http/pom.xml index 3b033bb741..475effe638 100644 --- a/common/transport/http/pom.xml +++ b/common/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.2.1.2-SNAPSHOT + 4.3.0-RC transport org.thingsboard.common.transport diff --git a/common/transport/lwm2m/pom.xml b/common/transport/lwm2m/pom.xml index 8c4e9e79ff..d0831647e8 100644 --- a/common/transport/lwm2m/pom.xml +++ b/common/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.2.1.2-SNAPSHOT + 4.3.0-RC transport org.thingsboard.common.transport diff --git a/common/transport/mqtt/pom.xml b/common/transport/mqtt/pom.xml index 7012f22bb4..3ba19f00e5 100644 --- a/common/transport/mqtt/pom.xml +++ b/common/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.2.1.2-SNAPSHOT + 4.3.0-RC transport org.thingsboard.common.transport diff --git a/common/transport/pom.xml b/common/transport/pom.xml index d6f1761a40..b56fbbc428 100644 --- a/common/transport/pom.xml +++ b/common/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC common org.thingsboard.common diff --git a/common/transport/snmp/pom.xml b/common/transport/snmp/pom.xml index 522034b9c8..b858b8ff5f 100644 --- a/common/transport/snmp/pom.xml +++ b/common/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard.common - 4.2.1.2-SNAPSHOT + 4.3.0-RC transport diff --git a/common/transport/transport-api/pom.xml b/common/transport/transport-api/pom.xml index 34de935e17..db34b1d21f 100644 --- a/common/transport/transport-api/pom.xml +++ b/common/transport/transport-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.2.1.2-SNAPSHOT + 4.3.0-RC transport org.thingsboard.common.transport diff --git a/common/util/pom.xml b/common/util/pom.xml index 2e1eb18478..83febcaef7 100644 --- a/common/util/pom.xml +++ b/common/util/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC common org.thingsboard.common diff --git a/common/version-control/pom.xml b/common/version-control/pom.xml index 1930c026b5..6207dc3e72 100644 --- a/common/version-control/pom.xml +++ b/common/version-control/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC common org.thingsboard.common diff --git a/dao/pom.xml b/dao/pom.xml index 36e5bad26d..f0aa6b833f 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC thingsboard dao diff --git a/edqs/pom.xml b/edqs/pom.xml index 2f5e513f16..546da5902f 100644 --- a/edqs/pom.xml +++ b/edqs/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC thingsboard edqs diff --git a/monitoring/pom.xml b/monitoring/pom.xml index 1b67c4c397..dbac99d9c4 100644 --- a/monitoring/pom.xml +++ b/monitoring/pom.xml @@ -21,7 +21,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC thingsboard diff --git a/msa/black-box-tests/pom.xml b/msa/black-box-tests/pom.xml index 0d560720c8..30873c4c50 100644 --- a/msa/black-box-tests/pom.xml +++ b/msa/black-box-tests/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC msa org.thingsboard.msa diff --git a/msa/edqs/pom.xml b/msa/edqs/pom.xml index a40ef45fff..1b0084d471 100644 --- a/msa/edqs/pom.xml +++ b/msa/edqs/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC msa org.thingsboard.msa diff --git a/msa/js-executor/pom.xml b/msa/js-executor/pom.xml index 99cef90704..50ab2286bb 100644 --- a/msa/js-executor/pom.xml +++ b/msa/js-executor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC msa org.thingsboard.msa diff --git a/msa/monitoring/pom.xml b/msa/monitoring/pom.xml index cd7615ae6e..2e85f02671 100644 --- a/msa/monitoring/pom.xml +++ b/msa/monitoring/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC msa diff --git a/msa/pom.xml b/msa/pom.xml index 8b19c757b1..8207996169 100644 --- a/msa/pom.xml +++ b/msa/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC thingsboard msa @@ -133,7 +133,7 @@ - 4.2.1-latest + 4.3.0-latest false diff --git a/msa/tb-node/pom.xml b/msa/tb-node/pom.xml index 0be77548a2..29ea35dba5 100644 --- a/msa/tb-node/pom.xml +++ b/msa/tb-node/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC msa org.thingsboard.msa diff --git a/msa/tb/pom.xml b/msa/tb/pom.xml index e46152fb62..fd562fc692 100644 --- a/msa/tb/pom.xml +++ b/msa/tb/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC msa org.thingsboard.msa diff --git a/msa/transport/coap/pom.xml b/msa/transport/coap/pom.xml index 7b9167980d..6a08c8154c 100644 --- a/msa/transport/coap/pom.xml +++ b/msa/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.2.1.2-SNAPSHOT + 4.3.0-RC transport org.thingsboard.msa.transport diff --git a/msa/transport/http/pom.xml b/msa/transport/http/pom.xml index e56c364c72..680863481a 100644 --- a/msa/transport/http/pom.xml +++ b/msa/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.2.1.2-SNAPSHOT + 4.3.0-RC transport org.thingsboard.msa.transport diff --git a/msa/transport/lwm2m/pom.xml b/msa/transport/lwm2m/pom.xml index 7a2fc4b007..1226a9d549 100644 --- a/msa/transport/lwm2m/pom.xml +++ b/msa/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.2.1.2-SNAPSHOT + 4.3.0-RC transport org.thingsboard.msa.transport diff --git a/msa/transport/mqtt/pom.xml b/msa/transport/mqtt/pom.xml index 7c0c77725d..cfe624c69d 100644 --- a/msa/transport/mqtt/pom.xml +++ b/msa/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.2.1.2-SNAPSHOT + 4.3.0-RC transport org.thingsboard.msa.transport diff --git a/msa/transport/pom.xml b/msa/transport/pom.xml index e1d53efaf4..b63aec5c68 100644 --- a/msa/transport/pom.xml +++ b/msa/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC msa org.thingsboard.msa diff --git a/msa/transport/snmp/pom.xml b/msa/transport/snmp/pom.xml index ba25ffa004..b6a72efa58 100644 --- a/msa/transport/snmp/pom.xml +++ b/msa/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard.msa transport - 4.2.1.2-SNAPSHOT + 4.3.0-RC org.thingsboard.msa.transport diff --git a/msa/vc-executor-docker/pom.xml b/msa/vc-executor-docker/pom.xml index 134c3d8ce5..eba717cb36 100644 --- a/msa/vc-executor-docker/pom.xml +++ b/msa/vc-executor-docker/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC msa org.thingsboard.msa diff --git a/msa/vc-executor/pom.xml b/msa/vc-executor/pom.xml index c487ddc721..0635e69bd5 100644 --- a/msa/vc-executor/pom.xml +++ b/msa/vc-executor/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC msa org.thingsboard.msa diff --git a/msa/web-ui/pom.xml b/msa/web-ui/pom.xml index 36e53ed52c..1eb47413d7 100644 --- a/msa/web-ui/pom.xml +++ b/msa/web-ui/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC msa org.thingsboard.msa diff --git a/netty-mqtt/pom.xml b/netty-mqtt/pom.xml index 99261b714d..01bc357127 100644 --- a/netty-mqtt/pom.xml +++ b/netty-mqtt/pom.xml @@ -19,11 +19,11 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC thingsboard netty-mqtt - 4.2.1.2-SNAPSHOT + 4.3.0-RC jar Netty MQTT Client diff --git a/pom.xml b/pom.xml index 9edccba7f7..99108196aa 100755 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC pom Thingsboard diff --git a/rest-client/pom.xml b/rest-client/pom.xml index 3ee5321627..fd5783227b 100644 --- a/rest-client/pom.xml +++ b/rest-client/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC thingsboard rest-client diff --git a/rule-engine/pom.xml b/rule-engine/pom.xml index d89c238559..dce202bc94 100644 --- a/rule-engine/pom.xml +++ b/rule-engine/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC thingsboard rule-engine diff --git a/rule-engine/rule-engine-api/pom.xml b/rule-engine/rule-engine-api/pom.xml index 5f48404b91..ef6904b24c 100644 --- a/rule-engine/rule-engine-api/pom.xml +++ b/rule-engine/rule-engine-api/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC rule-engine org.thingsboard.rule-engine diff --git a/rule-engine/rule-engine-components/pom.xml b/rule-engine/rule-engine-components/pom.xml index e307d9a57d..78e6b5e684 100644 --- a/rule-engine/rule-engine-components/pom.xml +++ b/rule-engine/rule-engine-components/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC rule-engine org.thingsboard.rule-engine diff --git a/tools/pom.xml b/tools/pom.xml index bd16603596..d76b19ba2d 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC thingsboard tools diff --git a/transport/coap/pom.xml b/transport/coap/pom.xml index 681c75b102..4a30c5d2bb 100644 --- a/transport/coap/pom.xml +++ b/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC transport org.thingsboard.transport diff --git a/transport/http/pom.xml b/transport/http/pom.xml index b338a4b4ea..bc1c7a845d 100644 --- a/transport/http/pom.xml +++ b/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC transport org.thingsboard.transport diff --git a/transport/lwm2m/pom.xml b/transport/lwm2m/pom.xml index 4d5d621a08..8871a2b2b3 100644 --- a/transport/lwm2m/pom.xml +++ b/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC transport org.thingsboard.transport diff --git a/transport/mqtt/pom.xml b/transport/mqtt/pom.xml index 336265ff00..2fe1cb91b2 100644 --- a/transport/mqtt/pom.xml +++ b/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC transport org.thingsboard.transport diff --git a/transport/pom.xml b/transport/pom.xml index a4b9c9c630..ad394b46f9 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC thingsboard transport diff --git a/transport/snmp/pom.xml b/transport/snmp/pom.xml index a361d85838..528538bd03 100644 --- a/transport/snmp/pom.xml +++ b/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC transport diff --git a/ui-ngx/pom.xml b/ui-ngx/pom.xml index 54b8adc93a..f83c914409 100644 --- a/ui-ngx/pom.xml +++ b/ui-ngx/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.2.1.2-SNAPSHOT + 4.3.0-RC thingsboard org.thingsboard From 86626834b01fd0851f89c794515fc7e3b10479c5 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Thu, 25 Dec 2025 14:50:47 +0200 Subject: [PATCH 29/47] Fix occasional alarm rule test failure due to race condition --- .../cf/ctx/state/alarm/AlarmCalculatedFieldState.java | 3 ++- .../server/service/cf/ctx/state/alarm/AlarmRuleState.java | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/alarm/AlarmCalculatedFieldState.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/alarm/AlarmCalculatedFieldState.java index 4f2cbede09..8c258d0b00 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/alarm/AlarmCalculatedFieldState.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/alarm/AlarmCalculatedFieldState.java @@ -150,7 +150,8 @@ public class AlarmCalculatedFieldState extends BaseCalculatedFieldState { clearRuleState = null; } } - log.debug("Initialized create rule states {} and clear rule state {} for {}", createRuleStates, clearRuleState, configuration); + log.debug("Initialized create rule states {} and clear rule state {} for {}. Restored: {}, reeval needed: {}", + createRuleStates, clearRuleState, configuration, restored, reevalNeeded); if (reevalNeeded.get()) { initCurrentAlarm(ctx); diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/alarm/AlarmRuleState.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/alarm/AlarmRuleState.java index 569bbe9310..cf7b1ce108 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/alarm/AlarmRuleState.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/alarm/AlarmRuleState.java @@ -262,6 +262,14 @@ public class AlarmRuleState { } } + public void setDurationCheckFuture(ScheduledFuture durationCheckFuture) { + if (this.durationCheckFuture != null) { + log.warn("Setting new duration check future while previous is not null for state {}", this, new RuntimeException("stacktrace")); + this.durationCheckFuture.cancel(true); + } + this.durationCheckFuture = durationCheckFuture; + } + public boolean isEmpty() { return eventCount == 0L && firstEventTs == 0L && lastCheckTs == 0L && durationCheckFuture == null; } From e083aaa4c3691c85c938470eaea7c3910c9bc83a Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 26 Dec 2025 11:34:43 +0200 Subject: [PATCH 30/47] UI: Fixed calculated fields panel dirty state --- .../calculated-fields/calculated-field.component.ts | 3 +-- .../calculated-field-arguments-table.component.ts | 5 +++-- ...alculated-field-geofencing-zone-groups-table.component.ts | 4 ++-- .../metrics/calculated-field-metrics-table.component.ts | 4 ++-- .../components/output/calculated-field-output.component.ts | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.ts index 01d7a8de7a..01cc53609d 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.ts @@ -116,10 +116,9 @@ export class CalculatedFieldComponent extends EntityComponent { this.entityForm.patchValue({ configuration: preparedConfig, debugSettings, entityId, ...value }, {emitEvent: false}); - this.entityForm.get('type').updateValueAndValidity({onlySelf: true}); }); } diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-arguments-table.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-arguments-table.component.ts index 9a8395a557..89c87c8e27 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-arguments-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-arguments-table.component.ts @@ -239,7 +239,7 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces } writeValue(argumentsObj: Record): void { - this.argumentsFormArray.clear(); + this.argumentsFormArray.clear({emitEvent: false}); this.populateArgumentsFormArray(argumentsObj); this.updateEntityNameMap(this.argumentsFormArray.value); } @@ -264,7 +264,8 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces }; this.argumentsFormArray.push(this.fb.control(value), { emitEvent: false }); }); - this.argumentsFormArray.updateValueAndValidity(); + this.updateDataSource(this.argumentsFormArray.value); + // this.argumentsFormArray.updateValueAndValidity({emitEvent: false}); } private updateEntityNameMap(values: CalculatedFieldArgumentValue[]): void { diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/geofencing-configuration/calculated-field-geofencing-zone-groups-table.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/geofencing-configuration/calculated-field-geofencing-zone-groups-table.component.ts index 228d08fd90..e102033f18 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/geofencing-configuration/calculated-field-geofencing-zone-groups-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/geofencing-configuration/calculated-field-geofencing-zone-groups-table.component.ts @@ -214,7 +214,7 @@ export class CalculatedFieldGeofencingZoneGroupsTableComponent implements Contro } writeValue(zonesObj: Record): void { - this.zoneGroupsFormArray.clear(); + this.zoneGroupsFormArray.clear({emitEvent: false}); this.populateZonesFormArray(zonesObj); this.updateEntityNameMap(this.zoneGroupsFormArray.value); } @@ -231,7 +231,7 @@ export class CalculatedFieldGeofencingZoneGroupsTableComponent implements Contro }; this.zoneGroupsFormArray.push(this.fb.control(value), { emitEvent: false }); }); - this.zoneGroupsFormArray.updateValueAndValidity(); + this.updateDataSource(this.zoneGroupsFormArray.value); } private updateEntityNameMap(values: CalculatedFieldGeofencingValue[]): void { diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/metrics/calculated-field-metrics-table.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/metrics/calculated-field-metrics-table.component.ts index 21b21d07ac..531f8d1014 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/metrics/calculated-field-metrics-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/metrics/calculated-field-metrics-table.component.ts @@ -218,7 +218,7 @@ export class CalculatedFieldMetricsTableComponent implements OnInit, ControlValu } writeValue(metrics: Record): void { - this.metricsFormArray.clear(); + this.metricsFormArray.clear({emitEvent: false}); this.populateZonesFormArray(metrics); } @@ -230,7 +230,7 @@ export class CalculatedFieldMetricsTableComponent implements OnInit, ControlValu }; this.metricsFormArray.push(this.fb.control(value), { emitEvent: false }); }); - this.metricsFormArray.updateValueAndValidity(); + this.updateDataSource(this.metricsFormArray.value); } private getSortValue(metric: CalculatedFieldAggMetricValue, column: string): string { diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/output/calculated-field-output.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/output/calculated-field-output.component.ts index 00fea861f6..63e3faf131 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/output/calculated-field-output.component.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/output/calculated-field-output.component.ts @@ -253,7 +253,7 @@ export class CalculatedFieldOutputComponent implements ControlValueAccessor, Val private updateTimeSeriesTtl(value: boolean) { if (value) { - this.outputForm.get('strategy.useCustomTtl').enable({emitEvent: true}); + this.outputForm.get('strategy.useCustomTtl').enable({emitEvent: false}); } else { this.outputForm.get('strategy.useCustomTtl').disable({emitEvent: false}); this.outputForm.get('strategy.ttl').disable({emitEvent: false}); From 3c5df439ccbbe228ef74c85f1c9984cd323c01ed Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 26 Dec 2025 12:42:23 +0200 Subject: [PATCH 31/47] UI: Fixed minor problem in calculated fields panel --- .../calculated-fields-table-config.ts | 23 ++++++++++++++++--- ...culated-field-arguments-table.component.ts | 3 +-- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table-config.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table-config.ts index 5841d9e511..e5e4063c21 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table-config.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table-config.ts @@ -35,7 +35,7 @@ import { DestroyRef, Renderer2 } from '@angular/core'; import { EntityDebugSettings } from '@shared/models/entity.models'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { CalculatedFieldsService } from '@core/http/calculated-fields.service'; -import { catchError, filter, switchMap, tap } from 'rxjs/operators'; +import { catchError, filter, first, switchMap, tap } from 'rxjs/operators'; import { ArgumentEntityType, ArgumentType, @@ -175,7 +175,8 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig true, - onAction: ($event, entity) => this.openDebugEventsDialog($event, entity), + onAction: ($event, entity) => + this.pageMode ? this.openDebugTab($event, entity) : this.openDebugEventsDialog($event, entity), }, { name: '', @@ -202,7 +203,7 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig 1) { + table.entityDetailsPanel.matTabGroup.selectedIndex = 1; + } else { + table.entityDetailsPanel.matTabGroup._tabs.changes.pipe( + first() + ).subscribe(() => { + table.entityDetailsPanel.matTabGroup.selectedIndex = 1; + }) + } + } + } + private editCalculatedField($event: Event, calculatedField: CalculatedFieldsTableEntity, isDirty = false): void { $event?.stopPropagation(); this.getCalculatedFieldDialog(calculatedField, 'action.apply', isDirty) diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-arguments-table.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-arguments-table.component.ts index 89c87c8e27..983609dfa7 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-arguments-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-arguments-table.component.ts @@ -249,7 +249,7 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces } protected changeIsScriptMode(): void { - this.argumentsFormArray.updateValueAndValidity(); + this.argumentsFormArray.updateValueAndValidity({emitEvent: !this.disable}); } protected isEditButtonShowBadge(argument: CalculatedFieldArgumentValue): boolean { @@ -265,7 +265,6 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces this.argumentsFormArray.push(this.fb.control(value), { emitEvent: false }); }); this.updateDataSource(this.argumentsFormArray.value); - // this.argumentsFormArray.updateValueAndValidity({emitEvent: false}); } private updateEntityNameMap(values: CalculatedFieldArgumentValue[]): void { From 96f2c340588d959f84b8b762e431fbcf37dcbfc4 Mon Sep 17 00:00:00 2001 From: ArtemDzhereleiko Date: Thu, 25 Dec 2025 13:04:34 +0200 Subject: [PATCH 32/47] UI: Alarm rule details page --- .../alarm-rules/alarm-rule.module.ts | 5 +- .../alarm-rules/alarm-rules-table-config.ts | 59 ++++-- .../alarm-rules-table.component.ts | 4 +- .../alarm-rules/alarm-rules.component.html | 157 ++++++++++++++ .../alarm-rules/alarm-rules.component.ts | 199 ++++++++++++++++++ .../home/pages/alarm/alarm-routing.module.ts | 87 +++++++- .../alarm/alarm-rules-tabs.component.html | 30 +++ .../pages/alarm/alarm-rules-tabs.component.ts | 49 +++++ .../modules/home/pages/alarm/alarm.module.ts | 5 +- .../assets/locale/locale.constant-en_US.json | 1 + 10 files changed, 572 insertions(+), 24 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules.component.html create mode 100644 ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules.component.ts create mode 100644 ui-ngx/src/app/modules/home/pages/alarm/alarm-rules-tabs.component.html create mode 100644 ui-ngx/src/app/modules/home/pages/alarm/alarm-rules-tabs.component.ts diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule.module.ts b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule.module.ts index 52d91188bc..f7adf5dab0 100644 --- a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule.module.ts +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule.module.ts @@ -53,6 +53,7 @@ import { AlarmRuleTableHeaderComponent } from "@home/components/alarm-rules/alar import { AlarmRuleFilterPredicateNoDataValueComponent } from "@home/components/alarm-rules/filter/alarm-rule-filter-predicate-no-data-value.component"; +import { AlarmRulesComponent } from '@home/components/alarm-rules/alarm-rules.component'; @NgModule({ declarations: [ @@ -73,7 +74,8 @@ import { AlarmRuleDetailsDialogComponent, AlarmRuleFilterConfigComponent, AlarmRuleTableHeaderComponent, - AlarmRuleFilterPredicateNoDataValueComponent + AlarmRuleFilterPredicateNoDataValueComponent, + AlarmRulesComponent ], imports: [ CommonModule, @@ -83,6 +85,7 @@ import { ], exports: [ AlarmRuleDialogComponent, + AlarmRulesComponent ] }) export class AlarmRuleModule { } diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table-config.ts b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table-config.ts index b4082fb75c..b0ac04aacb 100644 --- a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table-config.ts +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table-config.ts @@ -21,7 +21,7 @@ import { EntityTableColumn, EntityTableConfig } from '@home/models/entity/entities-table-config.models'; -import { EntityType, entityTypeTranslations } from '@shared/models/entity-type.models'; +import { EntityType, entityTypeResources, entityTypeTranslations } from '@shared/models/entity-type.models'; import { TranslateService } from '@ngx-translate/core'; import { Direction } from '@shared/models/page/sort-order'; import { MatDialog } from '@angular/material/dialog'; @@ -67,6 +67,10 @@ import { CalculatedFieldScriptTestDialogComponent, CalculatedFieldTestScriptDialogData } from "@home/components/calculated-fields/components/test-dialog/calculated-field-script-test-dialog.component"; +import { AlarmRulesTabsComponent } from '@home/pages/alarm/alarm-rules-tabs.component'; +import { Router } from '@angular/router'; +import { EntityAction } from '@home/models/entity/entity-component.models'; +import { AlarmRulesComponent } from '@home/components/alarm-rules/alarm-rules.component'; type AlarmRuleTableEntity = CalculatedField | CalculatedFieldInfo; @@ -93,27 +97,26 @@ export class AlarmRulesTableConfig extends EntityTableConfig { - this.editCalculatedField($event, model); - return true; - }; } this.tableTitle = this.pageMode ? '' : this.translate.instant('alarm-rule.alarm-rules'); - this.detailsPanelEnabled = false; + this.detailsPanelEnabled = this.pageMode; + this.entityResources = entityTypeResources.get(EntityType.CALCULATED_FIELD); this.entityType = EntityType.CALCULATED_FIELD; this.entityTranslations = { type: 'alarm-rule.alarm-rule', typePlural: 'alarm-rule.alarm-rules', list: 'alarm-rule.list', add: 'action.add', + details: 'alarm-rule.details', noEntities: 'alarm-rule.no-found', search: 'action.search', selectedEntities: 'alarm-rule.selected-fields' @@ -121,11 +124,17 @@ export class AlarmRulesTableConfig extends EntityTableConfig this.fetchCalculatedFields(pageLink); this.addEntity = this.getCalculatedAlarmDialog.bind(this); + this.loadEntity = id => this.calculatedFieldsService.getCalculatedFieldById(id.id); + this.saveEntity = (alarmRule) => this.calculatedFieldsService.saveCalculatedField(alarmRule); + this.deleteEntityTitle = (field) => this.translate.instant('alarm-rule.delete-title', {title: field.name}); this.deleteEntityContent = () => this.translate.instant('alarm-rule.delete-text'); this.deleteEntitiesTitle = count => this.translate.instant('alarm-rule.delete-multiple-title', {count}); this.deleteEntitiesContent = () => this.translate.instant('alarm-rule.delete-multiple-text'); this.deleteEntity = id => this.calculatedFieldsService.deleteCalculatedField(id.id); + + this.onEntityAction = action => this.onCFAction(action); + this.addActionDescriptors = [ { name: this.translate.instant('alarm-rule.create'), @@ -186,14 +195,18 @@ export class AlarmRulesTableConfig extends EntityTableConfig true, iconFunction: ({ debugSettings }) => this.entityDebugSettingsService.isDebugActive(debugSettings?.allEnabledUntil) || debugSettings?.failuresEnabled ? 'mdi:bug' : 'mdi:bug-outline', onAction: ($event, entity) => this.onOpenDebugConfig($event, entity), - }, - { - name: this.translate.instant('action.edit'), - icon: 'edit', - isEnabled: () => true, - onAction: ($event, entity) => this.editCalculatedField($event, entity), } ); + if (!this.pageMode) { + this.cellActionDescriptors.push( + { + name: this.translate.instant('action.edit'), + icon: 'edit', + isEnabled: () => true, + onAction: ($event, entity) => this.editCalculatedField($event, entity), + } + ) + } } fetchCalculatedFields(pageLink: PageLink): Observable> { @@ -384,4 +397,22 @@ export class AlarmRulesTableConfig extends EntityTableConfig): boolean { + switch (action.action) { + case 'open': + this.openCalculatedField(action.event, action.entity); + return true; + case 'export': + this.exportAlarmRule(action.event, action.entity); + return true; + } + return false; + } } diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table.component.ts b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table.component.ts index 7f4c0b4ee7..aaddf94aac 100644 --- a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table.component.ts @@ -36,7 +36,7 @@ import { EntityDebugSettingsService } from '@home/components/entity/debug/entity import { DatePipe } from '@angular/common'; import { AlarmRulesTableConfig } from "@home/components/alarm-rules/alarm-rules-table-config"; import { UtilsService } from "@core/services/utils.service"; -import { ActivatedRoute } from "@angular/router"; +import { ActivatedRoute, Router } from "@angular/router"; @Component({ selector: 'tb-alarm-rules-table', @@ -70,6 +70,7 @@ export class AlarmRulesTableComponent { private utilsService: UtilsService, private destroyRef: DestroyRef, private route: ActivatedRoute, + private router: Router ) { this.pageMode = !!this.route.snapshot.data.isPage; effect(() => { @@ -88,6 +89,7 @@ export class AlarmRulesTableComponent { this.importExportService, this.entityDebugSettingsService, this.utilsService, + this.router, this.pageMode, ); this.cd.markForCheck(); diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules.component.html b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules.component.html new file mode 100644 index 0000000000..e304a78732 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules.component.html @@ -0,0 +1,157 @@ + +
+ + + +
+
+
+
+
{{ 'common.general' | translate }}
+
+ + {{ 'entity-field.title' | translate }} + + @if (entityForm.get('name').errors && entityForm.get('name').touched) { + + @if (entityForm.get('name').hasError('required')) { + {{ 'common.hint.title-required' | translate }} + } @else if (entityForm.get('name').hasError('pattern')) { + {{ 'common.hint.title-pattern' | translate }} + } @else if (entityForm.get('name').hasError('maxlength')) { + {{ 'common.hint.title-max-length' | translate }} + } + + } + + + +
+ + +
+ +
+
{{ 'calculated-fields.arguments' | translate }}
+ +
+
+
{{ 'alarm-rule.create-conditions' | translate }}
+
+ + +
+
+
+
{{ 'alarm-rule.clear-condition' | translate }}
+
+
+ + +
+ +
+
+ alarm-rule.no-clear-alarm-rule +
+
+ +
+
+
+ + + {{ 'alarm-rule.advanced-settings' | translate }} + + +
+
+ + {{ 'alarm-rule.propagate-alarm' | translate }} + +
+ @if (configFormGroup.get('propagate').value) { + + + } +
+
+ + {{ 'alarm-rule.propagate-alarm-to-owner' | translate }} + +
+
+ + {{ 'alarm-rule.propagate-alarm-to-tenant' | translate }} + +
+
+
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules.component.ts b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules.component.ts new file mode 100644 index 0000000000..200cf09741 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules.component.ts @@ -0,0 +1,199 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { ChangeDetectorRef, Component, DestroyRef, Inject, Input } from '@angular/core'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { EntityComponent } from '../../components/entity/entity.component'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { EntityType } from '@shared/models/entity-type.models'; +import { TranslateService } from '@ngx-translate/core'; +import { + CalculatedFieldArgument, + CalculatedFieldConfiguration, + CalculatedFieldInfo, + calculatedFieldsEntityTypeList, + CalculatedFieldType +} from '@shared/models/calculated-field.models'; +import { oneSpaceInsideRegex } from '@shared/models/regex.constants'; +import { EntityId } from '@shared/models/id/entity-id'; +import { switchMap } from 'rxjs/operators'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { BaseData } from '@shared/models/base-data'; +import { Observable } from 'rxjs'; +import { CalculatedFieldsService } from '@core/http/calculated-fields.service'; +import { getCurrentAuthUser } from '@core/auth/auth.selectors'; +import { + CalculatedFieldsTableConfig, + CalculatedFieldsTableEntity +} from '@home/components/calculated-fields/calculated-fields-table-config'; +import { TenantId } from '@shared/models/id/tenant-id'; +import { StringItemsOption } from '@shared/components/string-items-list.component'; +import { RelationTypes } from '@shared/models/relation.models'; +import { AlarmRule, AlarmRuleConditionType, AlarmRuleExpressionType } from '@shared/models/alarm-rule.models'; + +@Component({ + selector: 'tb-alarm-rules', + templateUrl: './alarm-rules.component.html', + styleUrls: [] +}) +export class AlarmRulesComponent extends EntityComponent { + + @Input() + standalone = false; + + @Input() + entityName: string; + + readonly tenantId = getCurrentAuthUser(this.store).tenantId; + readonly ownerId = new TenantId(getCurrentAuthUser(this.store).tenantId); + readonly EntityType = EntityType; + readonly calculatedFieldsEntityTypeList = calculatedFieldsEntityTypeList; + readonly CalculatedFieldType = CalculatedFieldType; + + constructor(protected store: Store, + protected translate: TranslateService, + @Inject('entity') protected entityValue: CalculatedFieldInfo, + @Inject('entitiesTableConfig') protected entitiesTableConfigValue: CalculatedFieldsTableConfig, + protected fb: FormBuilder, + protected cd: ChangeDetectorRef, + private destroyRef: DestroyRef, + private calculatedFieldsService: CalculatedFieldsService) { + super(store, fb, entityValue, entitiesTableConfigValue, cd); + } + + hideDelete() { + if (this.entitiesTableConfig) { + return !this.entitiesTableConfig.deleteEnabled(this.entity); + } else { + return false; + } + } + + additionalDebugActionConfig = { + ...this.entitiesTableConfig.additionalDebugActionConfig, + action: () => this.entitiesTableConfig.additionalDebugActionConfig.action( + { id: this.entity.id, ...this.entityFormValue() }, false, + (expression) => { + if (expression) { + this.entityForm.get('configuration').setValue({...this.entityFormValue().configuration, expression}); + this.entityForm.get('configuration').markAsDirty(); + } + }), + }; + + get entityId(): EntityId { + return this.entityForm.get('entityId').value; + } + + get entitiesTableConfig(): CalculatedFieldsTableConfig { + return this.entitiesTableConfigValue; + } + + changeEntity(entity: BaseData): void { + this.entityName = entity?.name; + } + + buildForm(entity?: CalculatedFieldInfo): FormGroup { + const form = this.fb.group({ + name: ['', [Validators.required, Validators.pattern(oneSpaceInsideRegex), Validators.maxLength(255)]], + entityId: [null], + type: [CalculatedFieldType.ALARM], + debugSettings: [], + configuration: this.fb.group({ + type: [CalculatedFieldType.ALARM], + arguments: this.fb.control({}, Validators.required), + propagate: [false], + propagateToOwner: [false], + propagateToTenant: [false], + propagateRelationTypes: [null], + createRules: [null, Validators.required], + clearRule: [null], + }), + }); + return form; + } + + updateForm(entity: CalculatedFieldInfo) { + const { configuration = {} as CalculatedFieldConfiguration, type = CalculatedFieldType.ALARM, debugSettings = { failuresEnabled: true, allEnabled: true }, entityId = this.entityId, ...value } = entity ?? {}; + setTimeout(() => { + this.entityForm.patchValue({ configuration, debugSettings, entityId, ...value }, {emitEvent: false}); + }); + if (!entityId) { + this.entityForm.get('configuration').disable({emitEvent: false}); + } + } + + onTestScript(expression?: string): Observable { + const calculatedFieldId = this.entity?.id?.id; + if (calculatedFieldId) { + return this.calculatedFieldsService.getLatestCalculatedFieldDebugEvent(calculatedFieldId, {ignoreLoading: true}) + .pipe( + switchMap(event => { + const args = event?.arguments ? JSON.parse(event.arguments) : null; + return this.entitiesTableConfig.getTestScriptDialog(this.entityFormValue(), args, false, expression); + }), + takeUntilDestroyed(this.destroyRef) + ) + } + + return this.entitiesTableConfig.getTestScriptDialog(this.entityFormValue(), null, false, expression); + } + + updateFormState() { + if (this.entityForm) { + if (this.isEditValue) { + this.entityForm.enable({emitEvent: false}); + this.entityForm.get('entityId').disable({emitEvent: false}); + } else { + this.entityForm.disable({emitEvent: false}); + } + } + } + + get arguments(): Record { + return this.entityForm.get('configuration.arguments').value; + } + + get predefinedTypeValues(): StringItemsOption[] { + return RelationTypes.map(type => ({ + name: type, + value: type + })); + } + + get configFormGroup(): FormGroup { + return this.entityForm.get('configuration') as FormGroup; + } + + public removeClearAlarmRule() { + this.configFormGroup.patchValue({clearRule: null}); + this.entityForm.markAsDirty(); + } + + public addClearAlarmRule() { + const clearAlarmRule: AlarmRule = { + condition: { + type: AlarmRuleConditionType.SIMPLE, + expression: { + type: AlarmRuleExpressionType.SIMPLE + } + } + }; + this.configFormGroup.patchValue({clearRule: clearAlarmRule}); + } + +} diff --git a/ui-ngx/src/app/modules/home/pages/alarm/alarm-routing.module.ts b/ui-ngx/src/app/modules/home/pages/alarm/alarm-routing.module.ts index 1154fc83b6..00585ffb54 100644 --- a/ui-ngx/src/app/modules/home/pages/alarm/alarm-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/alarm/alarm-routing.module.ts @@ -14,14 +14,64 @@ /// limitations under the License. /// -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { DestroyRef, inject, NgModule } from '@angular/core'; +import { ActivatedRouteSnapshot, ResolveFn, Router, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { Authority } from '@shared/models/authority.enum'; import { AlarmTableComponent } from '@home/components/alarm/alarm-table.component'; import { AlarmsMode } from '@shared/models/alarm.models'; import { MenuId } from '@core/services/menu.models'; import { RouterTabsComponent } from "@home/components/router-tabs.component"; import { AlarmRulesTableComponent } from "@home/components/alarm-rules/alarm-rules-table.component"; +import { CalculatedFieldsTableComponent } from '@home/components/calculated-fields/calculated-fields-table.component'; +import { EntityDetailsPageComponent } from '@home/components/entity/entity-details-page.component'; +import { ConfirmOnExitGuard } from '@core/guards/confirm-on-exit.guard'; +import { entityDetailsPageBreadcrumbLabelFunction } from '@home/pages/home-pages.models'; +import { BreadCrumbConfig } from '@shared/components/breadcrumb'; +import { CalculatedFieldsTableConfigResolver } from '@home/pages/calculated-fields/calculated-fields-routing.module'; +import { CalculatedFieldsTableConfig } from '@home/components/calculated-fields/calculated-fields-table-config'; +import { CalculatedFieldsService } from '@core/http/calculated-fields.service'; +import { TranslateService } from '@ngx-translate/core'; +import { MatDialog } from '@angular/material/dialog'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { DatePipe } from '@angular/common'; +import { ImportExportService } from '@shared/import-export/import-export.service'; +import { EntityDebugSettingsService } from '@home/components/entity/debug/entity-debug-settings.service'; +import { UtilsService } from '@core/services/utils.service'; +import { AlarmRulesTableConfig } from '@home/components/alarm-rules/alarm-rules-table-config'; + +export const AlarmRulesTableConfigResolver: ResolveFn = + (_route: ActivatedRouteSnapshot, + _state: RouterStateSnapshot, + calculatedFieldsService = inject(CalculatedFieldsService), + translate = inject(TranslateService), + dialog = inject(MatDialog), + store = inject(Store), + datePipe = inject(DatePipe), + destroyRef = inject(DestroyRef), + importExportService = inject(ImportExportService), + entityDebugSettingsService = inject(EntityDebugSettingsService), + utilsService = inject(UtilsService), + router = inject(Router), + ) => { + return new AlarmRulesTableConfig( + calculatedFieldsService, + translate, + dialog, + datePipe, + null, + store, + destroyRef, + null, + null, + null, + importExportService, + entityDebugSettingsService, + utilsService, + router, + true, + ); + }; const routes: Routes = [ { @@ -57,15 +107,38 @@ const routes: Routes = [ }, { path: 'alarm-rules', - component: AlarmRulesTableComponent, data: { - auth: [Authority.TENANT_ADMIN], - title: 'alarm-rule.alarm-rules', breadcrumb: { menuId: MenuId.alarm_rules }, - isPage: true, - } + }, + children: [ + { + path: '', + component: AlarmRulesTableComponent, + data: { + auth: [Authority.TENANT_ADMIN], + title: 'alarm-rule.alarm-rules', + isPage: true, + } + }, + { + path: ':entityId', + component: EntityDetailsPageComponent, + canDeactivate: [ConfirmOnExitGuard], + data: { + breadcrumb: { + labelFunction: entityDetailsPageBreadcrumbLabelFunction, + icon: 'tune' + } as BreadCrumbConfig, + auth: [Authority.TENANT_ADMIN], + title: 'entity.type-calculated-fields', + }, + resolve: { + entitiesTableConfig: AlarmRulesTableConfigResolver + } + } + ] } ] } diff --git a/ui-ngx/src/app/modules/home/pages/alarm/alarm-rules-tabs.component.html b/ui-ngx/src/app/modules/home/pages/alarm/alarm-rules-tabs.component.html new file mode 100644 index 0000000000..7cabcbf5b8 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/alarm/alarm-rules-tabs.component.html @@ -0,0 +1,30 @@ + +@if (entity) { + + + +} diff --git a/ui-ngx/src/app/modules/home/pages/alarm/alarm-rules-tabs.component.ts b/ui-ngx/src/app/modules/home/pages/alarm/alarm-rules-tabs.component.ts new file mode 100644 index 0000000000..c239cfbd4d --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/alarm/alarm-rules-tabs.component.ts @@ -0,0 +1,49 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component } from '@angular/core'; +import { EntityTabsComponent } from '../../components/entity/entity-tabs.component'; +import { CalculatedFieldEventBody, DebugEventType, EventType } from '@shared/models/event.models'; +import type { + CalculatedFieldsTableConfig, + CalculatedFieldsTableEntity +} from '@home/components/calculated-fields/calculated-fields-table-config'; +import { debugCfActionEnabled } from '@shared/models/calculated-field.models'; + +@Component({ + selector: 'tb-alarm-rules-tabs', + templateUrl: './alarm-rules-tabs.component.html', + styleUrls: [] +}) +export class AlarmRulesTabsComponent extends EntityTabsComponent { + + readonly DebugEventType = DebugEventType; + readonly EventType = EventType; + + constructor() { + super(); + } + + get debugActionDisabled(): boolean { + return !debugCfActionEnabled(this.entity); + }; + + onDebugEventSelected(event: CalculatedFieldEventBody) { + (this.entitiesTableConfig as CalculatedFieldsTableConfig).getTestScriptDialog(this.entity, JSON.parse(event.arguments)) + .subscribe((expression) => { + }); + }; +} diff --git a/ui-ngx/src/app/modules/home/pages/alarm/alarm.module.ts b/ui-ngx/src/app/modules/home/pages/alarm/alarm.module.ts index 312259b217..500c6bf47b 100644 --- a/ui-ngx/src/app/modules/home/pages/alarm/alarm.module.ts +++ b/ui-ngx/src/app/modules/home/pages/alarm/alarm.module.ts @@ -20,9 +20,12 @@ import { SharedModule } from '@shared/shared.module'; import { HomeDialogsModule } from '../../dialogs/home-dialogs.module'; import { HomeComponentsModule } from '@modules/home/components/home-components.module'; import { AlarmRoutingModule } from '@home/pages/alarm/alarm-routing.module'; +import { AlarmRulesTabsComponent } from '@home/pages/alarm/alarm-rules-tabs.component'; @NgModule({ - declarations: [], + declarations: [ + AlarmRulesTabsComponent + ], imports: [ CommonModule, SharedModule, diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 8c2ecc2890..38f72b3206 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -1394,6 +1394,7 @@ "create": "Create new alarm rule", "add": "Add alarm rule", "copy": "Copy alarm rule configuration", + "details": "Alarm rule details", "no-found": "No alarm rules found", "list": "{ count, plural, =1 {One alarm rule} other {List of # alarm rules} }", "selected-fields": "{ count, plural, =1 {1 alarm rule} other {# alarm rules} } selected", From 0a58ff5e771fa1cea99dec9ddbf8ce9178ed8359 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 26 Dec 2025 14:17:19 +0200 Subject: [PATCH 33/47] UI: Improved alarm rules detail panel/page --- .../calculated-field-form.service.ts | 22 ++++++- .../alarm-rule-dialog.component.html | 33 +++------- .../alarm-rule-dialog.component.ts | 63 +++++-------------- .../alarm-rules/alarm-rules-table-config.ts | 23 ++++++- .../alarm-rules/alarm-rules.component.html | 1 + .../alarm-rules/alarm-rules.component.ts | 60 ++++++------------ .../cf-alarm-rule-condition.component.html | 4 +- .../cf-alarm-rule-condition.component.ts | 16 +++-- .../create-cf-alarm-rules.component.ts | 6 +- .../calculated-field.component.html | 11 +--- .../calculated-field.component.ts | 4 +- .../calculated-field-dialog.component.html | 1 + .../calculated-field-dialog.component.ts | 2 +- .../app/shared/models/alarm-rule.models.ts | 8 ++- 14 files changed, 109 insertions(+), 145 deletions(-) rename ui-ngx/src/app/{modules/home/components/calculated-fields => core/services}/calculated-field-form.service.ts (84%) diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field-form.service.ts b/ui-ngx/src/app/core/services/calculated-field-form.service.ts similarity index 84% rename from ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field-form.service.ts rename to ui-ngx/src/app/core/services/calculated-field-form.service.ts index 6ef02f0cf8..cc3e495c17 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field-form.service.ts +++ b/ui-ngx/src/app/core/services/calculated-field-form.service.ts @@ -30,7 +30,6 @@ import { isDefined } from '@core/utils'; import { CalculatedFieldsService } from '@core/http/calculated-fields.service'; import { CalculatedFieldsTableEntity } from '@home/components/calculated-fields/calculated-fields-table-config'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -import { EntityType } from '@shared/models/entity-type.models'; @Injectable({ providedIn: 'root' }) export class CalculatedFieldFormService { @@ -40,13 +39,32 @@ export class CalculatedFieldFormService { buildForm(): FormGroup { return this.fb.group({ name: ['', [Validators.required, Validators.pattern(oneSpaceInsideRegex), Validators.maxLength(255)]], - entityId: [{type: EntityType.DEVICE_PROFILE, id: null}, Validators.required], + entityId: [null, Validators.required], type: [CalculatedFieldType.SIMPLE], debugSettings: [], configuration: this.fb.control({} as CalculatedFieldConfiguration), }); } + buildAlarmRuleForm(): FormGroup { + return this.fb.group({ + name: ['', [Validators.required, Validators.pattern(oneSpaceInsideRegex), Validators.maxLength(255)]], + entityId: [null, Validators.required], + type: [CalculatedFieldType.ALARM], + debugSettings: [], + configuration: this.fb.group({ + type: [CalculatedFieldType.ALARM], + arguments: this.fb.control({}, Validators.required), + propagate: [false], + propagateToOwner: [false], + propagateToTenant: [false], + propagateRelationTypes: [null], + createRules: [null, Validators.required], + clearRule: [null], + }), + }); + } + setupTypeChange(form: FormGroup, destroyRef: DestroyRef, isEditActive?: () => boolean): void { form.get('type').valueChanges.pipe( pairwise(), diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-dialog.component.html b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-dialog.component.html index 869b707d98..1b116d0696 100644 --- a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-dialog.component.html @@ -58,31 +58,14 @@ />
@if (!data.entityId) { -
- - - @if (fieldFormGroup.get('entityId.entityType').value) { - - } -
+ + }
diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-dialog.component.ts b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-dialog.component.ts index a84aa52693..854f7fb1a0 100644 --- a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-dialog.component.ts @@ -14,16 +14,15 @@ /// limitations under the License. /// -import { Component, DestroyRef, Inject, ViewChild, ViewEncapsulation } from '@angular/core'; +import { Component, DestroyRef, Inject, ViewEncapsulation } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; -import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { FormGroup } from '@angular/forms'; import { Router } from '@angular/router'; import { DialogComponent } from '@shared/components/dialog.component'; import { CalculatedField, CalculatedFieldArgument, CalculatedFieldType } from '@shared/models/calculated-field.models'; -import { oneSpaceInsideRegex } from '@shared/models/regex.constants'; -import { AliasEntityType, EntityType, entityTypeTranslations } from '@shared/models/entity-type.models'; +import { EntityType, entityTypeTranslations } from '@shared/models/entity-type.models'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { ScriptLanguage } from '@shared/models/rule-node.models'; import { CalculatedFieldsService } from '@core/http/calculated-fields.service'; @@ -39,13 +38,11 @@ import { } from "@shared/models/alarm-rule.models"; import { deepTrim } from "@core/utils"; import { combineLatest, Observable } from "rxjs"; -import { debounceTime, startWith, switchMap } from "rxjs/operators"; -import { EntityTypeSelectComponent } from "@shared/components/entity/entity-type-select.component"; -import { EntityAutocompleteComponent } from "@shared/components/entity/entity-autocomplete.component"; -import { EntityService } from "@core/http/entity.service"; +import { debounceTime, startWith } from "rxjs/operators"; import { RelationTypes } from "@shared/models/relation.models"; import { StringItemsOption } from "@shared/components/string-items-list.component"; import { BaseData } from "@shared/models/base-data"; +import { CalculatedFieldFormService } from '@core/services/calculated-field-form.service'; export interface AlarmRuleDialogData { value?: CalculatedField; @@ -67,24 +64,7 @@ export interface AlarmRuleDialogData { }) export class AlarmRuleDialogComponent extends DialogComponent { - fieldFormGroup = this.fb.group({ - name: ['', [Validators.required, Validators.pattern(oneSpaceInsideRegex), Validators.maxLength(255)]], - type: [CalculatedFieldType.ALARM], - debugSettings: [], - entityId: this.fb.group({ - entityType: this.fb.control(EntityType.DEVICE_PROFILE, Validators.required), - id: [null as null | string, Validators.required], - }), - configuration: this.fb.group({ - arguments: this.fb.control({}, Validators.required), - propagate: [false], - propagateToOwner: [false], - propagateToTenant: [false], - propagateRelationTypes: [null], - createRules: [null, Validators.required], - clearRule: [null], - }), - }); + fieldFormGroup: FormGroup ; additionalDebugActionConfig = this.data.value?.id ? { ...this.data.additionalDebugActionConfig, @@ -105,18 +85,15 @@ export class AlarmRuleDialogComponent extends DialogComponent, protected router: Router, @Inject(MAT_DIALOG_DATA) public data: AlarmRuleDialogData, protected dialogRef: MatDialogRef, private calculatedFieldsService: CalculatedFieldsService, - private entityService: EntityService, private destroyRef: DestroyRef, - private fb: FormBuilder) { + private cfFormService: CalculatedFieldFormService) { super(store, router, dialogRef); + this.fieldFormGroup = this.cfFormService.buildAlarmRuleForm(); this.applyDialogData(); this.updateRulesValidators(); @@ -132,7 +109,7 @@ export class AlarmRuleDialogComponent extends DialogComponent { - const calculatedFieldId = this.data.value?.id?.id; - if (calculatedFieldId) { - return this.calculatedFieldsService.getLatestCalculatedFieldDebugEvent(calculatedFieldId, {ignoreLoading: true}) - .pipe( - switchMap(event => { - const args = event?.arguments ? JSON.parse(event.arguments) : null; - return this.data.getTestScriptDialogFn(this.fromGroupValue, expression, args, false); - }), - takeUntilDestroyed(this.destroyRef) - ) - } - return this.data.getTestScriptDialogFn(this.fromGroupValue, expression, null, false); + return this.cfFormService.testScript( + this.data.value?.id?.id, + this.fromGroupValue, + this.data.getTestScriptDialogFn, + this.destroyRef, + expression + ); } private updateRulesValidators(): void { @@ -250,5 +220,4 @@ export class AlarmRuleDialogComponent extends DialogComponent): void { this.entityName = entity.name; } - } diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table-config.ts b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table-config.ts index b0ac04aacb..ad718408e4 100644 --- a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table-config.ts +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table-config.ts @@ -36,7 +36,7 @@ import { DestroyRef, Renderer2 } from '@angular/core'; import { EntityDebugSettings } from '@shared/models/entity.models'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { CalculatedFieldsService } from '@core/http/calculated-fields.service'; -import { catchError, filter, switchMap, tap } from 'rxjs/operators'; +import { catchError, filter, first, switchMap, tap } from 'rxjs/operators'; import { ArgumentEntityType, ArgumentType, @@ -186,7 +186,8 @@ export class AlarmRulesTableConfig extends EntityTableConfig true, - onAction: ($event, entity) => this.openDebugEventsDialog($event, entity), + onAction: ($event, entity) => + this.pageMode ? this.openDebugTab($event, entity) : this.openDebugEventsDialog($event, entity), }, { name: '', @@ -308,6 +309,22 @@ export class AlarmRulesTableConfig extends EntityTableConfig 1) { + table.entityDetailsPanel.matTabGroup.selectedIndex = 1; + } else { + table.entityDetailsPanel.matTabGroup._tabs.changes.pipe( + first() + ).subscribe(() => { + table.entityDetailsPanel.matTabGroup.selectedIndex = 1; + }) + } + } + } + private exportAlarmRule($event: Event, calculatedField: AlarmRuleTableEntity): void { $event?.stopPropagation(); this.importExportService.exportCalculatedField(calculatedField.id.id); @@ -361,7 +378,7 @@ export class AlarmRulesTableConfig extends EntityTableConfig this.updateData()); } - private getTestScriptDialog(calculatedField: AlarmRuleTableEntity, expression: string, argumentsObj?: CalculatedFieldEventArguments, openCalculatedFieldEdit = true): Observable { + private getTestScriptDialog(calculatedField: AlarmRuleTableEntity, argumentsObj?: CalculatedFieldEventArguments, openCalculatedFieldEdit = true, expression?: string): Observable { if (calculatedField.type === CalculatedFieldType.ALARM) { const resultArguments = Object.keys(calculatedField.configuration.arguments).reduce((acc, key) => { const type = calculatedField.configuration.arguments[key].refEntityKey.type; diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules.component.html b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules.component.html index e304a78732..28695d9283 100644 --- a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules.component.html +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules.component.html @@ -65,6 +65,7 @@ diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules.component.ts b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules.component.ts index 200cf09741..fb92f6c4fd 100644 --- a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules.component.ts +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules.component.ts @@ -14,11 +14,11 @@ /// limitations under the License. /// -import { ChangeDetectorRef, Component, DestroyRef, Inject, Input } from '@angular/core'; +import { ChangeDetectorRef, Component, DestroyRef, inject, Inject, Input } from '@angular/core'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; -import { EntityComponent } from '../../components/entity/entity.component'; -import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { EntityComponent } from '@home/components/entity/entity.component'; +import { FormBuilder, FormGroup } from '@angular/forms'; import { EntityType } from '@shared/models/entity-type.models'; import { TranslateService } from '@ngx-translate/core'; import { @@ -28,13 +28,9 @@ import { calculatedFieldsEntityTypeList, CalculatedFieldType } from '@shared/models/calculated-field.models'; -import { oneSpaceInsideRegex } from '@shared/models/regex.constants'; import { EntityId } from '@shared/models/id/entity-id'; -import { switchMap } from 'rxjs/operators'; -import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { BaseData } from '@shared/models/base-data'; import { Observable } from 'rxjs'; -import { CalculatedFieldsService } from '@core/http/calculated-fields.service'; import { getCurrentAuthUser } from '@core/auth/auth.selectors'; import { CalculatedFieldsTableConfig, @@ -44,6 +40,7 @@ import { TenantId } from '@shared/models/id/tenant-id'; import { StringItemsOption } from '@shared/components/string-items-list.component'; import { RelationTypes } from '@shared/models/relation.models'; import { AlarmRule, AlarmRuleConditionType, AlarmRuleExpressionType } from '@shared/models/alarm-rule.models'; +import { CalculatedFieldFormService } from '@core/services/calculated-field-form.service'; @Component({ selector: 'tb-alarm-rules', @@ -58,20 +55,21 @@ export class AlarmRulesComponent extends EntityComponent, protected translate: TranslateService, @Inject('entity') protected entityValue: CalculatedFieldInfo, @Inject('entitiesTableConfig') protected entitiesTableConfigValue: CalculatedFieldsTableConfig, protected fb: FormBuilder, - protected cd: ChangeDetectorRef, - private destroyRef: DestroyRef, - private calculatedFieldsService: CalculatedFieldsService) { + protected cd: ChangeDetectorRef) { super(store, fb, entityValue, entitiesTableConfigValue, cd); } @@ -107,24 +105,8 @@ export class AlarmRulesComponent extends EntityComponent { - const calculatedFieldId = this.entity?.id?.id; - if (calculatedFieldId) { - return this.calculatedFieldsService.getLatestCalculatedFieldDebugEvent(calculatedFieldId, {ignoreLoading: true}) - .pipe( - switchMap(event => { - const args = event?.arguments ? JSON.parse(event.arguments) : null; - return this.entitiesTableConfig.getTestScriptDialog(this.entityFormValue(), args, false, expression); - }), - takeUntilDestroyed(this.destroyRef) - ) - } - - return this.entitiesTableConfig.getTestScriptDialog(this.entityFormValue(), null, false, expression); + return this.cfFormService.testScript( + this.entity?.id?.id, + this.entityFormValue(), + this.entitiesTableConfig.getTestScriptDialog.bind(this.entitiesTableConfig), + this.destroyRef, + expression + ); } updateFormState() { diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule-condition.component.html b/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule-condition.component.html index 22fc01f9de..99ee0f9634 100644 --- a/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule-condition.component.html +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule-condition.component.html @@ -15,7 +15,7 @@ limitations under the License. --> -
+
{{ 'alarm-rule.condition' | translate }}
- - - - - - - - - -
@@ -75,6 +65,7 @@
diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.ts index 01cc53609d..65595fe9b8 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.ts @@ -38,7 +38,7 @@ import { CalculatedFieldsTableEntity } from '@home/components/calculated-fields/calculated-fields-table-config'; import { TenantId } from '@shared/models/id/tenant-id'; -import { CalculatedFieldFormService } from '@home/components/calculated-fields/calculated-field-form.service'; +import { CalculatedFieldFormService } from '@core/services/calculated-field-form.service'; @Component({ selector: 'tb-calculated-field', @@ -55,8 +55,8 @@ export class CalculatedFieldComponent extends EntityComponent 20;' -export type AlarmRuleTestScriptFn = (calculatedField: CalculatedField, expression: string, argumentsObj?: Record, closeAllOnSave?: boolean) => Observable; +export type AlarmRuleTestScriptFn = (calculatedField: CalculatedField, argumentsObj?: CalculatedFieldEventArguments, openCalculatedFieldEdit?: boolean, expression?: string) => Observable; export function checkPredicates(predicates: any[], validSet: Set): boolean { for (const predicate of predicates) { From 44f2b44d1af328a5b18f58be0a0f0bcc61a0be5d Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 26 Dec 2025 16:24:07 +0200 Subject: [PATCH 34/47] UI: Fixed not apply contidition in alarm rules and fixed style --- .../home/components/alarm-rules/cf-alarm-rule.component.html | 2 +- .../components/alarm-rules/create-cf-alarm-rules.component.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule.component.html b/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule.component.html index a7ccc25fe6..f48819da74 100644 --- a/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule.component.html +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule.component.html @@ -15,7 +15,7 @@ limitations under the License. --> -
+
diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/create-cf-alarm-rules.component.ts b/ui-ngx/src/app/modules/home/components/alarm-rules/create-cf-alarm-rules.component.ts index 15d9858817..d5ec210f34 100644 --- a/ui-ngx/src/app/modules/home/components/alarm-rules/create-cf-alarm-rules.component.ts +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/create-cf-alarm-rules.component.ts @@ -114,7 +114,7 @@ export class CreateCfAlarmRulesComponent implements ControlValueAccessor, Valida } createAlarmRulesControls.push(this.fb.group({ severity: [severity, Validators.required], - alarmRule: {value: [createAlarmRule, Validators.required], disabled: this.disabled} + alarmRule: [{value: createAlarmRule, disabled: this.disabled}, Validators.required] })); }); } From 9823c780761ca4b0b14120d9526f84199278b183 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 26 Dec 2025 17:04:37 +0200 Subject: [PATCH 35/47] UI: Fixed show label in select cf entity --- .../alarm-rules/alarm-rule-dialog.component.html | 1 + .../components/alarm-rules/alarm-rules.component.html | 1 + .../calculated-fields/calculated-field.component.html | 1 + .../dialog/calculated-field-dialog.component.html | 1 + .../components/entity/entity-select.component.html | 1 + .../components/entity/entity-select.component.ts | 3 +++ .../components/entity/entity-type-select.component.ts | 10 +++++++++- 7 files changed, 17 insertions(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-dialog.component.html b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-dialog.component.html index 1b116d0696..ed936cd60e 100644 --- a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-dialog.component.html @@ -61,6 +61,7 @@ diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.html b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.html index 7cea16c636..7d4b4b7920 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.html +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.html @@ -66,6 +66,7 @@ diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.html b/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.html index 78df90701f..293dacdca6 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.html @@ -59,6 +59,7 @@ >(); diff --git a/ui-ngx/src/app/shared/components/entity/entity-type-select.component.ts b/ui-ngx/src/app/shared/components/entity/entity-type-select.component.ts index fcfe158da0..dcabc49906 100644 --- a/ui-ngx/src/app/shared/components/entity/entity-type-select.component.ts +++ b/ui-ngx/src/app/shared/components/entity/entity-type-select.component.ts @@ -52,8 +52,16 @@ export class EntityTypeSelectComponent implements ControlValueAccessor, OnInit, @coerceBoolean() showLabel: boolean; + private labelValue = this.translate.instant('entity.type'); + + get label(): string { + return this.labelValue; + } + @Input() - label = this.translate.instant('entity.type'); + set label(value: string) { + this.labelValue = value ?? this.translate.instant('entity.type'); + } @Input() @coerceBoolean() From c4ce25b6bfd353d7dc7f80a814e52b40646b8b68 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 26 Dec 2025 17:34:53 +0200 Subject: [PATCH 36/47] UI: Disabled change CF type when import --- .../calculated-fields/calculated-fields-table-config.ts | 5 +++-- .../components/dialog/calculated-field-dialog.component.ts | 7 ++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table-config.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table-config.ts index e5e4063c21..e32bdc1188 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table-config.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table-config.ts @@ -253,7 +253,7 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig { + private getCalculatedFieldDialog(value?: CalculatedFieldsTableEntity, buttonTitle = 'action.add', isDirty = false, disabledSelectType = false): Observable { const entityId = this.entityId || value?.entityId; const entityName = this.entityName || (value as CalculatedFieldInfo)?.entityName; return this.dialog.open(CalculatedFieldDialogComponent, { @@ -269,6 +269,7 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig this.getCalculatedFieldDialog(this.updateImportedCalculatedField(calculatedField), 'action.add', true)), + switchMap(calculatedField => this.getCalculatedFieldDialog(this.updateImportedCalculatedField(calculatedField), 'action.add', true, true)), filter(Boolean), switchMap(calculatedField => this.calculatedFieldsService.saveCalculatedField(calculatedField)), filter(Boolean), diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.ts index 28a028eacc..ee38fb2a2d 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.ts @@ -50,6 +50,7 @@ export interface CalculatedFieldDialogData { additionalDebugActionConfig: AdditionalDebugActionConfig<(calculatedField: CalculatedField) => void>; getTestScriptDialogFn: CalculatedFieldTestScriptFn; isDirty?: boolean; + disabledSelectType?: boolean; } @Component({ @@ -106,10 +107,14 @@ export class CalculatedFieldDialogComponent extends DialogComponent Date: Fri, 26 Dec 2025 17:42:16 +0200 Subject: [PATCH 37/47] UI: Time series data aggregation CF remove 'Count unique' aggregation function --- .../metrics/calculated-field-metrics-panel.component.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/metrics/calculated-field-metrics-panel.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/metrics/calculated-field-metrics-panel.component.ts index 3d5a53ea4e..811f7b87fa 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/metrics/calculated-field-metrics-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/metrics/calculated-field-metrics-panel.component.ts @@ -74,7 +74,7 @@ export class CalculatedFieldMetricsPanelComponent implements OnInit { entityFilter: EntityFilter; - readonly AggFunctions = Object.values(AggFunction) as AggFunction[]; + AggFunctions = Object.values(AggFunction) as AggFunction[]; readonly AggFunctionTranslations = AggFunctionTranslations; readonly ScriptLanguage = ScriptLanguage; readonly AggInputType = AggInputType; @@ -103,6 +103,10 @@ export class CalculatedFieldMetricsPanelComponent implements OnInit { this.validateInputKey(); this.functionArgs = ['ctx', ...this.arguments]; + + if (this.simpleMode) { + this.AggFunctions = this.AggFunctions.filter(aggFunc => aggFunc !== AggFunction.COUNT_UNIQUE); + } } saveMetric(): void { From 1840401b29824aa16ef82e3f50b46d673e7d6605 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 26 Dec 2025 17:52:57 +0200 Subject: [PATCH 38/47] UI: Add show title in CF page and minor bug show required --- .../calculated-fields/calculated-fields-table-config.ts | 2 +- ...lculated-field-geofencing-zone-groups-panel.component.html | 4 +++- ui-ngx/src/assets/locale/locale.constant-en_US.json | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table-config.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table-config.ts index e32bdc1188..e09949d2c6 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table-config.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table-config.ts @@ -111,7 +111,7 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig
- {{ 'calculated-fields.entity-zone-relationship' | translate }} + + {{ 'calculated-fields.entity-zone-relationship' | translate }} +
diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 38f72b3206..259008791e 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -1183,7 +1183,7 @@ "perimeter-attribute-key": "Perimeter attribute key", "perimeter-attribute-key-required": "Perimeter attribute key is required.", "perimeter-attribute-key-pattern": "Perimeter attribute key is invalid.", - "entity-zone-relationship": "Path from Entity to Zones *", + "entity-zone-relationship": "Path from Entity to Zones", "direction": "Relation direction", "direction-from": "From entity to zone", "direction-to": "From zone to entity", From 3a647f00379a3fc398d2de5d99ab49652dfea703 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 26 Dec 2025 18:56:17 +0200 Subject: [PATCH 39/47] UI: Optimize watchKeyChange in CF arguments --- .../calculated-field-argument-panel.component.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-argument-panel.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-argument-panel.component.ts index 38855ec79e..51d14c9478 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-argument-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-argument-panel.component.ts @@ -259,7 +259,11 @@ export class CalculatedFieldArgumentPanelComponent implements OnInit, AfterViewI }; } if (!onInit) { - this.argumentFormGroup.get('refEntityKey').get('key').setValue(''); + this.argumentFormGroup.get('refEntityKey').get('key').setValue('', {emitEvents: !this.watchKeyChange}); + if (this.watchKeyChange && this.argumentFormGroup.get('argumentName').pristine) { + this.argumentFormGroup.get('argumentName').markAsUntouched({emitEvent: false}); + this.argumentFormGroup.get('argumentName').setValue('', {emitEvent: false}); + } } else if (this.predefinedEntityFilter) { entityFilter = this.predefinedEntityFilter; } From 595b52d7b279d0f754dae69ea19024865d0deb7d Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 26 Dec 2025 19:01:46 +0200 Subject: [PATCH 40/47] UI: Fixed cf argument validation --- .../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/calculated-field-arguments/calculated-field-arguments-table.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-arguments-table.component.ts index 983609dfa7..a05326699d 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-arguments-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-arguments-table.component.ts @@ -154,7 +154,7 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces validate(): ValidationErrors | null { this.updateErrorText(); - return this.errorText ? { argumentsFormArray: false } : null; + return this.errorText || !this.argumentsFormArray.controls.length ? { argumentsFormArray: false } : null; } setDisabledState(isDisabled: boolean): void { From bd4a450326200a4e1e5b1b1f57fb402075a094e1 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Mon, 29 Dec 2025 11:23:51 +0200 Subject: [PATCH 41/47] Use 3.8-stable tb-gateway image --- application/src/main/resources/thingsboard.yml | 2 +- .../server/controller/DeviceConnectivityControllerTest.java | 2 +- .../server/dao/device/DeviceConnectivityServiceImpl.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 787111c28e..63c461dd7c 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -1467,7 +1467,7 @@ device: pem_cert_file: "${DEVICE_CONNECTIVITY_COAPS_CA_ROOT_CERT:cafile.pem}" gateway: # The docker tag for thingsboard/tb-gateway image used in docker-compose file for gateway launch - image_version: "${DEVICE_CONNECTIVITY_GATEWAY_IMAGE_VERSION:3.7-stable}" + image_version: "${DEVICE_CONNECTIVITY_GATEWAY_IMAGE_VERSION:3.8-stable}" # Edges parameters edges: diff --git a/application/src/test/java/org/thingsboard/server/controller/DeviceConnectivityControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/DeviceConnectivityControllerTest.java index 724b1c622f..2213bb731c 100644 --- a/application/src/test/java/org/thingsboard/server/controller/DeviceConnectivityControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/DeviceConnectivityControllerTest.java @@ -95,7 +95,7 @@ public class DeviceConnectivityControllerTest extends AbstractControllerTest { private DeviceProfileId mqttDeviceProfileId; private DeviceProfileId coapDeviceProfileId; - @Value("${device.connectivity.gateway.image_version:3.7-stable}") + @Value("${device.connectivity.gateway.image_version:3.8-stable}") private String gatewayImageVersion; @Before diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityServiceImpl.java index 6f22e95c87..e4f2796d3b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityServiceImpl.java @@ -85,7 +85,7 @@ public class DeviceConnectivityServiceImpl implements DeviceConnectivityService private String mqttsPemCertFile; @Value("${device.connectivity.coaps.pem_cert_file:}") private String coapsPemCertFile; - @Value("${device.connectivity.gateway.image_version:3.7-stable}") + @Value("${device.connectivity.gateway.image_version:3.8-stable}") private String gatewayImageVersion; @Override From 00ab73f114d1bb042f4e4913d4d680354aec7db7 Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Mon, 29 Dec 2025 11:34:06 +0200 Subject: [PATCH 42/47] passed ctx as param to updateEntry --- ...CalculatedFieldEntityMessageProcessor.java | 15 +++++---- ...tractCalculatedFieldProcessingService.java | 6 ++-- .../service/cf/ctx/state/ArgumentEntry.java | 2 +- .../ctx/state/BaseCalculatedFieldState.java | 8 ++--- .../ctx/state/SingleValueArgumentEntry.java | 2 +- .../cf/ctx/state/TsRollingArgumentEntry.java | 2 +- .../RelatedEntitiesArgumentEntry.java | 5 +-- .../EntityAggregationArgumentEntry.java | 28 ++++++---------- ...EntityAggregationCalculatedFieldState.java | 9 ----- .../alarm/AlarmCalculatedFieldState.java | 6 ++-- .../geofencing/GeofencingArgumentEntry.java | 3 +- .../propagation/PropagationArgumentEntry.java | 3 +- .../utils/CalculatedFieldArgumentUtils.java | 4 +-- .../GeofencingValueArgumentEntryTest.java | 25 +++++++++----- .../state/PropagationArgumentEntryTest.java | 33 +++++++++++-------- .../RelatedEntitiesArgumentEntryTest.java | 15 ++++++--- .../state/SingleValueArgumentEntryTest.java | 23 ++++++++----- .../ctx/state/TsRollingArgumentEntryTest.java | 17 +++++++--- 18 files changed, 114 insertions(+), 92 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldEntityMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldEntityMessageProcessor.java index d2ab4c85ac..a24c5fb732 100644 --- a/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldEntityMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldEntityMessageProcessor.java @@ -572,24 +572,24 @@ public class CalculatedFieldEntityMessageProcessor extends AbstractContextAwareM } private Map mapToArguments(CalculatedFieldCtx ctx, List data) { - return mapToArguments(entityId, ctx.getMainEntityArguments(), Collections.emptyMap(), data); + return mapToArguments(entityId, ctx, ctx.getMainEntityArguments(), Collections.emptyMap(), data); } private Map mapToArguments(CalculatedFieldCtx ctx, EntityId entityId, List data) { - return mapToArguments(entityId, ctx.getLinkedAndDynamicArgs(entityId), ctx.getRelatedEntityArguments(), data); + return mapToArguments(entityId, ctx, ctx.getLinkedAndDynamicArgs(entityId), ctx.getRelatedEntityArguments(), data); } - private Map mapToArguments(EntityId originator, Map> args, Map> relatedEntityArgs, List data) { + private Map mapToArguments(EntityId originator, CalculatedFieldCtx ctx, Map> args, Map> relatedEntityArgs, List data) { Map arguments = new HashMap<>(); if (!relatedEntityArgs.isEmpty() || !args.isEmpty()) { for (TsKvProto item : data) { ReferencedEntityKey key = new ReferencedEntityKey(item.getKv().getKey(), ArgumentType.TS_LATEST, null); SingleValueArgumentEntry relatedArgIncoming = new SingleValueArgumentEntry(originator, item); - mapLatest(relatedArgIncoming, relatedEntityArgs.get(key), arguments); + mapLatest(ctx, relatedArgIncoming, relatedEntityArgs.get(key), arguments); SingleValueArgumentEntry incoming = new SingleValueArgumentEntry(item); - mapLatest(incoming, args.get(key), arguments); + mapLatest(ctx, incoming, args.get(key), arguments); key = new ReferencedEntityKey(item.getKv().getKey(), ArgumentType.TS_ROLLING, null); mapRolling(item, args.get(key), arguments); @@ -598,7 +598,8 @@ public class CalculatedFieldEntityMessageProcessor extends AbstractContextAwareM return arguments; } - private void mapLatest(SingleValueArgumentEntry incoming, + private void mapLatest(CalculatedFieldCtx ctx, + SingleValueArgumentEntry incoming, Set argNames, Map arguments) { if (argNames != null) { @@ -606,7 +607,7 @@ public class CalculatedFieldEntityMessageProcessor extends AbstractContextAwareM if (existing == null) { return incoming; } - existing.updateEntry(incoming); + existing.updateEntry(incoming, ctx); return existing; })); } diff --git a/application/src/main/java/org/thingsboard/server/service/cf/AbstractCalculatedFieldProcessingService.java b/application/src/main/java/org/thingsboard/server/service/cf/AbstractCalculatedFieldProcessingService.java index cd88c93a71..ec645085e6 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/AbstractCalculatedFieldProcessingService.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/AbstractCalculatedFieldProcessingService.java @@ -233,7 +233,7 @@ public abstract class AbstractCalculatedFieldProcessingService { return config.getArguments().entrySet().stream() .collect(Collectors.toMap( Map.Entry::getKey, - entry -> fetchTimeSeries(ctx, entityId, entry.getValue(), config.getInterval(), ts) + entry -> fetchTimeSeries(ctx.getTenantId(), entityId, entry.getValue(), config.getInterval(), ts) )); } @@ -341,11 +341,11 @@ public abstract class AbstractCalculatedFieldProcessingService { return resolveArgumentValue(argKey, argumentEntryFut); } - private ListenableFuture fetchTimeSeries(CalculatedFieldCtx ctx, EntityId entityId, Argument argument, AggInterval interval, long queryEndTs) { + private ListenableFuture fetchTimeSeries(TenantId tenantId, EntityId entityId, Argument argument, AggInterval interval, long queryEndTs) { long intervalStartTs = interval.getCurrentIntervalStartTs(); long intervalEndTs = interval.getCurrentIntervalEndTs(); ReadTsKvQuery query = new BaseReadTsKvQuery(argument.getRefEntityKey().getKey(), intervalStartTs, queryEndTs, 0, 1, Aggregation.NONE); - return fetchTimeSeriesInternal(ctx.getTenantId(), entityId, query, timeSeries -> transformAggregationArgument(timeSeries, intervalStartTs, intervalEndTs, ctx)); + return fetchTimeSeriesInternal(tenantId, entityId, query, timeSeries -> transformAggregationArgument(timeSeries, intervalStartTs, intervalEndTs)); } private ListenableFuture fetchTsRolling(TenantId tenantId, EntityId entityId, Argument argument, long queryEndTs) { diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/ArgumentEntry.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/ArgumentEntry.java index dc23ffa979..fc4f8ce365 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/ArgumentEntry.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/ArgumentEntry.java @@ -52,7 +52,7 @@ public interface ArgumentEntry { Object getValue(); - boolean updateEntry(ArgumentEntry entry); + boolean updateEntry(ArgumentEntry entry, CalculatedFieldCtx ctx); boolean isEmpty(); diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/BaseCalculatedFieldState.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/BaseCalculatedFieldState.java index 09ca35cc4b..0f648600a5 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/BaseCalculatedFieldState.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/BaseCalculatedFieldState.java @@ -86,13 +86,13 @@ public abstract class BaseCalculatedFieldState implements CalculatedFieldState, validateNewEntry(key, newEntry); if (existingEntry instanceof RelatedEntitiesArgumentEntry || existingEntry instanceof EntityAggregationArgumentEntry) { - updateEntry(existingEntry, newEntry); + updateEntry(existingEntry, newEntry, ctx); } else { arguments.put(key, newEntry); } entryUpdated = true; } else { - entryUpdated = updateEntry(existingEntry, newEntry); + entryUpdated = updateEntry(existingEntry, newEntry, ctx); } if (entryUpdated) { @@ -111,8 +111,8 @@ public abstract class BaseCalculatedFieldState implements CalculatedFieldState, return updatedArguments; } - protected boolean updateEntry(ArgumentEntry existingEntry, ArgumentEntry newEntry) { - return existingEntry.updateEntry(newEntry); + protected boolean updateEntry(ArgumentEntry existingEntry, ArgumentEntry newEntry, CalculatedFieldCtx ctx) { + return existingEntry.updateEntry(newEntry, ctx); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SingleValueArgumentEntry.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SingleValueArgumentEntry.java index 4d0c4d7724..028c3c429b 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SingleValueArgumentEntry.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SingleValueArgumentEntry.java @@ -159,7 +159,7 @@ public class SingleValueArgumentEntry implements ArgumentEntry { } @Override - public boolean updateEntry(ArgumentEntry entry) { + public boolean updateEntry(ArgumentEntry entry, CalculatedFieldCtx ctx) { if (entry instanceof SingleValueArgumentEntry singleValueEntry) { if (singleValueEntry.getTs() < this.ts) { return false; diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/TsRollingArgumentEntry.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/TsRollingArgumentEntry.java index 8cdc9ddcf9..8abddb3d4a 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/TsRollingArgumentEntry.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/TsRollingArgumentEntry.java @@ -100,7 +100,7 @@ public class TsRollingArgumentEntry implements ArgumentEntry, HasLatestTs { } @Override - public boolean updateEntry(ArgumentEntry entry) { + public boolean updateEntry(ArgumentEntry entry, CalculatedFieldCtx ctx) { if (entry instanceof TsRollingArgumentEntry tsRollingEntry) { updateTsRollingEntry(tsRollingEntry); } else if (entry instanceof SingleValueArgumentEntry singleValueEntry) { diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/RelatedEntitiesArgumentEntry.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/RelatedEntitiesArgumentEntry.java index 219cf471ed..0a5c850e4f 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/RelatedEntitiesArgumentEntry.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/RelatedEntitiesArgumentEntry.java @@ -23,6 +23,7 @@ import org.thingsboard.script.api.tbel.TbelCfSingleValueArg; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.service.cf.ctx.state.ArgumentEntry; import org.thingsboard.server.service.cf.ctx.state.ArgumentEntryType; +import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldCtx; import org.thingsboard.server.service.cf.ctx.state.HasLatestTs; import org.thingsboard.server.service.cf.ctx.state.SingleValueArgumentEntry; @@ -63,7 +64,7 @@ public class RelatedEntitiesArgumentEntry implements ArgumentEntry, HasLatestTs } @Override - public boolean updateEntry(ArgumentEntry entry) { + public boolean updateEntry(ArgumentEntry entry, CalculatedFieldCtx ctx) { if (entry instanceof RelatedEntitiesArgumentEntry relatedEntitiesArgumentEntry) { entityInputs.putAll(relatedEntitiesArgumentEntry.entityInputs); return true; @@ -74,7 +75,7 @@ public class RelatedEntitiesArgumentEntry implements ArgumentEntry, HasLatestTs } ArgumentEntry argumentEntry = entityInputs.get(singleValueArgumentEntry.getEntityId()); if (argumentEntry != null) { - argumentEntry.updateEntry(singleValueArgumentEntry); + argumentEntry.updateEntry(singleValueArgumentEntry, ctx); } else { entityInputs.put(singleValueArgumentEntry.getEntityId(), singleValueArgumentEntry); } diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/single/EntityAggregationArgumentEntry.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/single/EntityAggregationArgumentEntry.java index ef34b6ae8e..c0a0603390 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/single/EntityAggregationArgumentEntry.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/single/EntityAggregationArgumentEntry.java @@ -39,18 +39,10 @@ public class EntityAggregationArgumentEntry implements ArgumentEntry { private boolean forceResetPrevious; - private AggInterval interval; - private long watermarkDuration; - public EntityAggregationArgumentEntry(Map aggIntervals) { this.aggIntervals = aggIntervals; } - public EntityAggregationArgumentEntry(Map aggIntervals, CalculatedFieldCtx ctx) { - this(aggIntervals); - setCtx(ctx); - } - @Override public ArgumentEntryType getType() { return ArgumentEntryType.ENTITY_AGGREGATION; @@ -61,15 +53,8 @@ public class EntityAggregationArgumentEntry implements ArgumentEntry { return aggIntervals; } - public void setCtx(CalculatedFieldCtx ctx) { - var configuration = (EntityAggregationCalculatedFieldConfiguration) ctx.getCalculatedField().getConfiguration(); - interval = configuration.getInterval(); - Watermark watermark = configuration.getWatermark(); - watermarkDuration = watermark == null ? 0 : TimeUnit.SECONDS.toMillis(watermark.getDuration()); - } - @Override - public boolean updateEntry(ArgumentEntry entry) { + public boolean updateEntry(ArgumentEntry entry, CalculatedFieldCtx ctx) { if (entry instanceof EntityAggregationArgumentEntry entityAggEntry) { aggIntervals.putAll(entityAggEntry.getAggIntervals()); return true; @@ -79,7 +64,7 @@ public class EntityAggregationArgumentEntry implements ArgumentEntry { if (updateExistingIntervals(singleValueArgEntry, entryTs, now)) { return true; } - return createNewInterval(entryTs, now); + return createNewInterval(entryTs, now, ctx); } return false; } @@ -104,7 +89,14 @@ public class EntityAggregationArgumentEntry implements ArgumentEntry { return updated; } - private boolean createNewInterval(long entryTs, long now) { + private boolean createNewInterval(long entryTs, long now, CalculatedFieldCtx ctx) { + if (!(ctx.getCalculatedField().getConfiguration() instanceof EntityAggregationCalculatedFieldConfiguration config)) { + return false; + } + AggInterval interval = config.getInterval(); + Watermark watermark = config.getWatermark(); + long watermarkDuration = watermark == null ? 0 : TimeUnit.SECONDS.toMillis(watermark.getDuration()); + ZonedDateTime zdt = ZonedDateTime.ofInstant(Instant.ofEpochMilli(entryTs), interval.getZoneId()); long startTs = interval.getDateTimeIntervalStartTs(zdt); diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/single/EntityAggregationCalculatedFieldState.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/single/EntityAggregationCalculatedFieldState.java index 59335717fb..99bddc374c 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/single/EntityAggregationCalculatedFieldState.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/single/EntityAggregationCalculatedFieldState.java @@ -82,15 +82,6 @@ public class EntityAggregationCalculatedFieldState extends BaseCalculatedFieldSt interval = configuration.getInterval(); metrics = configuration.getMetrics(); produceIntermediateResult = configuration.isProduceIntermediateResult(); - setCtxToArguments(); - } - - private void setCtxToArguments() { - arguments.values().forEach(argument -> { - if (argument instanceof EntityAggregationArgumentEntry entityAggArgument) { - entityAggArgument.setCtx(ctx); - } - }); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/alarm/AlarmCalculatedFieldState.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/alarm/AlarmCalculatedFieldState.java index 4f2cbede09..63b7cc8eb1 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/alarm/AlarmCalculatedFieldState.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/alarm/AlarmCalculatedFieldState.java @@ -224,17 +224,17 @@ public class AlarmCalculatedFieldState extends BaseCalculatedFieldState { } @Override - protected boolean updateEntry(ArgumentEntry existingArgumentEntry, ArgumentEntry newArgumentEntry) { + protected boolean updateEntry(ArgumentEntry existingArgumentEntry, ArgumentEntry newArgumentEntry, CalculatedFieldCtx ctx) { if (!(existingArgumentEntry instanceof SingleValueArgumentEntry existingEntry) || !(newArgumentEntry instanceof SingleValueArgumentEntry newEntry)) { - return super.updateEntry(existingArgumentEntry, newArgumentEntry); + return super.updateEntry(existingArgumentEntry, newArgumentEntry, ctx); } if (newEntry.getTs() < existingEntry.getTs()) { if (existingEntry.isDefaultValue()) { existingEntry.setTs(newEntry.getTs()); } } - return super.updateEntry(existingEntry, newEntry); + return super.updateEntry(existingEntry, newEntry, ctx); } public void processAlarmAction(Alarm alarm, ActionType action) { diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/geofencing/GeofencingArgumentEntry.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/geofencing/GeofencingArgumentEntry.java index 01c7119993..a3305ea52d 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/geofencing/GeofencingArgumentEntry.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/geofencing/GeofencingArgumentEntry.java @@ -25,6 +25,7 @@ import org.thingsboard.server.common.util.ProtoUtils; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.service.cf.ctx.state.ArgumentEntry; import org.thingsboard.server.service.cf.ctx.state.ArgumentEntryType; +import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldCtx; import org.thingsboard.server.service.cf.ctx.state.HasLatestTs; import java.util.Map; @@ -68,7 +69,7 @@ public class GeofencingArgumentEntry implements ArgumentEntry, HasLatestTs { } @Override - public boolean updateEntry(ArgumentEntry entry) { + public boolean updateEntry(ArgumentEntry entry, CalculatedFieldCtx ctx) { if (!(entry instanceof GeofencingArgumentEntry geofencingArgumentEntry)) { throw new IllegalArgumentException("Unsupported argument entry type for geofencing argument entry: " + entry.getType()); } diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/propagation/PropagationArgumentEntry.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/propagation/PropagationArgumentEntry.java index 8536c0f65f..de04d0e817 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/propagation/PropagationArgumentEntry.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/propagation/PropagationArgumentEntry.java @@ -21,6 +21,7 @@ import org.thingsboard.script.api.tbel.TbelCfPropagationArg; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.service.cf.ctx.state.ArgumentEntry; import org.thingsboard.server.service.cf.ctx.state.ArgumentEntryType; +import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldCtx; import java.util.ArrayList; import java.util.Collection; @@ -59,7 +60,7 @@ public class PropagationArgumentEntry implements ArgumentEntry { } @Override - public boolean updateEntry(ArgumentEntry entry) { + public boolean updateEntry(ArgumentEntry entry, CalculatedFieldCtx ctx) { if (!(entry instanceof PropagationArgumentEntry updated)) { throw new IllegalArgumentException("Unsupported argument entry type for propagation argument entry: " + entry.getType()); } diff --git a/application/src/main/java/org/thingsboard/server/utils/CalculatedFieldArgumentUtils.java b/application/src/main/java/org/thingsboard/server/utils/CalculatedFieldArgumentUtils.java index d6f08cbc69..7e0701cd2d 100644 --- a/application/src/main/java/org/thingsboard/server/utils/CalculatedFieldArgumentUtils.java +++ b/application/src/main/java/org/thingsboard/server/utils/CalculatedFieldArgumentUtils.java @@ -75,7 +75,7 @@ public class CalculatedFieldArgumentUtils { return new SingleValueArgumentEntry(); } - public static ArgumentEntry transformAggregationArgument(List timeSeries, long startIntervalTs, long endIntervalTs, CalculatedFieldCtx ctx) { + public static ArgumentEntry transformAggregationArgument(List timeSeries, long startIntervalTs, long endIntervalTs) { Map aggIntervals = new HashMap<>(); AggIntervalEntry aggIntervalEntry = new AggIntervalEntry(startIntervalTs, endIntervalTs); if (timeSeries == null || timeSeries.isEmpty()) { @@ -83,7 +83,7 @@ public class CalculatedFieldArgumentUtils { } else { aggIntervals.put(aggIntervalEntry, new AggIntervalEntryStatus(System.currentTimeMillis())); } - return new EntityAggregationArgumentEntry(aggIntervals, ctx); + return new EntityAggregationArgumentEntry(aggIntervals); } private static KvEntry createDefaultKvEntry(Argument argument) { diff --git a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/GeofencingValueArgumentEntryTest.java b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/GeofencingValueArgumentEntryTest.java index 6da4bdc882..d274da2434 100644 --- a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/GeofencingValueArgumentEntryTest.java +++ b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/GeofencingValueArgumentEntryTest.java @@ -18,6 +18,9 @@ package org.thingsboard.server.service.cf.ctx.state; import io.hypersistence.utils.hibernate.type.json.internal.JacksonUtil; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; import org.thingsboard.common.util.geo.PerimeterDefinition; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.EntityId; @@ -33,6 +36,7 @@ import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +@ExtendWith(MockitoExtension.class) public class GeofencingValueArgumentEntryTest { private final AssetId ZONE_1_ID = new AssetId(UUID.fromString("c0e3031c-7df1-45e4-9590-cfd621a4d714")); @@ -46,6 +50,9 @@ public class GeofencingValueArgumentEntryTest { private GeofencingArgumentEntry entry; + @Mock + private CalculatedFieldCtx ctx; + @BeforeEach void setUp() { entry = new GeofencingArgumentEntry(Map.of(ZONE_1_ID, allowedZoneAttributeKvEntry, ZONE_2_ID, restrictedZoneAttributeKvEntry)); @@ -58,14 +65,14 @@ public class GeofencingValueArgumentEntryTest { @Test void testUpdateEntryWhenSingleEntryPassed() { - assertThatThrownBy(() -> entry.updateEntry(new SingleValueArgumentEntry())) + assertThatThrownBy(() -> entry.updateEntry(new SingleValueArgumentEntry(), ctx)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("Unsupported argument entry type for geofencing argument entry: SINGLE_VALUE"); } @Test void testUpdateEntryWhenRollingEntryPassed() { - assertThatThrownBy(() -> entry.updateEntry(new TsRollingArgumentEntry(5, 30000L))) + assertThatThrownBy(() -> entry.updateEntry(new TsRollingArgumentEntry(5, 30000L), ctx)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("Unsupported argument entry type for geofencing argument entry: TS_ROLLING"); } @@ -74,7 +81,7 @@ public class GeofencingValueArgumentEntryTest { void testUpdateEntryWithTheSameTs() { BaseAttributeKvEntry differentValueSameTs = new BaseAttributeKvEntry(new JsonDataEntry("zone", "[[50.472001, 30.504001], [50.472001, 30.506001], [50.474001, 30.506001], [50.474001, 30.504001]]"), 363L, 156L); var updated = new GeofencingArgumentEntry(Map.of(ZONE_1_ID, differentValueSameTs, ZONE_2_ID, restrictedZoneAttributeKvEntry)); - assertThat(entry.updateEntry(updated)).isFalse(); + assertThat(entry.updateEntry(updated, ctx)).isFalse(); } @Test @@ -83,7 +90,7 @@ public class GeofencingValueArgumentEntryTest { BaseAttributeKvEntry differentValueNewVersionIsNull = new BaseAttributeKvEntry(new JsonDataEntry("zone", "[[50.472001, 30.504001], [50.472001, 30.506001], [50.474001, 30.506001], [50.474001, 30.504001]]"), 364L, null); var updated = new GeofencingArgumentEntry(Map.of(ZONE_1_ID, differentValueNewVersionIsNull, ZONE_2_ID, restrictedZoneAttributeKvEntry)); - assertThat(entry.updateEntry(updated)).isTrue(); + assertThat(entry.updateEntry(updated, ctx)).isTrue(); assertThat(entry.getValue()).isInstanceOf(Map.class); Map value = (Map) entry.getValue(); @@ -105,7 +112,7 @@ public class GeofencingValueArgumentEntryTest { BaseAttributeKvEntry differentValueNewVersionIsSet = new BaseAttributeKvEntry(new JsonDataEntry("zone", "[[50.472001, 30.504001], [50.472001, 30.506001], [50.474001, 30.506001], [50.474001, 30.504001]]"), 364L, 156L); var updated = new GeofencingArgumentEntry(Map.of(ZONE_1_ID, differentValueNewVersionIsSet, ZONE_2_ID, restrictedZoneAttributeKvEntry)); - assertThat(entry.updateEntry(updated)).isTrue(); + assertThat(entry.updateEntry(updated, ctx)).isTrue(); assertThat(entry.getValue()).isInstanceOf(Map.class); Map value = (Map) entry.getValue(); @@ -126,7 +133,7 @@ public class GeofencingValueArgumentEntryTest { BaseAttributeKvEntry differentValueNewVersionIsSet = new BaseAttributeKvEntry(new JsonDataEntry("zone", "[[50.472001, 30.504001], [50.472001, 30.506001], [50.474001, 30.506001], [50.474001, 30.504001]]"), 364L, 154L); var updated = new GeofencingArgumentEntry(Map.of(ZONE_1_ID, differentValueNewVersionIsSet, ZONE_2_ID, restrictedZoneAttributeKvEntry)); - assertThat(entry.updateEntry(updated)).isFalse(); + assertThat(entry.updateEntry(updated, ctx)).isFalse(); } @Test @@ -134,7 +141,7 @@ public class GeofencingValueArgumentEntryTest { BaseAttributeKvEntry newTsAndTheSameValue = new BaseAttributeKvEntry(allowedZoneDataEntry, 364L, 156L); var updated = new GeofencingArgumentEntry(Map.of(ZONE_1_ID, newTsAndTheSameValue, ZONE_2_ID, restrictedZoneAttributeKvEntry)); - assertThat(entry.updateEntry(updated)).isTrue(); + assertThat(entry.updateEntry(updated, ctx)).isTrue(); } @Test @@ -142,7 +149,7 @@ public class GeofencingValueArgumentEntryTest { BaseAttributeKvEntry oldTsAndTheSameValue = new BaseAttributeKvEntry(allowedZoneDataEntry, 362L, 156L); var updated = new GeofencingArgumentEntry(Map.of(ZONE_1_ID, oldTsAndTheSameValue, ZONE_2_ID, restrictedZoneAttributeKvEntry)); - assertThat(entry.updateEntry(updated)).isFalse(); + assertThat(entry.updateEntry(updated, ctx)).isFalse(); } @Test @@ -150,7 +157,7 @@ public class GeofencingValueArgumentEntryTest { final AssetId NEW_ZONE_ID = new AssetId(UUID.fromString("a3eacf1a-6af3-4e9f-87c4-502bb25c7dc3")); BaseAttributeKvEntry newZone = new BaseAttributeKvEntry(new JsonDataEntry("zone", "[[50.472001, 30.504001], [50.472001, 30.506001], [50.474001, 30.506001], [50.474001, 30.504001]]"), 364L, 156L); var updated = new GeofencingArgumentEntry(Map.of(ZONE_1_ID, allowedZoneAttributeKvEntry, ZONE_2_ID, restrictedZoneAttributeKvEntry, NEW_ZONE_ID, newZone)); - assertThat(entry.updateEntry(updated)).isTrue(); + assertThat(entry.updateEntry(updated, ctx)).isTrue(); } @Test diff --git a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/PropagationArgumentEntryTest.java b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/PropagationArgumentEntryTest.java index 12f3e4298d..f4098dc2df 100644 --- a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/PropagationArgumentEntryTest.java +++ b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/PropagationArgumentEntryTest.java @@ -17,6 +17,9 @@ package org.thingsboard.server.service.cf.ctx.state; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; import org.thingsboard.script.api.tbel.TbelCfArg; import org.thingsboard.script.api.tbel.TbelCfPropagationArg; import org.thingsboard.server.common.data.id.AssetId; @@ -31,6 +34,7 @@ import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +@ExtendWith(MockitoExtension.class) public class PropagationArgumentEntryTest { private final AssetId ENTITY_1_ID = new AssetId(UUID.fromString("b0a8637d-6d67-43d5-a483-c0e391afe805")); @@ -39,6 +43,9 @@ public class PropagationArgumentEntryTest { private PropagationArgumentEntry entry; + @Mock + private CalculatedFieldCtx ctx; + @BeforeEach void setUp() { List propagationEntityIds = new ArrayList<>(); @@ -68,14 +75,14 @@ public class PropagationArgumentEntryTest { @Test void testUpdateEntryWhenSingleEntryPassed() { - assertThatThrownBy(() -> entry.updateEntry(new SingleValueArgumentEntry())) + assertThatThrownBy(() -> entry.updateEntry(new SingleValueArgumentEntry(), ctx)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("Unsupported argument entry type for propagation argument entry: SINGLE_VALUE"); } @Test void testUpdateEntryWhenRollingEntryPassed() { - assertThatThrownBy(() -> entry.updateEntry(new TsRollingArgumentEntry(5, 30000L))) + assertThatThrownBy(() -> entry.updateEntry(new TsRollingArgumentEntry(5, 30000L), ctx)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("Unsupported argument entry type for propagation argument entry: TS_ROLLING"); } @@ -85,7 +92,7 @@ public class PropagationArgumentEntryTest { var newIds = new ArrayList(List.of(ENTITY_3_ID, ENTITY_1_ID)); var updated = new PropagationArgumentEntry(newIds); - boolean changed = entry.updateEntry(updated); + boolean changed = entry.updateEntry(updated, ctx); assertThat(changed).isTrue(); assertThat(entry.getEntityIds()).containsExactlyElementsOf(newIds); @@ -95,7 +102,7 @@ public class PropagationArgumentEntryTest { void testUpdateEntryClearsWhenNewEntryIsEmpty() { var updatedEmpty = new PropagationArgumentEntry(List.of()); - boolean changed = entry.updateEntry(updatedEmpty); + boolean changed = entry.updateEntry(updatedEmpty, ctx); assertThat(changed).isTrue(); assertThat(entry.getEntityIds()).isEmpty(); @@ -106,7 +113,7 @@ public class PropagationArgumentEntryTest { var added = new PropagationArgumentEntry(); added.setAdded(List.of(ENTITY_3_ID)); - boolean changed = entry.updateEntry(added); + boolean changed = entry.updateEntry(added, ctx); assertThat(changed).isTrue(); assertThat(entry.getEntityIds()).containsExactlyInAnyOrder(ENTITY_1_ID, ENTITY_2_ID, ENTITY_3_ID); @@ -118,7 +125,7 @@ public class PropagationArgumentEntryTest { var added = new PropagationArgumentEntry(); added.setAdded(List.of(ENTITY_2_ID)); - boolean changed = entry.updateEntry(added); + boolean changed = entry.updateEntry(added, ctx); assertThat(changed).isFalse(); assertThat(entry.getEntityIds()).containsExactlyInAnyOrder(ENTITY_1_ID, ENTITY_2_ID); @@ -130,7 +137,7 @@ public class PropagationArgumentEntryTest { var removed = new PropagationArgumentEntry(); removed.setRemoved(ENTITY_2_ID); - boolean changed = entry.updateEntry(removed); + boolean changed = entry.updateEntry(removed, ctx); assertThat(changed).isTrue(); assertThat(entry.getEntityIds()).containsExactlyInAnyOrder(ENTITY_1_ID); @@ -142,7 +149,7 @@ public class PropagationArgumentEntryTest { var removed = new PropagationArgumentEntry(); removed.setRemoved(ENTITY_3_ID); - boolean changed = entry.updateEntry(removed); + boolean changed = entry.updateEntry(removed, ctx); assertThat(changed).isFalse(); assertThat(entry.getEntityIds()).containsExactlyInAnyOrder(ENTITY_1_ID, ENTITY_2_ID); @@ -154,7 +161,7 @@ public class PropagationArgumentEntryTest { var restore = new PropagationArgumentEntry(List.of(ENTITY_1_ID, ENTITY_2_ID, ENTITY_3_ID)); restore.setIgnoreRemovedEntities(true); - boolean changed = entry.updateEntry(restore); + boolean changed = entry.updateEntry(restore, ctx); assertThat(changed).isTrue(); assertThat(entry.getEntityIds()).containsExactlyInAnyOrder(ENTITY_1_ID, ENTITY_2_ID, ENTITY_3_ID); @@ -168,7 +175,7 @@ public class PropagationArgumentEntryTest { var restore = new PropagationArgumentEntry(List.of(ENTITY_1_ID)); restore.setIgnoreRemovedEntities(true); - boolean changed = entry.updateEntry(restore); + boolean changed = entry.updateEntry(restore, ctx); assertThat(changed).isFalse(); // expected no change, since we consider the removal of stale ids as no-op assertThat(entry.getEntityIds()).containsExactlyInAnyOrder(ENTITY_1_ID); @@ -182,7 +189,7 @@ public class PropagationArgumentEntryTest { var restore = new PropagationArgumentEntry(List.of(ENTITY_1_ID, ENTITY_3_ID)); restore.setIgnoreRemovedEntities(true); - boolean changed = entry.updateEntry(restore); + boolean changed = entry.updateEntry(restore, ctx); assertThat(changed).isTrue(); assertThat(entry.getEntityIds()).containsExactlyInAnyOrder(ENTITY_1_ID, ENTITY_3_ID); @@ -197,7 +204,7 @@ public class PropagationArgumentEntryTest { var restore = new PropagationArgumentEntry(List.of(ENTITY_1_ID, ENTITY_2_ID)); restore.setIgnoreRemovedEntities(true); - boolean changed = entry.updateEntry(restore); + boolean changed = entry.updateEntry(restore, ctx); assertThat(changed).isFalse(); assertThat(entry.getEntityIds()).containsExactlyInAnyOrder(ENTITY_1_ID, ENTITY_2_ID); @@ -211,7 +218,7 @@ public class PropagationArgumentEntryTest { var restore = new PropagationArgumentEntry(List.of()); restore.setIgnoreRemovedEntities(true); - boolean changed = entry.updateEntry(restore); + boolean changed = entry.updateEntry(restore, ctx); assertThat(changed).isFalse(); // expected no change, since we consider the removal of stale ids as no-op assertThat(entry.getEntityIds()).isEmpty(); diff --git a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/RelatedEntitiesArgumentEntryTest.java b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/RelatedEntitiesArgumentEntryTest.java index cc60b249ac..c357e2c30b 100644 --- a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/RelatedEntitiesArgumentEntryTest.java +++ b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/RelatedEntitiesArgumentEntryTest.java @@ -17,6 +17,9 @@ package org.thingsboard.server.service.cf.ctx.state; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.kv.BasicTsKvEntry; @@ -30,10 +33,14 @@ import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +@ExtendWith(MockitoExtension.class) public class RelatedEntitiesArgumentEntryTest { private RelatedEntitiesArgumentEntry entry; + @Mock + private CalculatedFieldCtx ctx; + private final DeviceId device1 = new DeviceId(UUID.fromString("1984e5f4-9ff0-4187-84ae-e4438bba4c8a")); private final DeviceId device2 = new DeviceId(UUID.fromString("937fc062-1a9d-438f-aa22-55a93fc908b7")); @@ -50,7 +57,7 @@ public class RelatedEntitiesArgumentEntryTest { @Test void testUpdateEntryWhenNotAggEntryPassed() { - assertThatThrownBy(() -> entry.updateEntry(new TsRollingArgumentEntry(5, 30000L))) + assertThatThrownBy(() -> entry.updateEntry(new TsRollingArgumentEntry(5, 30000L), ctx)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("Unsupported argument entry type for aggregation argument entry: " + ArgumentEntryType.TS_ROLLING); } @@ -65,7 +72,7 @@ public class RelatedEntitiesArgumentEntryTest { device4, new SingleValueArgumentEntry(device4, new BasicTsKvEntry(ts - 60, new LongDataEntry("key", 23L), 7L)) ), false); - assertThat(entry.updateEntry(relatedEntitiesArgumentEntry)).isTrue(); + assertThat(entry.updateEntry(relatedEntitiesArgumentEntry, ctx)).isTrue(); Map aggInputs = entry.getEntityInputs(); assertThat(aggInputs.size()).isEqualTo(4); @@ -79,7 +86,7 @@ public class RelatedEntitiesArgumentEntryTest { SingleValueArgumentEntry singleEntityArgumentEntry = new SingleValueArgumentEntry(device3, new BasicTsKvEntry(ts - 50, new LongDataEntry("key", 18L), 10L)); - assertThat(entry.updateEntry(singleEntityArgumentEntry)).isTrue(); + assertThat(entry.updateEntry(singleEntityArgumentEntry, ctx)).isTrue(); Map aggInputs = entry.getEntityInputs(); assertThat(aggInputs.size()).isEqualTo(3); @@ -90,7 +97,7 @@ public class RelatedEntitiesArgumentEntryTest { void testUpdateEntryWhenSingleValueArgumentEntryPassedAndEntryByIdExist() { SingleValueArgumentEntry singleEntityArgumentEntry = new SingleValueArgumentEntry(device2, new BasicTsKvEntry(ts - 50, new LongDataEntry("key", 18L), 10L)); - assertThat(entry.updateEntry(singleEntityArgumentEntry)).isTrue(); + assertThat(entry.updateEntry(singleEntityArgumentEntry, ctx)).isTrue(); Map aggInputs = entry.getEntityInputs(); assertThat(aggInputs.size()).isEqualTo(2); diff --git a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/SingleValueArgumentEntryTest.java b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/SingleValueArgumentEntryTest.java index 4ada355054..e2d287c778 100644 --- a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/SingleValueArgumentEntryTest.java +++ b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/SingleValueArgumentEntryTest.java @@ -17,6 +17,9 @@ package org.thingsboard.server.service.cf.ctx.state; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; import org.thingsboard.script.api.tbel.TbelCfArg; import org.thingsboard.script.api.tbel.TbelCfSingleValueArg; import org.thingsboard.server.common.data.kv.JsonDataEntry; @@ -29,10 +32,14 @@ import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +@ExtendWith(MockitoExtension.class) public class SingleValueArgumentEntryTest { private SingleValueArgumentEntry entry; + @Mock + private CalculatedFieldCtx ctx; + private final long ts = System.currentTimeMillis(); @BeforeEach @@ -47,48 +54,48 @@ public class SingleValueArgumentEntryTest { @Test void testUpdateEntryWhenRollingEntryPassed() { - assertThatThrownBy(() -> entry.updateEntry(new TsRollingArgumentEntry(5, 30000L))) + assertThatThrownBy(() -> entry.updateEntry(new TsRollingArgumentEntry(5, 30000L), ctx)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("Unsupported argument entry type for single value argument entry: " + ArgumentEntryType.TS_ROLLING); } @Test void testUpdateEntryWithTheSameTs() { - assertThat(entry.updateEntry(new SingleValueArgumentEntry(ts, new LongDataEntry("key", 13L), 363L))).isFalse(); + assertThat(entry.updateEntry(new SingleValueArgumentEntry(ts, new LongDataEntry("key", 13L), 363L), ctx)).isFalse(); } @Test void testUpdateEntryWithTheSameTsAndDifferentVersion() { - assertThat(entry.updateEntry(new SingleValueArgumentEntry(ts, new LongDataEntry("key", 13L), 364L))).isTrue(); + assertThat(entry.updateEntry(new SingleValueArgumentEntry(ts, new LongDataEntry("key", 13L), 364L), ctx)).isTrue(); } @Test void testUpdateEntryWhenNewVersionIsNull() { - assertThat(entry.updateEntry(new SingleValueArgumentEntry(ts + 16, new LongDataEntry("key", 13L), null))).isTrue(); + assertThat(entry.updateEntry(new SingleValueArgumentEntry(ts + 16, new LongDataEntry("key", 13L), null), ctx)).isTrue(); assertThat(entry.getValue()).isEqualTo(13L); assertThat(entry.getVersion()).isNull(); } @Test void testUpdateEntryWhenNewVersionIsGreaterThanCurrent() { - assertThat(entry.updateEntry(new SingleValueArgumentEntry(ts + 18, new LongDataEntry("key", 18L), 369L))).isTrue(); + assertThat(entry.updateEntry(new SingleValueArgumentEntry(ts + 18, new LongDataEntry("key", 18L), 369L), ctx)).isTrue(); assertThat(entry.getValue()).isEqualTo(18L); assertThat(entry.getVersion()).isEqualTo(369L); } @Test void testUpdateEntryWhenNewVersionIsLessThanCurrent() { - assertThat(entry.updateEntry(new SingleValueArgumentEntry(ts + 18, new LongDataEntry("key", 18L), 234L))).isFalse(); + assertThat(entry.updateEntry(new SingleValueArgumentEntry(ts + 18, new LongDataEntry("key", 18L), 234L), ctx)).isFalse(); } @Test void testUpdateEntryWhenValueWasNotChanged() { - assertThat(entry.updateEntry(new SingleValueArgumentEntry(ts + 18, new LongDataEntry("key", 11L), 364L))).isTrue(); + assertThat(entry.updateEntry(new SingleValueArgumentEntry(ts + 18, new LongDataEntry("key", 11L), 364L), ctx)).isTrue(); } @Test void testUpdateEntryWithOldTs() { - assertThat(entry.updateEntry(new SingleValueArgumentEntry(ts - 10, new LongDataEntry("key", 14L), 365L))).isFalse(); + assertThat(entry.updateEntry(new SingleValueArgumentEntry(ts - 10, new LongDataEntry("key", 14L), 365L), ctx)).isFalse(); } @Test diff --git a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/TsRollingArgumentEntryTest.java b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/TsRollingArgumentEntryTest.java index b1f8063857..94b2a7b389 100644 --- a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/TsRollingArgumentEntryTest.java +++ b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/TsRollingArgumentEntryTest.java @@ -17,6 +17,9 @@ package org.thingsboard.server.service.cf.ctx.state; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; import org.thingsboard.server.common.data.kv.DoubleDataEntry; import org.thingsboard.server.common.data.kv.StringDataEntry; @@ -26,10 +29,14 @@ import java.util.TreeMap; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +@ExtendWith(MockitoExtension.class) public class TsRollingArgumentEntryTest { private TsRollingArgumentEntry entry; + @Mock + private CalculatedFieldCtx ctx; + private final long ts = System.currentTimeMillis(); @BeforeEach @@ -51,7 +58,7 @@ public class TsRollingArgumentEntryTest { void testUpdateEntryWhenSingleValueEntryPassed() { SingleValueArgumentEntry newEntry = new SingleValueArgumentEntry(ts - 10, new DoubleDataEntry("key", 23.0), 123L); - assertThat(entry.updateEntry(newEntry)).isTrue(); + assertThat(entry.updateEntry(newEntry, ctx)).isTrue(); assertThat(entry.getTsRecords()).hasSize(4); assertThat(entry.getTsRecords().get(ts - 10)).isEqualTo(23.0); } @@ -64,7 +71,7 @@ public class TsRollingArgumentEntryTest { values.put(ts - 5, 1.0); newEntry.setTsRecords(values); - assertThat(entry.updateEntry(newEntry)).isTrue(); + assertThat(entry.updateEntry(newEntry, ctx)).isTrue(); assertThat(entry.getTsRecords()).hasSize(5); assertThat(entry.getTsRecords()).isEqualTo(Map.of( ts - 40, 10.0, @@ -79,7 +86,7 @@ public class TsRollingArgumentEntryTest { void testUpdateEntryWhenValueIsNotNumber() { SingleValueArgumentEntry newEntry = new SingleValueArgumentEntry(ts - 10, new StringDataEntry("key", "string"), 123L); - assertThat(entry.updateEntry(newEntry)).isTrue(); + assertThat(entry.updateEntry(newEntry, ctx)).isTrue(); assertThat(entry.getTsRecords().get(ts - 10)).isNaN(); } @@ -93,7 +100,7 @@ public class TsRollingArgumentEntryTest { newEntry.setTsRecords(values); entry = new TsRollingArgumentEntry(3, 30000L); - assertThat(entry.updateEntry(newEntry)).isTrue(); + assertThat(entry.updateEntry(newEntry, ctx)).isTrue(); assertThat(entry.getTsRecords()).hasSize(1); assertThat(entry.getTsRecords()).isEqualTo(Map.of( ts - 5, 0.0 @@ -111,7 +118,7 @@ public class TsRollingArgumentEntryTest { newEntry.setTsRecords(values); entry = new TsRollingArgumentEntry(3, 30000L); - assertThat(entry.updateEntry(newEntry)).isTrue(); + assertThat(entry.updateEntry(newEntry, ctx)).isTrue(); assertThat(entry.getTsRecords()).hasSize(3); assertThat(entry.getTsRecords()).isEqualTo(Map.of( ts - 18, 0.0, From 860195426c163f980513a367a1634deea6de3bb7 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Mon, 29 Dec 2025 12:36:57 +0200 Subject: [PATCH 43/47] UI: Fixed loading state in login pages --- .../login/pages/login/create-password.component.ts | 10 +++++----- .../modules/login/pages/login/login.component.html | 6 +++--- .../app/modules/login/pages/login/login.component.ts | 11 +++++++---- .../login/pages/login/reset-password.component.ts | 8 ++++---- 4 files changed, 19 insertions(+), 16 deletions(-) diff --git a/ui-ngx/src/app/modules/login/pages/login/create-password.component.ts b/ui-ngx/src/app/modules/login/pages/login/create-password.component.ts index 0f2ec26ad3..f0a6bdd0ff 100644 --- a/ui-ngx/src/app/modules/login/pages/login/create-password.component.ts +++ b/ui-ngx/src/app/modules/login/pages/login/create-password.component.ts @@ -20,6 +20,7 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { ActivatedRoute } from '@angular/router'; import { UserPasswordPolicy } from '@shared/models/settings.models'; import { passwordsMatchValidator, passwordStrengthValidator } from '@shared/models/password.models'; +import { finalize } from 'rxjs/operators'; @Component({ selector: 'tb-create-password', @@ -60,11 +61,10 @@ export class CreatePasswordComponent { if (this.createPassword.invalid) { this.createPassword.markAllAsTouched(); } else { - this.isLoading = true - this.authService.activate(this.activateToken, this.createPassword.get('newPassword').value, true) - .subscribe({ - error: () => {this.isLoading = false;} - }); + this.isLoading = true; + this.authService.activate(this.activateToken, this.createPassword.get('newPassword').value, true).pipe( + finalize(() => {this.isLoading = false;}) + ).subscribe(() => {}); } } } diff --git a/ui-ngx/src/app/modules/login/pages/login/login.component.html b/ui-ngx/src/app/modules/login/pages/login/login.component.html index f43ff18435..f2878bde27 100644 --- a/ui-ngx/src/app/modules/login/pages/login/login.component.html +++ b/ui-ngx/src/app/modules/login/pages/login/login.component.html @@ -22,9 +22,9 @@
- + - +
@if(oauth2Clients?.length) { @@ -70,7 +70,7 @@ lock -
-
diff --git a/ui-ngx/src/app/modules/login/pages/login/create-password.component.ts b/ui-ngx/src/app/modules/login/pages/login/create-password.component.ts index f0a6bdd0ff..6e9be088c7 100644 --- a/ui-ngx/src/app/modules/login/pages/login/create-password.component.ts +++ b/ui-ngx/src/app/modules/login/pages/login/create-password.component.ts @@ -21,13 +21,14 @@ import { ActivatedRoute } from '@angular/router'; import { UserPasswordPolicy } from '@shared/models/settings.models'; import { passwordsMatchValidator, passwordStrengthValidator } from '@shared/models/password.models'; import { finalize } from 'rxjs/operators'; +import { PageComponent } from '@shared/components/page.component'; @Component({ selector: 'tb-create-password', templateUrl: './create-password.component.html', styleUrls: ['./password.component.scss'] }) -export class CreatePasswordComponent { +export class CreatePasswordComponent extends PageComponent { passwordPolicy: UserPasswordPolicy; createPassword: FormGroup; @@ -39,7 +40,7 @@ export class CreatePasswordComponent { constructor(private route: ActivatedRoute, private authService: AuthService, private fb: FormBuilder) { - + super(); this.activateToken = this.route.snapshot.queryParams['activateToken'] || ''; this.passwordPolicy = this.route.snapshot.data['passwordPolicy']; From 4e45f82cde00dce0d5acbf6a2d0e6b02c1eb86da Mon Sep 17 00:00:00 2001 From: ArtemDzhereleiko Date: Tue, 23 Dec 2025 19:24:43 +0200 Subject: [PATCH 46/47] UI: Show create relation only for relation query type --- ...calculated-field-geofencing-zone-groups-panel.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/geofencing-configuration/calculated-field-geofencing-zone-groups-panel.component.html b/ui-ngx/src/app/modules/home/components/calculated-fields/components/geofencing-configuration/calculated-field-geofencing-zone-groups-panel.component.html index 52c0226275..e09d5b0f4e 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/geofencing-configuration/calculated-field-geofencing-zone-groups-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/geofencing-configuration/calculated-field-geofencing-zone-groups-panel.component.html @@ -223,7 +223,7 @@
-
+
{{ 'calculated-fields.create-relation-with-matched-zones' | translate }} From dac772b47610a87cb95f3254f61bee25ae3fe026 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Tue, 30 Dec 2025 12:15:27 +0200 Subject: [PATCH 47/47] Fix release notes generation for bugs section --- .github/release.yml | 51 ++++++++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/.github/release.yml b/.github/release.yml index 53b2e3ae65..1dad5d274f 100644 --- a/.github/release.yml +++ b/.github/release.yml @@ -23,27 +23,19 @@ changelog: labels: - 'Major Core' - 'Major Rule Engine' - exclude: - labels: - - 'Bug' + - title: 'Major UI' labels: - 'Major UI' - exclude: - labels: - - 'Bug' + - title: 'Major Transport' labels: - 'Major Transport' - exclude: - labels: - - 'Bug' + - title: 'Major Edge' labels: - 'Major Edge' - exclude: - labels: - - 'Bug' + - title: 'Core & Rule Engine' labels: - 'Core' @@ -51,38 +43,63 @@ changelog: exclude: labels: - 'Bug' + - title: 'UI' labels: - 'UI' exclude: labels: - 'Bug' + - title: 'Transport' labels: - 'Transport' exclude: labels: - 'Bug' + - title: 'Edge' labels: - 'Edge' exclude: labels: - 'Bug' + - title: 'Bug: Core & Rule Engine' labels: - - 'Core' - - 'Rule Engine' - 'Bug' + exclude: + labels: + - 'UI' + - 'Transport' + - 'Edge' + - title: 'Bug: UI' labels: - - 'UI' - 'Bug' + exclude: + labels: + - 'Core' + - 'Rule Engine' + - 'Transport' + - 'Edge' + - title: 'Bug: Transport' labels: - - 'Transport' - 'Bug' + exclude: + labels: + - 'Core' + - 'Rule Engine' + - 'UI' + - 'Edge' + - title: 'Bug: Edge' labels: - - 'Edge' - 'Bug' + exclude: + labels: + - 'Core' + - 'Rule Engine' + - 'UI' + - 'Transport'