From 1b9dfa17c25c188d67a1b86076c765da5538bed9 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Mon, 9 Oct 2023 18:24:43 +0300 Subject: [PATCH 001/252] Refactoring for latencies tracking --- .../monitoring/data/Latencies.java | 4 ++ .../thingsboard/monitoring/data/Latency.java | 48 ++----------------- .../notification/HighLatencyNotification.java | 10 ++-- .../monitoring/service/BaseHealthChecker.java | 2 +- .../service/BaseMonitoringService.java | 5 +- .../service/MonitoringReporter.java | 26 +++++----- 6 files changed, 30 insertions(+), 65 deletions(-) diff --git a/monitoring/src/main/java/org/thingsboard/monitoring/data/Latencies.java b/monitoring/src/main/java/org/thingsboard/monitoring/data/Latencies.java index 3370d42462..52e73a64cd 100644 --- a/monitoring/src/main/java/org/thingsboard/monitoring/data/Latencies.java +++ b/monitoring/src/main/java/org/thingsboard/monitoring/data/Latencies.java @@ -25,4 +25,8 @@ public class Latencies { return String.format("%sRequest", key); } + public static String wsUpdate(String key) { + return String.format("%sWsUpdate", key); + } + } diff --git a/monitoring/src/main/java/org/thingsboard/monitoring/data/Latency.java b/monitoring/src/main/java/org/thingsboard/monitoring/data/Latency.java index c64291e30f..4b17bf179f 100644 --- a/monitoring/src/main/java/org/thingsboard/monitoring/data/Latency.java +++ b/monitoring/src/main/java/org/thingsboard/monitoring/data/Latency.java @@ -15,53 +15,15 @@ */ package org.thingsboard.monitoring.data; -import com.google.common.util.concurrent.AtomicDouble; -import lombok.RequiredArgsConstructor; +import lombok.Data; -import java.util.concurrent.atomic.AtomicInteger; - -@RequiredArgsConstructor +@Data(staticConstructor = "of") public class Latency { - private final String key; - private final AtomicDouble latencySum = new AtomicDouble(); - private final AtomicInteger counter = new AtomicInteger(); - - public synchronized void report(double latencyInMs) { - latencySum.addAndGet(latencyInMs); - counter.incrementAndGet(); - } - - public synchronized double getAvg() { - return latencySum.get() / counter.get(); - } - - public boolean isNotEmpty() { - return counter.get() > 0; - } - - public synchronized void reset() { - latencySum.set(0.0); - counter.set(0); - } - - public String getKey() { - return key; - } - - public synchronized Latency snapshot() { - Latency snapshot = new Latency(key); - snapshot.latencySum.set(latencySum.get()); - snapshot.counter.set(counter.get()); - return snapshot; - } + private final double value; - @Override - public String toString() { - return "Latency{" + - "key='" + key + '\'' + - ", avgLatency=" + getAvg() + - '}'; + public String getFormattedValue() { + return String.format("%,.2f ms", value); } } diff --git a/monitoring/src/main/java/org/thingsboard/monitoring/data/notification/HighLatencyNotification.java b/monitoring/src/main/java/org/thingsboard/monitoring/data/notification/HighLatencyNotification.java index 939cb7440f..5744e40d41 100644 --- a/monitoring/src/main/java/org/thingsboard/monitoring/data/notification/HighLatencyNotification.java +++ b/monitoring/src/main/java/org/thingsboard/monitoring/data/notification/HighLatencyNotification.java @@ -21,11 +21,11 @@ import java.util.Collection; public class HighLatencyNotification implements Notification { - private final Collection latencies; + private final Collection highLatencies; private final int thresholdMs; - public HighLatencyNotification(Collection latencies, int thresholdMs) { - this.latencies = latencies; + public HighLatencyNotification(Collection highLatencies, int thresholdMs) { + this.highLatencies = highLatencies; this.thresholdMs = thresholdMs; } @@ -33,8 +33,8 @@ public class HighLatencyNotification implements Notification { public String getText() { StringBuilder text = new StringBuilder(); text.append("Some of the latencies are higher than ").append(thresholdMs).append(" ms:\n"); - latencies.forEach(latency -> { - text.append(String.format("[%s] %,.2f ms\n", latency.getKey(), latency.getAvg())); + highLatencies.forEach(latency -> { + text.append(String.format("[%s] %s\n", latency.getKey(), latency.getFormattedValue())); }); return text.toString(); } diff --git a/monitoring/src/main/java/org/thingsboard/monitoring/service/BaseHealthChecker.java b/monitoring/src/main/java/org/thingsboard/monitoring/service/BaseHealthChecker.java index affca1ce09..697573289d 100644 --- a/monitoring/src/main/java/org/thingsboard/monitoring/service/BaseHealthChecker.java +++ b/monitoring/src/main/java/org/thingsboard/monitoring/service/BaseHealthChecker.java @@ -96,7 +96,7 @@ public abstract class BaseHealthChecker, T extends MonitoringTarget> { - @Autowired + @Autowired(required = false) private List configs; private final List> healthCheckers = new LinkedList<>(); private final List devices = new LinkedList<>(); @@ -54,6 +54,9 @@ public abstract class BaseMonitoringService, T ext @PostConstruct private void init() { + if (configs == null || configs.isEmpty()) { + return; + } tbClient.logIn(); configs.forEach(config -> { config.getTargets().forEach(target -> { diff --git a/monitoring/src/main/java/org/thingsboard/monitoring/service/MonitoringReporter.java b/monitoring/src/main/java/org/thingsboard/monitoring/service/MonitoringReporter.java index f41454a76a..aabf04caee 100644 --- a/monitoring/src/main/java/org/thingsboard/monitoring/service/MonitoringReporter.java +++ b/monitoring/src/main/java/org/thingsboard/monitoring/service/MonitoringReporter.java @@ -63,25 +63,20 @@ public class MonitoringReporter { private String reportingAssetId; public void reportLatencies(TbClient tbClient) { - List latencies = this.latencies.values().stream() - .filter(Latency::isNotEmpty) - .map(latency -> { - Latency snapshot = latency.snapshot(); - latency.reset(); - return snapshot; - }) - .collect(Collectors.toList()); if (latencies.isEmpty()) { return; } - log.info("Latencies:\n{}", latencies.stream().map(latency -> latency.getKey() + ": " + latency.getAvg() + " ms") + log.debug("Latencies:\n{}", latencies.values().stream().map(latency -> latency.getKey() + ": " + latency.getFormattedValue()) .collect(Collectors.joining("\n")) + "\n"); - if (!latencyReportingEnabled) return; - if (latencies.stream().anyMatch(latency -> latency.getAvg() >= (double) latencyThresholdMs)) { - HighLatencyNotification highLatencyNotification = new HighLatencyNotification(latencies, latencyThresholdMs); + List highLatencies = latencies.values().stream() + .filter(latency -> latency.getValue() >= (double) latencyThresholdMs) + .collect(Collectors.toList()); + if (!highLatencies.isEmpty()) { + HighLatencyNotification highLatencyNotification = new HighLatencyNotification(highLatencies, latencyThresholdMs); notificationService.sendNotification(highLatencyNotification); + log.warn("{}", highLatencyNotification.getText()); } try { @@ -99,10 +94,11 @@ public class MonitoringReporter { } ObjectNode msg = JacksonUtil.newObjectNode(); - latencies.forEach(latency -> { - msg.set(latency.getKey(), new DoubleNode(latency.getAvg())); + latencies.values().forEach(latency -> { + msg.set(latency.getKey(), new DoubleNode(latency.getValue())); }); tbClient.saveEntityTelemetry(new AssetId(UUID.fromString(reportingAssetId)), "time", msg); + latencies.clear(); } catch (Exception e) { log.error("Failed to report latencies: {}", e.getMessage()); } @@ -112,7 +108,7 @@ public class MonitoringReporter { String latencyKey = key + "Latency"; double latencyInMs = (double) latencyInNanos / 1000_000; log.trace("Reporting latency [{}]: {} ms", key, latencyInMs); - latencies.computeIfAbsent(latencyKey, k -> new Latency(latencyKey)).report(latencyInMs); + latencies.put(latencyKey, Latency.of(latencyKey, latencyInMs)); } public void serviceFailure(Object serviceKey, Throwable error) { From 6116052afc44215342e923660e67f28d277f05d4 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Wed, 11 Oct 2023 18:34:58 +0200 Subject: [PATCH 002/252] fixed device active/inactive widgets in home-page --- ui-ngx/src/assets/dashboard/tenant_admin_home_page.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/assets/dashboard/tenant_admin_home_page.json b/ui-ngx/src/assets/dashboard/tenant_admin_home_page.json index a2edebb7c4..37e8d82e45 100644 --- a/ui-ngx/src/assets/dashboard/tenant_admin_home_page.json +++ b/ui-ngx/src/assets/dashboard/tenant_admin_home_page.json @@ -943,7 +943,7 @@ "keyFilters": [ { "key": { - "type": "ATTRIBUTE", + "type": "SERVER_ATTRIBUTE", "key": "active" }, "valueType": "BOOLEAN", @@ -975,7 +975,7 @@ "keyFilters": [ { "key": { - "type": "ATTRIBUTE", + "type": "SERVER_ATTRIBUTE", "key": "active" }, "valueType": "BOOLEAN", @@ -1051,4 +1051,4 @@ }, "externalId": null, "name": "Tenant Administrator Home Page" -} \ No newline at end of file +} From f0bb74cf5b8201e6229a1174848047dddb0385dd Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Wed, 11 Oct 2023 19:02:56 +0200 Subject: [PATCH 003/252] fixed home page for customer --- ui-ngx/src/assets/dashboard/customer_user_home_page.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/assets/dashboard/customer_user_home_page.json b/ui-ngx/src/assets/dashboard/customer_user_home_page.json index 7c79162d5b..aef3367ff3 100644 --- a/ui-ngx/src/assets/dashboard/customer_user_home_page.json +++ b/ui-ngx/src/assets/dashboard/customer_user_home_page.json @@ -461,7 +461,7 @@ "keyFilters": [ { "key": { - "type": "ATTRIBUTE", + "type": "SERVER_ATTRIBUTE", "key": "active" }, "valueType": "BOOLEAN", @@ -493,7 +493,7 @@ "keyFilters": [ { "key": { - "type": "ATTRIBUTE", + "type": "SERVER_ATTRIBUTE", "key": "active" }, "valueType": "BOOLEAN", @@ -569,4 +569,4 @@ }, "externalId": null, "name": "Customer User Home Page" -} \ No newline at end of file +} From 657c247687e88f2010f386e3d43c903afc3e169b Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Thu, 26 Oct 2023 15:23:09 +0300 Subject: [PATCH 004/252] UI: Add case for widget type --- ui-ngx/src/app/core/http/entity.service.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/app/core/http/entity.service.ts b/ui-ngx/src/app/core/http/entity.service.ts index 69ca114001..fe884ab927 100644 --- a/ui-ngx/src/app/core/http/entity.service.ts +++ b/ui-ngx/src/app/core/http/entity.service.ts @@ -41,7 +41,7 @@ import { AttributeScope, DataKeyType } from '@shared/models/telemetry/telemetry. import { defaultHttpOptionsFromConfig, RequestConfig } from '@core/http/http-utils'; import { RuleChainService } from '@core/http/rule-chain.service'; import { AliasInfo, StateParams, SubscriptionInfo } from '@core/api/widget-api.models'; -import { DataKey, Datasource, DatasourceType, KeyInfo } from '@app/shared/models/widget.models'; +import { DataKey, Datasource, DatasourceType, DeprecatedFilter, KeyInfo } from '@app/shared/models/widget.models'; import { UtilsService } from '@core/services/utils.service'; import { AliasFilterType, @@ -65,7 +65,9 @@ import { Device, DeviceCredentialsType } from '@shared/models/device.models'; import { AttributeService } from '@core/http/attribute.service'; import { AlarmData, - AlarmDataQuery, AlarmFilter, AlarmFilterConfig, + AlarmDataQuery, + AlarmFilter, + AlarmFilterConfig, createDefaultEntityDataPageLink, EntityData, EntityDataQuery, @@ -418,6 +420,10 @@ export class EntityService { pageLink.sortOrder.property = 'title'; entitiesObservable = this.widgetService.getWidgetBundles(pageLink, false, config); break; + case EntityType.WIDGET_TYPE: + pageLink.sortOrder.property = 'name'; + entitiesObservable = this.widgetService.getWidgetTypes(pageLink, true, false, DeprecatedFilter.ALL, null, config); + break; case EntityType.NOTIFICATION_TARGET: pageLink.sortOrder.property = 'name'; entitiesObservable = this.notificationService.getNotificationTargets(pageLink, subType as NotificationType, config); From 5206ebb87b4a804abcdaec39f5fb7b71133af270 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Thu, 26 Oct 2023 17:25:54 +0300 Subject: [PATCH 005/252] UI: Fixed widget bundle entity list --- ui-ngx/src/app/core/http/entity.service.ts | 2 +- ui-ngx/src/app/core/http/widget.service.ts | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/ui-ngx/src/app/core/http/entity.service.ts b/ui-ngx/src/app/core/http/entity.service.ts index fe884ab927..4e04304754 100644 --- a/ui-ngx/src/app/core/http/entity.service.ts +++ b/ui-ngx/src/app/core/http/entity.service.ts @@ -418,7 +418,7 @@ export class EntityService { break; case EntityType.WIDGETS_BUNDLE: pageLink.sortOrder.property = 'title'; - entitiesObservable = this.widgetService.getWidgetBundles(pageLink, false, config); + entitiesObservable = this.widgetService.getWidgetBundles(pageLink, true, false, config); break; case EntityType.WIDGET_TYPE: pageLink.sortOrder.property = 'name'; diff --git a/ui-ngx/src/app/core/http/widget.service.ts b/ui-ngx/src/app/core/http/widget.service.ts index 6b53e4d0cb..3d33aad9e7 100644 --- a/ui-ngx/src/app/core/http/widget.service.ts +++ b/ui-ngx/src/app/core/http/widget.service.ts @@ -85,9 +85,12 @@ export class WidgetService { ); } - public getWidgetBundles(pageLink: PageLink, fullSearch = false, config?: RequestConfig): Observable> { - return this.http.get>(`/api/widgetsBundles${pageLink.toQuery()}&fullSearch=${fullSearch}`, - defaultHttpOptionsFromConfig(config)); + public getWidgetBundles(pageLink: PageLink, tenantOnly = false, + fullSearch = false, config?: RequestConfig): Observable> { + return this.http.get>( + `/api/widgetsBundles${pageLink.toQuery()}&tenantOnly=${tenantOnly}&fullSearch=${fullSearch}`, + defaultHttpOptionsFromConfig(config) + ); } public getWidgetsBundle(widgetsBundleId: string, From fed3ec95ec85952b6eafb9007f07a4365a0d1af7 Mon Sep 17 00:00:00 2001 From: rusikv Date: Fri, 27 Oct 2023 11:21:36 +0300 Subject: [PATCH 006/252] UI: fix for timeseries charts validation blocks widget adding --- .../widget/config/basic/chart/flot-basic-config.component.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/flot-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/flot-basic-config.component.ts index 2b2618e25f..745bb262d5 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/flot-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/flot-basic-config.component.ts @@ -82,6 +82,8 @@ export class FlotBasicConfigComponent extends BasicWidgetConfigComponent { legendConfig: [configData.config.settings?.legendConfig, []], actions: [configData.config.actions || {}, []] }); + this.flotWidgetConfigForm.get('series').valueChanges + .subscribe(() => this.flotWidgetConfigForm.get('datasources').updateValueAndValidity()); } protected prepareOutputConfig(config: any): WidgetConfigComponentData { From 95f37ee61bfc3a094033c87e20e14ce45f6e54b6 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Fri, 27 Oct 2023 11:30:07 +0300 Subject: [PATCH 007/252] UI: Fixed alarm type list width and empty option --- .../home/components/alarm/alarm-filter-config.component.scss | 3 +++ .../shared/components/entity/entity-subtype-list.component.ts | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-filter-config.component.scss b/ui-ngx/src/app/modules/home/components/alarm/alarm-filter-config.component.scss index 9b0c323a40..16a2f43006 100644 --- a/ui-ngx/src/app/modules/home/components/alarm/alarm-filter-config.component.scss +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-filter-config.component.scss @@ -40,6 +40,9 @@ tb-entity-subtype-list { flex: 1; width: 180px; + .mdc-evolution-chip-set__chips { + width: 100%; + } } .mat-mdc-chip { diff --git a/ui-ngx/src/app/shared/components/entity/entity-subtype-list.component.ts b/ui-ngx/src/app/shared/components/entity/entity-subtype-list.component.ts index cb7cc7b63d..b2ea6a185d 100644 --- a/ui-ngx/src/app/shared/components/entity/entity-subtype-list.component.ts +++ b/ui-ngx/src/app/shared/components/entity/entity-subtype-list.component.ts @@ -298,7 +298,7 @@ export class EntitySubTypeListComponent implements ControlValueAccessor, OnInit, } else { result = subTypes.filter(subType => searchText ? subType.toUpperCase().startsWith(searchText.toUpperCase()) : true); } - if (!result.length) { + if (!result.length && searchText.length) { result = [searchText]; } return result; From 2677eff78e1c9c20ce05647a235d48731e37135c Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Fri, 27 Oct 2023 17:06:52 +0300 Subject: [PATCH 008/252] UI: Fixed width for alarm rule in dialog --- .../components/profile/add-device-profile-dialog.component.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.scss b/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.scss index ca54ae2053..c2cc55fb83 100644 --- a/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.scss +++ b/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.scss @@ -45,6 +45,7 @@ overflow: hidden; .mat-horizontal-stepper-wrapper { + max-width: 100%; flex: 1 1 100%; } From 6adbc1ad1e6f04649608c02ce1365474ca51e1c3 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Mon, 30 Oct 2023 11:33:32 +0200 Subject: [PATCH 009/252] UI: Fixed panel for long list --- .../components/widget/lib/display-columns-panel.component.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/display-columns-panel.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/display-columns-panel.component.scss index b1abff0335..f3b531f32e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/display-columns-panel.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/display-columns-panel.component.scss @@ -16,8 +16,9 @@ :host { width: 100%; height: 100%; + max-height: 80vh; min-width: 300px; - overflow: hidden; + overflow: auto; background: #fff; border-radius: 4px; box-shadow: From d53dbd698837d5b78e5e27ecec329ffdcfa29a04 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Mon, 30 Oct 2023 14:06:55 +0200 Subject: [PATCH 010/252] Migrate resource's data from Base64 to bytea --- .../main/data/upgrade/3.6.1/schema_update.sql | 27 ++++ .../controller/TbResourceController.java | 70 +++++----- .../controller/TenantProfileController.java | 1 + .../install/ThingsboardInstallService.java | 4 + .../service/install/InstallScripts.java | 5 +- .../install/SqlDatabaseUpgradeService.java | 120 ++++++++---------- .../resource/DefaultTbResourceService.java | 15 --- .../server/utils/LwM2mObjectModelUtils.java | 10 +- .../controller/TbResourceControllerTest.java | 57 +++++---- .../sql/BaseTbResourceServiceTest.java | 58 ++++++--- .../lwm2m/AbstractLwM2MIntegrationTest.java | 2 +- .../server/common/data/TbResource.java | 51 ++++---- .../server/common/data/TbResourceInfo.java | 37 ++---- .../DefaultTenantProfileConfiguration.java | 1 + .../server/LwM2mVersionedModelProvider.java | 3 +- .../dao/model/sql/TbResourceEntity.java | 4 +- .../dao/model/sql/TbResourceInfoEntity.java | 6 + .../dao/resource/BaseResourceService.java | 33 ++++- .../server/dao/service/DataValidator.java | 3 +- .../validator/ResourceDataValidator.java | 22 +++- .../main/resources/sql/schema-entities.sql | 2 +- .../server/dao/service/TenantServiceTest.java | 3 +- ui-ngx/src/app/core/http/resource.service.ts | 4 - ...enant-profile-configuration.component.html | 16 +++ ...-tenant-profile-configuration.component.ts | 1 + .../resources-library-table-config.resolve.ts | 2 +- .../resource/resources-library.component.html | 2 +- .../resource/resources-library.component.ts | 12 +- .../src/app/shared/models/resource.models.ts | 5 +- ui-ngx/src/app/shared/models/tenant.model.ts | 2 + .../assets/locale/locale.constant-en_US.json | 9 +- 31 files changed, 335 insertions(+), 252 deletions(-) create mode 100644 application/src/main/data/upgrade/3.6.1/schema_update.sql diff --git a/application/src/main/data/upgrade/3.6.1/schema_update.sql b/application/src/main/data/upgrade/3.6.1/schema_update.sql new file mode 100644 index 0000000000..5ebe39fb6e --- /dev/null +++ b/application/src/main/data/upgrade/3.6.1/schema_update.sql @@ -0,0 +1,27 @@ +-- +-- Copyright © 2016-2023 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. +-- + +DO +$$ + BEGIN + IF NOT EXISTS(SELECT 1 FROM information_schema.columns WHERE table_name = 'resource' AND column_name = 'data' AND data_type = 'bytea') THEN + ALTER TABLE resource RENAME COLUMN data TO base64_data; + ALTER TABLE resource ADD COLUMN data bytea; + UPDATE resource SET data = decode(base64_data, 'base64') WHERE base64_data IS NOT NULL; + ALTER TABLE resource DROP COLUMN base64_data; + END IF; + END; +$$; diff --git a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java index 94e318f37c..de18849b3a 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java @@ -26,7 +26,10 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; @@ -87,15 +90,15 @@ public class TbResourceController extends BaseController { @ApiOperation(value = "Download Resource (downloadResource)", notes = "Download Resource based on the provided Resource Id." + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/resource/{resourceId}/download", method = RequestMethod.GET) + @GetMapping(value = "/resource/{resourceId}/download") @ResponseBody - public ResponseEntity downloadResource(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION) - @PathVariable(RESOURCE_ID) String strResourceId) throws ThingsboardException { + public ResponseEntity downloadResource(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION) + @PathVariable(RESOURCE_ID) String strResourceId) throws ThingsboardException { checkParameter(RESOURCE_ID, strResourceId); TbResourceId resourceId = new TbResourceId(toUUID(strResourceId)); TbResource tbResource = checkResourceId(resourceId, Operation.READ); - ByteArrayResource resource = new ByteArrayResource(Base64.getDecoder().decode(tbResource.getData().getBytes())); + ByteArrayResource resource = new ByteArrayResource(tbResource.getData()); return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + tbResource.getFileName()) .header("x-filename", tbResource.getFileName()) @@ -106,11 +109,11 @@ public class TbResourceController extends BaseController { @ApiOperation(value = "Download LWM2M Resource (downloadLwm2mResourceIfChanged)", notes = DOWNLOAD_RESOURCE_IF_NOT_CHANGED + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/resource/lwm2m/{resourceId}/download", method = RequestMethod.GET, produces = "application/xml") + @GetMapping(value = "/resource/lwm2m/{resourceId}/download", produces = "application/xml") @ResponseBody - public ResponseEntity downloadLwm2mResourceIfChanged(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION) - @PathVariable(RESOURCE_ID) String strResourceId, - @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws ThingsboardException { + public ResponseEntity downloadLwm2mResourceIfChanged(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION) + @PathVariable(RESOURCE_ID) String strResourceId, + @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws ThingsboardException { return downloadResourceIfChanged(ResourceType.LWM2M_MODEL, strResourceId, etag); } @@ -118,30 +121,30 @@ public class TbResourceController extends BaseController { @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @RequestMapping(value = "/resource/pkcs12/{resourceId}/download", method = RequestMethod.GET, produces = "application/x-pkcs12") @ResponseBody - public ResponseEntity downloadPkcs12ResourceIfChanged(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION) - @PathVariable(RESOURCE_ID) String strResourceId, - @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws ThingsboardException { + public ResponseEntity downloadPkcs12ResourceIfChanged(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION) + @PathVariable(RESOURCE_ID) String strResourceId, + @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws ThingsboardException { return downloadResourceIfChanged(ResourceType.PKCS_12, strResourceId, etag); } @ApiOperation(value = "Download JKS Resource (downloadJksResourceIfChanged)", notes = DOWNLOAD_RESOURCE_IF_NOT_CHANGED + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/resource/jks/{resourceId}/download", method = RequestMethod.GET, produces = "application/x-java-keystore") + @GetMapping(value = "/resource/jks/{resourceId}/download", produces = "application/x-java-keystore") @ResponseBody - public ResponseEntity downloadJksResourceIfChanged(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION) - @PathVariable(RESOURCE_ID) String strResourceId, - @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws ThingsboardException { + public ResponseEntity downloadJksResourceIfChanged(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION) + @PathVariable(RESOURCE_ID) String strResourceId, + @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws ThingsboardException { return downloadResourceIfChanged(ResourceType.JKS, strResourceId, etag); } @ApiOperation(value = "Download JS Resource (downloadJsResourceIfChanged)", notes = DOWNLOAD_RESOURCE_IF_NOT_CHANGED + AVAILABLE_FOR_ANY_AUTHORIZED_USER) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/resource/js/{resourceId}/download", method = RequestMethod.GET, produces = "application/javascript") + @GetMapping(value = "/resource/js/{resourceId}/download", produces = "application/javascript") @ResponseBody - public ResponseEntity downloadJsResourceIfChanged(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION) - @PathVariable(RESOURCE_ID) String strResourceId, - @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws ThingsboardException { + public ResponseEntity downloadJsResourceIfChanged(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION) + @PathVariable(RESOURCE_ID) String strResourceId, + @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws ThingsboardException { return downloadResourceIfChanged(ResourceType.JS_MODULE, strResourceId, etag); } @@ -150,7 +153,7 @@ public class TbResourceController extends BaseController { RESOURCE_INFO_DESCRIPTION + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH, produces = "application/json") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/resource/info/{resourceId}", method = RequestMethod.GET) + @GetMapping(value = "/resource/info/{resourceId}") @ResponseBody public TbResourceInfo getResourceInfoById(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION) @PathVariable(RESOURCE_ID) String strResourceId) throws ThingsboardException { @@ -162,15 +165,18 @@ public class TbResourceController extends BaseController { @ApiOperation(value = "Get Resource (getResourceById)", notes = "Fetch the Resource object based on the provided Resource Id. " + RESOURCE_DESCRIPTION + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH, - produces = "application/json") + produces = "application/json", hidden = true) + @Deprecated // resource's data should be fetched with a download request @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/resource/{resourceId}", method = RequestMethod.GET) + @GetMapping(value = "/resource/{resourceId}") @ResponseBody public TbResource getResourceById(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION) @PathVariable(RESOURCE_ID) String strResourceId) throws ThingsboardException { checkParameter(RESOURCE_ID, strResourceId); TbResourceId resourceId = new TbResourceId(toUUID(strResourceId)); - return checkResourceId(resourceId, Operation.READ); + TbResource resource = checkResourceId(resourceId, Operation.READ); + resource.setBase64Data(Base64.getEncoder().encodeToString(resource.getData())); + return resource; } @ApiOperation(value = "Create Or Update Resource (saveResource)", @@ -184,10 +190,10 @@ public class TbResourceController extends BaseController { produces = "application/json", consumes = "application/json") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/resource", method = RequestMethod.POST) + @PostMapping(value = "/resource") @ResponseBody - public TbResource saveResource(@ApiParam(value = "A JSON value representing the Resource.") - @RequestBody TbResource resource) throws Exception { + public TbResourceInfo saveResource(@ApiParam(value = "A JSON value representing the Resource.") + @RequestBody TbResource resource) throws Exception { resource.setTenantId(getTenantId()); checkEntity(resource.getId(), resource, Resource.TB_RESOURCE); return tbResourceService.save(resource, getCurrentUser()); @@ -198,7 +204,7 @@ public class TbResourceController extends BaseController { PAGE_DATA_PARAMETERS + RESOURCE_INFO_DESCRIPTION + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH, produces = "application/json") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/resource", method = RequestMethod.GET) + @GetMapping(value = "/resource") @ResponseBody public PageData getResources(@ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true) @RequestParam int pageSize, @@ -230,7 +236,7 @@ public class TbResourceController extends BaseController { PAGE_DATA_PARAMETERS + LWM2M_OBJECT_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH, produces = "application/json") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/resource/lwm2m/page", method = RequestMethod.GET) + @GetMapping(value = "/resource/lwm2m/page") @ResponseBody public List getLwm2mListObjectsPage(@ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true) @RequestParam int pageSize, @@ -251,7 +257,7 @@ public class TbResourceController extends BaseController { "You can specify parameters to filter the results. " + LWM2M_OBJECT_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH, produces = "application/json") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/resource/lwm2m", method = RequestMethod.GET) + @GetMapping(value = "/resource/lwm2m") @ResponseBody public List getLwm2mListObjects(@ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES, required = true) @RequestParam String sortOrder, @@ -265,7 +271,7 @@ public class TbResourceController extends BaseController { @ApiOperation(value = "Delete Resource (deleteResource)", notes = "Deletes the Resource. Referencing non-existing Resource Id will cause an error." + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/resource/{resourceId}", method = RequestMethod.DELETE) + @DeleteMapping(value = "/resource/{resourceId}") @ResponseBody public void deleteResource(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION) @PathVariable("resourceId") String strResourceId) throws ThingsboardException { @@ -275,7 +281,7 @@ public class TbResourceController extends BaseController { tbResourceService.delete(tbResource, getCurrentUser()); } - private ResponseEntity downloadResourceIfChanged(ResourceType type, String strResourceId, String etag) throws ThingsboardException { + private ResponseEntity downloadResourceIfChanged(ResourceType type, String strResourceId, String etag) throws ThingsboardException { checkParameter(RESOURCE_ID, strResourceId); TbResourceId resourceId = new TbResourceId(toUUID(strResourceId)); @@ -289,7 +295,7 @@ public class TbResourceController extends BaseController { } TbResource tbResource = checkResourceId(resourceId, Operation.READ); - ByteArrayResource resource = new ByteArrayResource(Base64.getDecoder().decode(tbResource.getData().getBytes())); + ByteArrayResource resource = new ByteArrayResource(tbResource.getData()); return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + tbResource.getFileName()) diff --git a/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java b/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java index 6fbee00f7f..1fe492d692 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java @@ -133,6 +133,7 @@ public class TenantProfileController extends BaseController { " \"maxRuleChains\": 0,\n" + " \"maxResourcesInBytes\": 0,\n" + " \"maxOtaPackagesInBytes\": 0,\n" + + " \"maxResourceSize\": 0,\n" + " \"transportTenantMsgRateLimit\": \"1000:1,20000:60\",\n" + " \"transportTenantTelemetryMsgRateLimit\": \"1000:1,20000:60\",\n" + " \"transportTenantTelemetryDataPointsRateLimit\": \"1000:1,20000:60\",\n" + diff --git a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java index 3829b3cf45..a42194a28d 100644 --- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java +++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java @@ -265,6 +265,10 @@ public class ThingsboardInstallService { case "3.6.0": log.info("Upgrading ThingsBoard from version 3.6.0 to 3.6.1 ..."); databaseEntitiesUpgradeService.upgradeDatabase("3.6.0"); + break; + case "3.6.1": + log.info("Upgrading ThingsBoard from version 3.6.1 to 3.6.2 ..."); + databaseEntitiesUpgradeService.upgradeDatabase("3.6.1"); //TODO DON'T FORGET to update switch statement in the CacheCleanupService if you need to clear the cache break; default: diff --git a/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java b/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java index 7f0b70cb27..00627e0421 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java +++ b/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java @@ -47,7 +47,6 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; -import java.util.Base64; import java.util.List; import java.util.Optional; import java.util.function.Function; @@ -309,7 +308,7 @@ public class InstallScripts { dirStream.forEach( path -> { try { - String data = Base64.getEncoder().encodeToString(Files.readAllBytes(path)); + byte[] data = Files.readAllBytes(path); TbResource tbResource = new TbResource(); tbResource.setTenantId(TenantId.SYS_TENANT_ID); tbResource.setData(data); @@ -330,7 +329,7 @@ public class InstallScripts { private void doSaveLwm2mResource(TbResource resource) throws ThingsboardException { log.trace("Executing saveResource [{}]", resource); - if (StringUtils.isEmpty(resource.getData())) { + if (resource.getData() == null || resource.getData().length == 0) { throw new DataValidationException("Resource data should be specified!"); } toLwm2mResource(resource); diff --git a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java index c02b7e9cc1..a4e9d61a74 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java @@ -63,6 +63,7 @@ import java.util.ArrayList; import java.util.List; import java.util.UUID; import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; import static org.thingsboard.server.service.install.DatabaseHelper.ADDITIONAL_INFO; import static org.thingsboard.server.service.install.DatabaseHelper.ASSIGNED_CUSTOMERS; @@ -717,86 +718,73 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService } break; case "3.5.0": - try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { - log.info("Updating schema ..."); - if (isOldSchema(conn, 3005000)) { - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.5.0", SCHEMA_UPDATE_SQL); - loadSql(schemaUpdateFile, conn); - conn.createStatement().execute("UPDATE tb_schema_settings SET schema_version = 3005001;"); - } - log.info("Schema updated."); - } catch (Exception e) { - log.error("Failed updating schema!!!", e); - } + updateSchema("3.5.0", 3005000, "3.5.1", 3005001, null); break; case "3.5.1": - try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { - if (isOldSchema(conn, 3005001)) { - log.info("Updating schema ..."); - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.5.1", SCHEMA_UPDATE_SQL); - loadSql(schemaUpdateFile, conn); - - String[] entityNames = new String[]{"device", "component_descriptor", "customer", "dashboard", "rule_chain", "rule_node", "ota_package", - "asset_profile", "asset", "device_profile", "tb_user", "tenant_profile", "tenant", "widgets_bundle", "entity_view", "edge"}; - for (String entityName : entityNames) { - try { - conn.createStatement().execute("ALTER TABLE " + entityName + " DROP COLUMN " + SEARCH_TEXT + " CASCADE"); - } catch (Exception e) { - } - } + updateSchema("3.5.1", 3005001, "3.6.0", 3006000, conn -> { + String[] entityNames = new String[]{"device", "component_descriptor", "customer", "dashboard", "rule_chain", "rule_node", "ota_package", + "asset_profile", "asset", "device_profile", "tb_user", "tenant_profile", "tenant", "widgets_bundle", "entity_view", "edge"}; + for (String entityName : entityNames) { try { - conn.createStatement().execute("ALTER TABLE component_descriptor ADD COLUMN IF NOT EXISTS configuration_version int DEFAULT 0;"); + conn.createStatement().execute("ALTER TABLE " + entityName + " DROP COLUMN " + SEARCH_TEXT + " CASCADE"); } catch (Exception e) { } - try { - conn.createStatement().execute("ALTER TABLE rule_node ADD COLUMN IF NOT EXISTS configuration_version int DEFAULT 0;"); - } catch (Exception e) { - } - try { - conn.createStatement().execute("CREATE INDEX IF NOT EXISTS idx_rule_node_type_configuration_version ON rule_node(type, configuration_version);"); - } catch (Exception e) { - } - try { - conn.createStatement().execute("UPDATE rule_node SET " + - "configuration = (configuration::jsonb || '{\"updateAttributesOnlyOnValueChange\": \"false\"}'::jsonb)::varchar, " + - "configuration_version = 1 " + - "WHERE type = 'org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode' AND configuration_version < 1;"); - } catch (Exception e) { - } - try { - conn.createStatement().execute("CREATE INDEX IF NOT EXISTS idx_notification_recipient_id_unread ON notification(recipient_id) WHERE status <> 'READ';"); - } catch (Exception e) { - } - - conn.createStatement().execute("UPDATE tb_schema_settings SET schema_version = 3006000;"); - log.info("Schema updated to version 3.6.0."); - } else { - log.info("Skip schema re-update to version 3.6.0. Use env flag 'SKIP_SCHEMA_VERSION_CHECK' to force the re-update."); } - } catch (Exception e) { - log.error("Failed updating schema!!!", e); - } + try { + conn.createStatement().execute("ALTER TABLE component_descriptor ADD COLUMN IF NOT EXISTS configuration_version int DEFAULT 0;"); + } catch (Exception e) { + } + try { + conn.createStatement().execute("ALTER TABLE rule_node ADD COLUMN IF NOT EXISTS configuration_version int DEFAULT 0;"); + } catch (Exception e) { + } + try { + conn.createStatement().execute("CREATE INDEX IF NOT EXISTS idx_rule_node_type_configuration_version ON rule_node(type, configuration_version);"); + } catch (Exception e) { + } + try { + conn.createStatement().execute("UPDATE rule_node SET " + + "configuration = (configuration::jsonb || '{\"updateAttributesOnlyOnValueChange\": \"false\"}'::jsonb)::varchar, " + + "configuration_version = 1 " + + "WHERE type = 'org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode' AND configuration_version < 1;"); + } catch (Exception e) { + } + try { + conn.createStatement().execute("CREATE INDEX IF NOT EXISTS idx_notification_recipient_id_unread ON notification(recipient_id) WHERE status <> 'READ';"); + } catch (Exception e) { + } + }); break; case "3.6.0": - try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { - if (isOldSchema(conn, 3006000)) { - log.info("Updating schema ..."); - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.6.0", SCHEMA_UPDATE_SQL); - loadSql(schemaUpdateFile, conn); - conn.createStatement().execute("UPDATE tb_schema_settings SET schema_version = 3006001;"); - log.info("Schema updated to version 3.6.1."); - } else { - log.info("Skip schema re-update to version 3.6.1. Use env flag 'SKIP_SCHEMA_VERSION_CHECK' to force the re-update."); - } - } catch (Exception e) { - log.error("Failed updating schema!!!", e); - } + updateSchema("3.6.0", 3006000, "3.6.1", 3006001, null); + break; + case "3.6.1": + updateSchema("3.6.1", 3006001, "3.6.2", 3006002, null); break; default: throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion); } } + private void updateSchema(String oldVersionStr, int oldVersion, String newVersionStr, int newVersion, Consumer additionalAction) { + try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { + log.info("Updating schema ..."); + if (isOldSchema(conn, oldVersion)) { + Path schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", oldVersionStr, SCHEMA_UPDATE_SQL); + loadSql(schemaUpdateFile, conn); + if (additionalAction != null) { + additionalAction.accept(conn); + } + conn.createStatement().execute("UPDATE tb_schema_settings SET schema_version = " + newVersion + ";"); + log.info("Schema updated to version {}", newVersionStr); + } else { + log.info("Skip schema re-update to version {}. Use env flag 'SKIP_SCHEMA_VERSION_CHECK' to force the re-update.", newVersionStr); + } + } catch (Exception e) { + log.error("Failed updating schema!!!", e); + } + } + private void runSchemaUpdateScript(Connection connection, String version) throws Exception { Path schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", version, SCHEMA_UPDATE_SQL); loadSql(schemaUpdateFile, connection); diff --git a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java index 064511abd9..d1ce667345 100644 --- a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java +++ b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java @@ -15,34 +15,26 @@ */ package org.thingsboard.server.service.resource; -import com.google.common.hash.HashCode; -import com.google.common.hash.Hashing; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ResourceType; -import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.TbResourceInfo; import org.thingsboard.server.common.data.TbResourceInfoFilter; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.audit.ActionType; -import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.lwm2m.LwM2mObject; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; -import org.thingsboard.server.common.data.widget.BaseWidgetType; -import org.thingsboard.server.common.data.widget.WidgetTypeDetails; -import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.widget.WidgetTypeService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.entitiy.AbstractTbEntityService; -import java.util.Base64; import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; @@ -166,18 +158,11 @@ public class DefaultTbResourceService extends AbstractTbEntityService implements } private TbResource doSave(TbResource resource) throws ThingsboardException { - log.trace("Executing saveResource [{}]", resource); - if (StringUtils.isEmpty(resource.getData())) { - throw new DataValidationException("Resource data should be specified!"); - } if (ResourceType.LWM2M_MODEL.equals(resource.getResourceType())) { toLwm2mResource(resource); } else { resource.setResourceKey(resource.getFileName()); } - HashCode hashCode = Hashing.sha256().hashBytes(Base64.getDecoder().decode(resource.getData().getBytes())); - resource.setEtag(hashCode.toString()); return resourceService.saveResource(resource); } } - diff --git a/application/src/main/java/org/thingsboard/server/utils/LwM2mObjectModelUtils.java b/application/src/main/java/org/thingsboard/server/utils/LwM2mObjectModelUtils.java index 0deb2f6bb7..b73ba09541 100644 --- a/application/src/main/java/org/thingsboard/server/utils/LwM2mObjectModelUtils.java +++ b/application/src/main/java/org/thingsboard/server/utils/LwM2mObjectModelUtils.java @@ -39,13 +39,13 @@ import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPA @Slf4j public class LwM2mObjectModelUtils { - + private static final TbDDFFileParser ddfFileParser = new TbDDFFileParser(); - - public static void toLwm2mResource (TbResource resource) throws ThingsboardException { + + public static void toLwm2mResource(TbResource resource) throws ThingsboardException { try { List objectModels = - ddfFileParser.parse(new ByteArrayInputStream(Base64.getDecoder().decode(resource.getData())), resource.getSearchText()); + ddfFileParser.parse(new ByteArrayInputStream(resource.getData()), resource.getSearchText()); if (!objectModels.isEmpty()) { ObjectModel objectModel = objectModels.get(0); @@ -73,7 +73,7 @@ public class LwM2mObjectModelUtils { public static LwM2mObject toLwM2mObject(TbResource resource, boolean isSave) { try { List objectModels = - ddfFileParser.parse(new ByteArrayInputStream(Base64.getDecoder().decode(resource.getData())), resource.getSearchText()); + ddfFileParser.parse(new ByteArrayInputStream(resource.getData()), resource.getSearchText()); if (objectModels.size() == 0) { return null; } else { diff --git a/application/src/test/java/org/thingsboard/server/controller/TbResourceControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/TbResourceControllerTest.java index 8725f43be1..0e05165c77 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TbResourceControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/TbResourceControllerTest.java @@ -20,7 +20,6 @@ import com.fasterxml.jackson.databind.JsonNode; import org.junit.After; import org.junit.Assert; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.mockito.Mockito; import org.springframework.http.HttpHeaders; @@ -35,11 +34,11 @@ import org.thingsboard.server.common.data.TbResourceInfo; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.widget.WidgetTypeDetails; -import org.thingsboard.server.common.data.widget.WidgetsBundle; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DaoSqlTest; @@ -54,6 +53,11 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @DaoSqlTest public class TbResourceControllerTest extends AbstractControllerTest { + /* + * TODO: + * test maxResourceSize limit in tenant profile + * */ + private IdComparator idComparator = new IdComparator<>(); private static final String DEFAULT_FILE_NAME = "test.jks"; @@ -101,7 +105,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JKS); resource.setTitle("My first resource"); resource.setFileName(DEFAULT_FILE_NAME); - resource.setData(TEST_DATA); + resource.setBase64Data(TEST_DATA); TbResource savedResource = save(resource); @@ -116,7 +120,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { Assert.assertEquals(resource.getTitle(), savedResource.getTitle()); Assert.assertEquals(DEFAULT_FILE_NAME, savedResource.getFileName()); Assert.assertEquals(DEFAULT_FILE_NAME, savedResource.getResourceKey()); - Assert.assertEquals(resource.getData(), savedResource.getData()); + Assert.assertArrayEquals(resource.getData(), download(savedResource.getId())); savedResource.setTitle("My new resource"); @@ -136,7 +140,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JKS); resource.setTitle(StringUtils.randomAlphabetic(300)); resource.setFileName(DEFAULT_FILE_NAME); - resource.setData(TEST_DATA); + resource.setBase64Data(TEST_DATA); Mockito.reset(tbClusterService, auditLogService); @@ -155,7 +159,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JKS); resource.setTitle("My first resource"); resource.setFileName(DEFAULT_FILE_NAME); - resource.setData(TEST_DATA); + resource.setBase64Data(TEST_DATA); TbResource savedResource = save(resource); @@ -184,13 +188,14 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JKS); resource.setTitle("My first resource"); resource.setFileName(DEFAULT_FILE_NAME); - resource.setData(TEST_DATA); + resource.setBase64Data(TEST_DATA); TbResource savedResource = save(resource); - TbResource foundResource = doGet("/api/resource/" + savedResource.getId().getId().toString(), TbResource.class); + TbResource foundResource = doGet("/api/resource/" + savedResource.getUuidId(), TbResource.class); Assert.assertNotNull(foundResource); - Assert.assertEquals(savedResource, foundResource); + Assert.assertEquals(savedResource.getId(), foundResource.getId()); + Assert.assertEquals(savedResource.getFileName(), foundResource.getFileName()); } @Test @@ -199,7 +204,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JKS); resource.setTitle("My first resource"); resource.setFileName(DEFAULT_FILE_NAME); - resource.setData(TEST_DATA); + resource.setBase64Data(TEST_DATA); TbResource savedResource = save(resource); @@ -213,7 +218,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.DELETED, resourceIdStr); - doGet("/api/resource/" + savedResource.getId().getId().toString()) + doGet("/api/resource/" + savedResource.getUuidId()) .andExpect(status().isNotFound()) .andExpect(statusReason(containsString(msgErrorNoFound("Resource", resourceIdStr)))); } @@ -224,7 +229,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JKS); resource.setTitle("My first resource"); resource.setFileName(DEFAULT_FILE_NAME); - resource.setData(TEST_DATA); + resource.setBase64Data(TEST_DATA); TbResource savedResource = save(resource); @@ -255,7 +260,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setTitle("Resource" + i); resource.setResourceType(ResourceType.JKS); resource.setFileName(i + DEFAULT_FILE_NAME); - resource.setData(TEST_DATA); + resource.setBase64Data(TEST_DATA); resources.add(new TbResourceInfo(save(resource))); } List loadedResources = new ArrayList<>(); @@ -292,7 +297,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setTitle("JKS Resource" + i); resource.setResourceType(ResourceType.JKS); resource.setFileName(i + DEFAULT_FILE_NAME); - resource.setData(TEST_DATA); + resource.setBase64Data(TEST_DATA); resources.add(new TbResourceInfo(save(resource))); } @@ -302,7 +307,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setTitle("LWM2M Resource" + i); resource.setResourceType(ResourceType.PKCS_12); resource.setFileName(i + DEFAULT_FILE_NAME_2); - resource.setData(TEST_DATA); + resource.setBase64Data(TEST_DATA); save(resource); } @@ -339,7 +344,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setTitle("Resource" + i); resource.setResourceType(ResourceType.JKS); resource.setFileName(i + DEFAULT_FILE_NAME); - resource.setData(TEST_DATA); + resource.setBase64Data(TEST_DATA); resources.add(new TbResourceInfo(save(resource))); } List loadedResources = new ArrayList<>(); @@ -399,7 +404,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setTitle("JKS Resource" + i); resource.setResourceType(ResourceType.JKS); resource.setFileName(i + DEFAULT_FILE_NAME); - resource.setData(TEST_DATA); + resource.setBase64Data(TEST_DATA); TbResourceInfo saved = new TbResourceInfo(save(resource)); jksResources.add(saved); } @@ -410,7 +415,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setTitle("LWM2M Resource" + i); resource.setResourceType(ResourceType.PKCS_12); resource.setFileName(i + DEFAULT_FILE_NAME_2); - resource.setData(TEST_DATA); + resource.setBase64Data(TEST_DATA); TbResource saved = save(resource); lwm2mesources.add(saved); } @@ -476,7 +481,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setTitle("Resource" + i); resource.setResourceType(ResourceType.JKS); resource.setFileName(i + DEFAULT_FILE_NAME); - resource.setData(TEST_DATA); + resource.setBase64Data(TEST_DATA); expectedResources.add(new TbResourceInfo(save(resource))); } @@ -487,7 +492,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setTitle("Resource" + i); resource.setResourceType(ResourceType.JKS); resource.setFileName(i + DEFAULT_FILE_NAME); - resource.setData(TEST_DATA); + resource.setBase64Data(TEST_DATA); TbResourceInfo savedResource = new TbResourceInfo(save(resource)); systemResources.add(savedResource); if (i >= 73) { @@ -531,7 +536,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JS_MODULE); resource.setTitle("Js resource"); resource.setFileName(JS_TEST_FILE_NAME); - resource.setData(TEST_DATA); + resource.setBase64Data(TEST_DATA); TbResource savedResource = save(resource); @@ -562,7 +567,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JS_MODULE); resource.setTitle("Js resource"); resource.setFileName(JS_TEST_FILE_NAME); - resource.setData(TEST_DATA); + resource.setBase64Data(TEST_DATA); TbResource savedResource = save(resource); @@ -606,7 +611,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JS_MODULE); resource.setTitle("Js resource"); resource.setFileName(JS_TEST_FILE_NAME); - resource.setData(TEST_DATA); + resource.setBase64Data(TEST_DATA); TbResource savedResource = save(resource); @@ -619,4 +624,10 @@ public class TbResourceControllerTest extends AbstractControllerTest { private TbResource save(TbResource tbResource) throws Exception { return doPostWithTypedResponse("/api/resource", tbResource, new TypeReference<>(){}); } + + private byte[] download(TbResourceId resourceId) throws Exception { + return doGet("/api/resource/" + resourceId + "/download") + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsByteArray(); + } } diff --git a/application/src/test/java/org/thingsboard/server/service/resource/sql/BaseTbResourceServiceTest.java b/application/src/test/java/org/thingsboard/server/service/resource/sql/BaseTbResourceServiceTest.java index 6f08e47412..ab55840f3c 100644 --- a/application/src/test/java/org/thingsboard/server/service/resource/sql/BaseTbResourceServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/resource/sql/BaseTbResourceServiceTest.java @@ -30,6 +30,7 @@ import org.thingsboard.server.common.data.TbResourceInfoFilter; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; @@ -46,6 +47,7 @@ import java.util.Collections; import java.util.List; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThrows; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -105,7 +107,8 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { ""; private static final String DEFAULT_FILE_NAME = "test.jks"; - private static final String TEST_DATA = "77u/PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCEtLQpGSUxFIElORk9STUFUSU9OCgpPTUEgUGVybWFuZW50IERvY3VtZW50CiAgIEZpbGU6IE9NQS1TVVAtTHdNMk1fQmluYXJ5QXBwRGF0YUNvbnRhaW5lci1WMV8wXzEtMjAxOTAyMjEtQQogICBUeXBlOiB4bWwKClB1YmxpYyBSZWFjaGFibGUgSW5mb3JtYXRpb24KICAgUGF0aDogaHR0cDovL3d3dy5vcGVubW9iaWxlYWxsaWFuY2Uub3JnL3RlY2gvcHJvZmlsZXMKICAgTmFtZTogTHdNMk1fQmluYXJ5QXBwRGF0YUNvbnRhaW5lci12MV8wXzEueG1sCgpOT1JNQVRJVkUgSU5GT1JNQVRJT04KCiAgSW5mb3JtYXRpb24gYWJvdXQgdGhpcyBmaWxlIGNhbiBiZSBmb3VuZCBpbiB0aGUgbGF0ZXN0IHJldmlzaW9uIG9mCgogIE9NQS1UUy1MV00yTV9CaW5hcnlBcHBEYXRhQ29udGFpbmVyLVYxXzBfMQoKICBUaGlzIGlzIGF2YWlsYWJsZSBhdCBodHRwOi8vd3d3Lm9wZW5tb2JpbGVhbGxpYW5jZS5vcmcvCgogIFNlbmQgY29tbWVudHMgdG8gaHR0cHM6Ly9naXRodWIuY29tL09wZW5Nb2JpbGVBbGxpYW5jZS9PTUFfTHdNMk1fZm9yX0RldmVsb3BlcnMvaXNzdWVzCgpDSEFOR0UgSElTVE9SWQoKMTUwNjIwMTggU3RhdHVzIGNoYW5nZWQgdG8gQXBwcm92ZWQgYnkgRE0sIERvYyBSZWYgIyBPTUEtRE0mU0UtMjAxOC0wMDYxLUlOUF9MV00yTV9BUFBEQVRBX1YxXzBfRVJQX2Zvcl9maW5hbF9BcHByb3ZhbAoyMTAyMjAxOSBTdGF0dXMgY2hhbmdlZCB0byBBcHByb3ZlZCBieSBJUFNPLCBEb2MgUmVmICMgT01BLUlQU08tMjAxOS0wMDI1LUlOUF9Md00yTV9PYmplY3RfQXBwX0RhdGFfQ29udGFpbmVyXzFfMF8xX2Zvcl9GaW5hbF9BcHByb3ZhbAoKTEVHQUwgRElTQ0xBSU1FUgoKQ29weXJpZ2h0IDIwMTkgT3BlbiBNb2JpbGUgQWxsaWFuY2UuCgpSZWRpc3RyaWJ1dGlvbiBhbmQgdXNlIGluIHNvdXJjZSBhbmQgYmluYXJ5IGZvcm1zLCB3aXRoIG9yIHdpdGhvdXQKbW9kaWZpY2F0aW9uLCBhcmUgcGVybWl0dGVkIHByb3ZpZGVkIHRoYXQgdGhlIGZvbGxvd2luZyBjb25kaXRpb25zCmFyZSBtZXQ6CgoxLiBSZWRpc3RyaWJ1dGlvbnMgb2Ygc291cmNlIGNvZGUgbXVzdCByZXRhaW4gdGhlIGFib3ZlIGNvcHlyaWdodApub3RpY2UsIHRoaXMgbGlzdCBvZiBjb25kaXRpb25zIGFuZCB0aGUgZm9sbG93aW5nIGRpc2NsYWltZXIuCjIuIFJlZGlzdHJpYnV0aW9ucyBpbiBiaW5hcnkgZm9ybSBtdXN0IHJlcHJvZHVjZSB0aGUgYWJvdmUgY29weXJpZ2h0Cm5vdGljZSwgdGhpcyBsaXN0IG9mIGNvbmRpdGlvbnMgYW5kIHRoZSBmb2xsb3dpbmcgZGlzY2xhaW1lciBpbiB0aGUKZG9jdW1lbnRhdGlvbiBhbmQvb3Igb3RoZXIgbWF0ZXJpYWxzIHByb3ZpZGVkIHdpdGggdGhlIGRpc3RyaWJ1dGlvbi4KMy4gTmVpdGhlciB0aGUgbmFtZSBvZiB0aGUgY29weXJpZ2h0IGhvbGRlciBub3IgdGhlIG5hbWVzIG9mIGl0cwpjb250cmlidXRvcnMgbWF5IGJlIHVzZWQgdG8gZW5kb3JzZSBvciBwcm9tb3RlIHByb2R1Y3RzIGRlcml2ZWQKZnJvbSB0aGlzIHNvZnR3YXJlIHdpdGhvdXQgc3BlY2lmaWMgcHJpb3Igd3JpdHRlbiBwZXJtaXNzaW9uLgoKVEhJUyBTT0ZUV0FSRSBJUyBQUk9WSURFRCBCWSBUSEUgQ09QWVJJR0hUIEhPTERFUlMgQU5EIENPTlRSSUJVVE9SUwoiQVMgSVMiIEFORCBBTlkgRVhQUkVTUyBPUiBJTVBMSUVEIFdBUlJBTlRJRVMsIElOQ0xVRElORywgQlVUIE5PVApMSU1JVEVEIFRPLCBUSEUgSU1QTElFRCBXQVJSQU5USUVTIE9GIE1FUkNIQU5UQUJJTElUWSBBTkQgRklUTkVTUwpGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UgQVJFIERJU0NMQUlNRUQuIElOIE5PIEVWRU5UIFNIQUxMIFRIRQpDT1BZUklHSFQgSE9MREVSIE9SIENPTlRSSUJVVE9SUyBCRSBMSUFCTEUgRk9SIEFOWSBESVJFQ1QsIElORElSRUNULApJTkNJREVOVEFMLCBTUEVDSUFMLCBFWEVNUExBUlksIE9SIENPTlNFUVVFTlRJQUwgREFNQUdFUyAoSU5DTFVESU5HLApCVVQgTk9UIExJTUlURUQgVE8sIFBST0NVUkVNRU5UIE9GIFNVQlNUSVRVVEUgR09PRFMgT1IgU0VSVklDRVM7CkxPU1MgT0YgVVNFLCBEQVRBLCBPUiBQUk9GSVRTOyBPUiBCVVNJTkVTUyBJTlRFUlJVUFRJT04pIEhPV0VWRVIKQ0FVU0VEIEFORCBPTiBBTlkgVEhFT1JZIE9GIExJQUJJTElUWSwgV0hFVEhFUiBJTiBDT05UUkFDVCwgU1RSSUNUCkxJQUJJTElUWSwgT1IgVE9SVCAoSU5DTFVESU5HIE5FR0xJR0VOQ0UgT1IgT1RIRVJXSVNFKSBBUklTSU5HIElOCkFOWSBXQVkgT1VUIE9GIFRIRSBVU0UgT0YgVEhJUyBTT0ZUV0FSRSwgRVZFTiBJRiBBRFZJU0VEIE9GIFRIRQpQT1NTSUJJTElUWSBPRiBTVUNIIERBTUFHRS4KClRoZSBhYm92ZSBsaWNlbnNlIGlzIHVzZWQgYXMgYSBsaWNlbnNlIHVuZGVyIGNvcHlyaWdodCBvbmx5LiBQbGVhc2UKcmVmZXJlbmNlIHRoZSBPTUEgSVBSIFBvbGljeSBmb3IgcGF0ZW50IGxpY2Vuc2luZyB0ZXJtczoKaHR0cHM6Ly93d3cub21hc3BlY3dvcmtzLm9yZy9hYm91dC9pbnRlbGxlY3R1YWwtcHJvcGVydHktcmlnaHRzLwoKLS0+CjxMV00yTSB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4c2k6bm9OYW1lc3BhY2VTY2hlbWFMb2NhdGlvbj0iaHR0cDovL29wZW5tb2JpbGVhbGxpYW5jZS5vcmcvdGVjaC9wcm9maWxlcy9MV00yTS54c2QiPgoJPE9iamVjdCBPYmplY3RUeXBlPSJNT0RlZmluaXRpb24iPgoJCTxOYW1lPkJpbmFyeUFwcERhdGFDb250YWluZXI8L05hbWU+CgkJPERlc2NyaXB0aW9uMT48IVtDREFUQVtUaGlzIEx3TTJNIE9iamVjdHMgcHJvdmlkZXMgdGhlIGFwcGxpY2F0aW9uIHNlcnZpY2UgZGF0YSByZWxhdGVkIHRvIGEgTHdNMk0gU2VydmVyLCBlZy4gV2F0ZXIgbWV0ZXIgZGF0YS4gClRoZXJlIGFyZSBzZXZlcmFsIG1ldGhvZHMgdG8gY3JlYXRlIGluc3RhbmNlIHRvIGluZGljYXRlIHRoZSBtZXNzYWdlIGRpcmVjdGlvbiBiYXNlZCBvbiB0aGUgbmVnb3RpYXRpb24gYmV0d2VlbiBBcHBsaWNhdGlvbiBhbmQgTHdNMk0uIFRoZSBDbGllbnQgYW5kIFNlcnZlciBzaG91bGQgbmVnb3RpYXRlIHRoZSBpbnN0YW5jZShzKSB1c2VkIHRvIGV4Y2hhbmdlIHRoZSBkYXRhLiBGb3IgZXhhbXBsZToKIC0gVXNpbmcgYSBzaW5nbGUgaW5zdGFuY2UgZm9yIGJvdGggZGlyZWN0aW9ucyBjb21tdW5pY2F0aW9uLCBmcm9tIENsaWVudCB0byBTZXJ2ZXIgYW5kIGZyb20gU2VydmVyIHRvIENsaWVudC4KIC0gVXNpbmcgYW4gaW5zdGFuY2UgZm9yIGNvbW11bmljYXRpb24gZnJvbSBDbGllbnQgdG8gU2VydmVyIGFuZCBhbm90aGVyIG9uZSBmb3IgY29tbXVuaWNhdGlvbiBmcm9tIFNlcnZlciB0byBDbGllbnQKIC0gVXNpbmcgc2V2ZXJhbCBpbnN0YW5jZXMKXV0+PC9EZXNjcmlwdGlvbjE+CgkJPE9iamVjdElEPjE5PC9PYmplY3RJRD4KCQk8T2JqZWN0VVJOPnVybjpvbWE6bHdtMm06b21hOjE5PC9PYmplY3RVUk4+CgkJPExXTTJNVmVyc2lvbj4xLjA8L0xXTTJNVmVyc2lvbj4KCQk8T2JqZWN0VmVyc2lvbj4xLjA8L09iamVjdFZlcnNpb24+CgkJPE11bHRpcGxlSW5zdGFuY2VzPk11bHRpcGxlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQk8TWFuZGF0b3J5Pk9wdGlvbmFsPC9NYW5kYXRvcnk+CgkJPFJlc291cmNlcz4KCQkJPEl0ZW0gSUQ9IjAiPjxOYW1lPkRhdGE8L05hbWU+CgkJCQk8T3BlcmF0aW9ucz5SVzwvT3BlcmF0aW9ucz4KCQkJCTxNdWx0aXBsZUluc3RhbmNlcz5NdWx0aXBsZTwvTXVsdGlwbGVJbnN0YW5jZXM+CgkJCQk8TWFuZGF0b3J5Pk1hbmRhdG9yeTwvTWFuZGF0b3J5PgoJCQkJPFR5cGU+T3BhcXVlPC9UeXBlPgoJCQkJPFJhbmdlRW51bWVyYXRpb24gLz4KCQkJCTxVbml0cyAvPgoJCQkJPERlc2NyaXB0aW9uPjwhW0NEQVRBW0luZGljYXRlcyB0aGUgYXBwbGljYXRpb24gZGF0YSBjb250ZW50Ll1dPjwvRGVzY3JpcHRpb24+CgkJCTwvSXRlbT4KCQkJPEl0ZW0gSUQ9IjEiPjxOYW1lPkRhdGEgUHJpb3JpdHk8L05hbWU+CgkJCQk8T3BlcmF0aW9ucz5SVzwvT3BlcmF0aW9ucz4KCQkJCTxNdWx0aXBsZUluc3RhbmNlcz5TaW5nbGU8L011bHRpcGxlSW5zdGFuY2VzPgoJCQkJPE1hbmRhdG9yeT5PcHRpb25hbDwvTWFuZGF0b3J5PgoJCQkJPFR5cGU+SW50ZWdlcjwvVHlwZT4KCQkJCTxSYW5nZUVudW1lcmF0aW9uPjEgYnl0ZXM8L1JhbmdlRW51bWVyYXRpb24+CgkJCQk8VW5pdHMgLz4KCQkJCTxEZXNjcmlwdGlvbj48IVtDREFUQVtJbmRpY2F0ZXMgdGhlIEFwcGxpY2F0aW9uIGRhdGEgcHJpb3JpdHk6CjA6SW1tZWRpYXRlCjE6QmVzdEVmZm9ydAoyOkxhdGVzdAozLTEwMDogUmVzZXJ2ZWQgZm9yIGZ1dHVyZSB1c2UuCjEwMS0yNTQ6IFByb3ByaWV0YXJ5IG1vZGUuXV0+PC9EZXNjcmlwdGlvbj4KCQkJPC9JdGVtPgoJCQk8SXRlbSBJRD0iMiI+PE5hbWU+RGF0YSBDcmVhdGlvbiBUaW1lPC9OYW1lPgoJCQkJPE9wZXJhdGlvbnM+Ulc8L09wZXJhdGlvbnM+CgkJCQk8TXVsdGlwbGVJbnN0YW5jZXM+U2luZ2xlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQkJCTxNYW5kYXRvcnk+T3B0aW9uYWw8L01hbmRhdG9yeT4KCQkJCTxUeXBlPlRpbWU8L1R5cGU+CgkJCQk8UmFuZ2VFbnVtZXJhdGlvbiAvPgoJCQkJPFVuaXRzIC8+CgkJCQk8RGVzY3JpcHRpb24+PCFbQ0RBVEFbSW5kaWNhdGVzIHRoZSBEYXRhIGluc3RhbmNlIGNyZWF0aW9uIHRpbWVzdGFtcC5dXT48L0Rlc2NyaXB0aW9uPgoJCQk8L0l0ZW0+CgkJCTxJdGVtIElEPSIzIj48TmFtZT5EYXRhIERlc2NyaXB0aW9uPC9OYW1lPgoJCQkJPE9wZXJhdGlvbnM+Ulc8L09wZXJhdGlvbnM+CgkJCQk8TXVsdGlwbGVJbnN0YW5jZXM+U2luZ2xlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQkJCTxNYW5kYXRvcnk+T3B0aW9uYWw8L01hbmRhdG9yeT4KCQkJCTxUeXBlPlN0cmluZzwvVHlwZT4KCQkJCTxSYW5nZUVudW1lcmF0aW9uPjMyIGJ5dGVzPC9SYW5nZUVudW1lcmF0aW9uPgoJCQkJPFVuaXRzIC8+CgkJCQk8RGVzY3JpcHRpb24+PCFbQ0RBVEFbSW5kaWNhdGVzIHRoZSBkYXRhIGRlc2NyaXB0aW9uLgplLmcuICJtZXRlciByZWFkaW5nIi5dXT48L0Rlc2NyaXB0aW9uPgoJCQk8L0l0ZW0+CgkJCTxJdGVtIElEPSI0Ij48TmFtZT5EYXRhIEZvcm1hdDwvTmFtZT4KCQkJCTxPcGVyYXRpb25zPlJXPC9PcGVyYXRpb25zPgoJCQkJPE11bHRpcGxlSW5zdGFuY2VzPlNpbmdsZTwvTXVsdGlwbGVJbnN0YW5jZXM+CgkJCQk8TWFuZGF0b3J5Pk9wdGlvbmFsPC9NYW5kYXRvcnk+CgkJCQk8VHlwZT5TdHJpbmc8L1R5cGU+CgkJCQk8UmFuZ2VFbnVtZXJhdGlvbj4zMiBieXRlczwvUmFuZ2VFbnVtZXJhdGlvbj4KCQkJCTxVbml0cyAvPgoJCQkJPERlc2NyaXB0aW9uPjwhW0NEQVRBW0luZGljYXRlcyB0aGUgZm9ybWF0IG9mIHRoZSBBcHBsaWNhdGlvbiBEYXRhLgplLmcuIFlHLU1ldGVyLVdhdGVyLVJlYWRpbmcKVVRGOC1zdHJpbmcKXV0+PC9EZXNjcmlwdGlvbj4KCQkJPC9JdGVtPgoJCQk8SXRlbSBJRD0iNSI+PE5hbWU+QXBwIElEPC9OYW1lPgoJCQkJPE9wZXJhdGlvbnM+Ulc8L09wZXJhdGlvbnM+CgkJCQk8TXVsdGlwbGVJbnN0YW5jZXM+U2luZ2xlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQkJCTxNYW5kYXRvcnk+T3B0aW9uYWw8L01hbmRhdG9yeT4KCQkJCTxUeXBlPkludGVnZXI8L1R5cGU+CgkJCQk8UmFuZ2VFbnVtZXJhdGlvbj4yIGJ5dGVzPC9SYW5nZUVudW1lcmF0aW9uPgoJCQkJPFVuaXRzIC8+CgkJCQk8RGVzY3JpcHRpb24+PCFbQ0RBVEFbSW5kaWNhdGVzIHRoZSBkZXN0aW5hdGlvbiBBcHBsaWNhdGlvbiBJRC5dXT48L0Rlc2NyaXB0aW9uPgoJCQk8L0l0ZW0+PC9SZXNvdXJjZXM+CgkJPERlc2NyaXB0aW9uMj48IVtDREFUQVtdXT48L0Rlc2NyaXB0aW9uMj4KCTwvT2JqZWN0Pgo8L0xXTTJNPgo="; + private static final String TEST_BASE64_DATA = "77u/PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCEtLQpGSUxFIElORk9STUFUSU9OCgpPTUEgUGVybWFuZW50IERvY3VtZW50CiAgIEZpbGU6IE9NQS1TVVAtTHdNMk1fQmluYXJ5QXBwRGF0YUNvbnRhaW5lci1WMV8wXzEtMjAxOTAyMjEtQQogICBUeXBlOiB4bWwKClB1YmxpYyBSZWFjaGFibGUgSW5mb3JtYXRpb24KICAgUGF0aDogaHR0cDovL3d3dy5vcGVubW9iaWxlYWxsaWFuY2Uub3JnL3RlY2gvcHJvZmlsZXMKICAgTmFtZTogTHdNMk1fQmluYXJ5QXBwRGF0YUNvbnRhaW5lci12MV8wXzEueG1sCgpOT1JNQVRJVkUgSU5GT1JNQVRJT04KCiAgSW5mb3JtYXRpb24gYWJvdXQgdGhpcyBmaWxlIGNhbiBiZSBmb3VuZCBpbiB0aGUgbGF0ZXN0IHJldmlzaW9uIG9mCgogIE9NQS1UUy1MV00yTV9CaW5hcnlBcHBEYXRhQ29udGFpbmVyLVYxXzBfMQoKICBUaGlzIGlzIGF2YWlsYWJsZSBhdCBodHRwOi8vd3d3Lm9wZW5tb2JpbGVhbGxpYW5jZS5vcmcvCgogIFNlbmQgY29tbWVudHMgdG8gaHR0cHM6Ly9naXRodWIuY29tL09wZW5Nb2JpbGVBbGxpYW5jZS9PTUFfTHdNMk1fZm9yX0RldmVsb3BlcnMvaXNzdWVzCgpDSEFOR0UgSElTVE9SWQoKMTUwNjIwMTggU3RhdHVzIGNoYW5nZWQgdG8gQXBwcm92ZWQgYnkgRE0sIERvYyBSZWYgIyBPTUEtRE0mU0UtMjAxOC0wMDYxLUlOUF9MV00yTV9BUFBEQVRBX1YxXzBfRVJQX2Zvcl9maW5hbF9BcHByb3ZhbAoyMTAyMjAxOSBTdGF0dXMgY2hhbmdlZCB0byBBcHByb3ZlZCBieSBJUFNPLCBEb2MgUmVmICMgT01BLUlQU08tMjAxOS0wMDI1LUlOUF9Md00yTV9PYmplY3RfQXBwX0RhdGFfQ29udGFpbmVyXzFfMF8xX2Zvcl9GaW5hbF9BcHByb3ZhbAoKTEVHQUwgRElTQ0xBSU1FUgoKQ29weXJpZ2h0IDIwMTkgT3BlbiBNb2JpbGUgQWxsaWFuY2UuCgpSZWRpc3RyaWJ1dGlvbiBhbmQgdXNlIGluIHNvdXJjZSBhbmQgYmluYXJ5IGZvcm1zLCB3aXRoIG9yIHdpdGhvdXQKbW9kaWZpY2F0aW9uLCBhcmUgcGVybWl0dGVkIHByb3ZpZGVkIHRoYXQgdGhlIGZvbGxvd2luZyBjb25kaXRpb25zCmFyZSBtZXQ6CgoxLiBSZWRpc3RyaWJ1dGlvbnMgb2Ygc291cmNlIGNvZGUgbXVzdCByZXRhaW4gdGhlIGFib3ZlIGNvcHlyaWdodApub3RpY2UsIHRoaXMgbGlzdCBvZiBjb25kaXRpb25zIGFuZCB0aGUgZm9sbG93aW5nIGRpc2NsYWltZXIuCjIuIFJlZGlzdHJpYnV0aW9ucyBpbiBiaW5hcnkgZm9ybSBtdXN0IHJlcHJvZHVjZSB0aGUgYWJvdmUgY29weXJpZ2h0Cm5vdGljZSwgdGhpcyBsaXN0IG9mIGNvbmRpdGlvbnMgYW5kIHRoZSBmb2xsb3dpbmcgZGlzY2xhaW1lciBpbiB0aGUKZG9jdW1lbnRhdGlvbiBhbmQvb3Igb3RoZXIgbWF0ZXJpYWxzIHByb3ZpZGVkIHdpdGggdGhlIGRpc3RyaWJ1dGlvbi4KMy4gTmVpdGhlciB0aGUgbmFtZSBvZiB0aGUgY29weXJpZ2h0IGhvbGRlciBub3IgdGhlIG5hbWVzIG9mIGl0cwpjb250cmlidXRvcnMgbWF5IGJlIHVzZWQgdG8gZW5kb3JzZSBvciBwcm9tb3RlIHByb2R1Y3RzIGRlcml2ZWQKZnJvbSB0aGlzIHNvZnR3YXJlIHdpdGhvdXQgc3BlY2lmaWMgcHJpb3Igd3JpdHRlbiBwZXJtaXNzaW9uLgoKVEhJUyBTT0ZUV0FSRSBJUyBQUk9WSURFRCBCWSBUSEUgQ09QWVJJR0hUIEhPTERFUlMgQU5EIENPTlRSSUJVVE9SUwoiQVMgSVMiIEFORCBBTlkgRVhQUkVTUyBPUiBJTVBMSUVEIFdBUlJBTlRJRVMsIElOQ0xVRElORywgQlVUIE5PVApMSU1JVEVEIFRPLCBUSEUgSU1QTElFRCBXQVJSQU5USUVTIE9GIE1FUkNIQU5UQUJJTElUWSBBTkQgRklUTkVTUwpGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UgQVJFIERJU0NMQUlNRUQuIElOIE5PIEVWRU5UIFNIQUxMIFRIRQpDT1BZUklHSFQgSE9MREVSIE9SIENPTlRSSUJVVE9SUyBCRSBMSUFCTEUgRk9SIEFOWSBESVJFQ1QsIElORElSRUNULApJTkNJREVOVEFMLCBTUEVDSUFMLCBFWEVNUExBUlksIE9SIENPTlNFUVVFTlRJQUwgREFNQUdFUyAoSU5DTFVESU5HLApCVVQgTk9UIExJTUlURUQgVE8sIFBST0NVUkVNRU5UIE9GIFNVQlNUSVRVVEUgR09PRFMgT1IgU0VSVklDRVM7CkxPU1MgT0YgVVNFLCBEQVRBLCBPUiBQUk9GSVRTOyBPUiBCVVNJTkVTUyBJTlRFUlJVUFRJT04pIEhPV0VWRVIKQ0FVU0VEIEFORCBPTiBBTlkgVEhFT1JZIE9GIExJQUJJTElUWSwgV0hFVEhFUiBJTiBDT05UUkFDVCwgU1RSSUNUCkxJQUJJTElUWSwgT1IgVE9SVCAoSU5DTFVESU5HIE5FR0xJR0VOQ0UgT1IgT1RIRVJXSVNFKSBBUklTSU5HIElOCkFOWSBXQVkgT1VUIE9GIFRIRSBVU0UgT0YgVEhJUyBTT0ZUV0FSRSwgRVZFTiBJRiBBRFZJU0VEIE9GIFRIRQpQT1NTSUJJTElUWSBPRiBTVUNIIERBTUFHRS4KClRoZSBhYm92ZSBsaWNlbnNlIGlzIHVzZWQgYXMgYSBsaWNlbnNlIHVuZGVyIGNvcHlyaWdodCBvbmx5LiBQbGVhc2UKcmVmZXJlbmNlIHRoZSBPTUEgSVBSIFBvbGljeSBmb3IgcGF0ZW50IGxpY2Vuc2luZyB0ZXJtczoKaHR0cHM6Ly93d3cub21hc3BlY3dvcmtzLm9yZy9hYm91dC9pbnRlbGxlY3R1YWwtcHJvcGVydHktcmlnaHRzLwoKLS0+CjxMV00yTSB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4c2k6bm9OYW1lc3BhY2VTY2hlbWFMb2NhdGlvbj0iaHR0cDovL29wZW5tb2JpbGVhbGxpYW5jZS5vcmcvdGVjaC9wcm9maWxlcy9MV00yTS54c2QiPgoJPE9iamVjdCBPYmplY3RUeXBlPSJNT0RlZmluaXRpb24iPgoJCTxOYW1lPkJpbmFyeUFwcERhdGFDb250YWluZXI8L05hbWU+CgkJPERlc2NyaXB0aW9uMT48IVtDREFUQVtUaGlzIEx3TTJNIE9iamVjdHMgcHJvdmlkZXMgdGhlIGFwcGxpY2F0aW9uIHNlcnZpY2UgZGF0YSByZWxhdGVkIHRvIGEgTHdNMk0gU2VydmVyLCBlZy4gV2F0ZXIgbWV0ZXIgZGF0YS4gClRoZXJlIGFyZSBzZXZlcmFsIG1ldGhvZHMgdG8gY3JlYXRlIGluc3RhbmNlIHRvIGluZGljYXRlIHRoZSBtZXNzYWdlIGRpcmVjdGlvbiBiYXNlZCBvbiB0aGUgbmVnb3RpYXRpb24gYmV0d2VlbiBBcHBsaWNhdGlvbiBhbmQgTHdNMk0uIFRoZSBDbGllbnQgYW5kIFNlcnZlciBzaG91bGQgbmVnb3RpYXRlIHRoZSBpbnN0YW5jZShzKSB1c2VkIHRvIGV4Y2hhbmdlIHRoZSBkYXRhLiBGb3IgZXhhbXBsZToKIC0gVXNpbmcgYSBzaW5nbGUgaW5zdGFuY2UgZm9yIGJvdGggZGlyZWN0aW9ucyBjb21tdW5pY2F0aW9uLCBmcm9tIENsaWVudCB0byBTZXJ2ZXIgYW5kIGZyb20gU2VydmVyIHRvIENsaWVudC4KIC0gVXNpbmcgYW4gaW5zdGFuY2UgZm9yIGNvbW11bmljYXRpb24gZnJvbSBDbGllbnQgdG8gU2VydmVyIGFuZCBhbm90aGVyIG9uZSBmb3IgY29tbXVuaWNhdGlvbiBmcm9tIFNlcnZlciB0byBDbGllbnQKIC0gVXNpbmcgc2V2ZXJhbCBpbnN0YW5jZXMKXV0+PC9EZXNjcmlwdGlvbjE+CgkJPE9iamVjdElEPjE5PC9PYmplY3RJRD4KCQk8T2JqZWN0VVJOPnVybjpvbWE6bHdtMm06b21hOjE5PC9PYmplY3RVUk4+CgkJPExXTTJNVmVyc2lvbj4xLjA8L0xXTTJNVmVyc2lvbj4KCQk8T2JqZWN0VmVyc2lvbj4xLjA8L09iamVjdFZlcnNpb24+CgkJPE11bHRpcGxlSW5zdGFuY2VzPk11bHRpcGxlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQk8TWFuZGF0b3J5Pk9wdGlvbmFsPC9NYW5kYXRvcnk+CgkJPFJlc291cmNlcz4KCQkJPEl0ZW0gSUQ9IjAiPjxOYW1lPkRhdGE8L05hbWU+CgkJCQk8T3BlcmF0aW9ucz5SVzwvT3BlcmF0aW9ucz4KCQkJCTxNdWx0aXBsZUluc3RhbmNlcz5NdWx0aXBsZTwvTXVsdGlwbGVJbnN0YW5jZXM+CgkJCQk8TWFuZGF0b3J5Pk1hbmRhdG9yeTwvTWFuZGF0b3J5PgoJCQkJPFR5cGU+T3BhcXVlPC9UeXBlPgoJCQkJPFJhbmdlRW51bWVyYXRpb24gLz4KCQkJCTxVbml0cyAvPgoJCQkJPERlc2NyaXB0aW9uPjwhW0NEQVRBW0luZGljYXRlcyB0aGUgYXBwbGljYXRpb24gZGF0YSBjb250ZW50Ll1dPjwvRGVzY3JpcHRpb24+CgkJCTwvSXRlbT4KCQkJPEl0ZW0gSUQ9IjEiPjxOYW1lPkRhdGEgUHJpb3JpdHk8L05hbWU+CgkJCQk8T3BlcmF0aW9ucz5SVzwvT3BlcmF0aW9ucz4KCQkJCTxNdWx0aXBsZUluc3RhbmNlcz5TaW5nbGU8L011bHRpcGxlSW5zdGFuY2VzPgoJCQkJPE1hbmRhdG9yeT5PcHRpb25hbDwvTWFuZGF0b3J5PgoJCQkJPFR5cGU+SW50ZWdlcjwvVHlwZT4KCQkJCTxSYW5nZUVudW1lcmF0aW9uPjEgYnl0ZXM8L1JhbmdlRW51bWVyYXRpb24+CgkJCQk8VW5pdHMgLz4KCQkJCTxEZXNjcmlwdGlvbj48IVtDREFUQVtJbmRpY2F0ZXMgdGhlIEFwcGxpY2F0aW9uIGRhdGEgcHJpb3JpdHk6CjA6SW1tZWRpYXRlCjE6QmVzdEVmZm9ydAoyOkxhdGVzdAozLTEwMDogUmVzZXJ2ZWQgZm9yIGZ1dHVyZSB1c2UuCjEwMS0yNTQ6IFByb3ByaWV0YXJ5IG1vZGUuXV0+PC9EZXNjcmlwdGlvbj4KCQkJPC9JdGVtPgoJCQk8SXRlbSBJRD0iMiI+PE5hbWU+RGF0YSBDcmVhdGlvbiBUaW1lPC9OYW1lPgoJCQkJPE9wZXJhdGlvbnM+Ulc8L09wZXJhdGlvbnM+CgkJCQk8TXVsdGlwbGVJbnN0YW5jZXM+U2luZ2xlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQkJCTxNYW5kYXRvcnk+T3B0aW9uYWw8L01hbmRhdG9yeT4KCQkJCTxUeXBlPlRpbWU8L1R5cGU+CgkJCQk8UmFuZ2VFbnVtZXJhdGlvbiAvPgoJCQkJPFVuaXRzIC8+CgkJCQk8RGVzY3JpcHRpb24+PCFbQ0RBVEFbSW5kaWNhdGVzIHRoZSBEYXRhIGluc3RhbmNlIGNyZWF0aW9uIHRpbWVzdGFtcC5dXT48L0Rlc2NyaXB0aW9uPgoJCQk8L0l0ZW0+CgkJCTxJdGVtIElEPSIzIj48TmFtZT5EYXRhIERlc2NyaXB0aW9uPC9OYW1lPgoJCQkJPE9wZXJhdGlvbnM+Ulc8L09wZXJhdGlvbnM+CgkJCQk8TXVsdGlwbGVJbnN0YW5jZXM+U2luZ2xlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQkJCTxNYW5kYXRvcnk+T3B0aW9uYWw8L01hbmRhdG9yeT4KCQkJCTxUeXBlPlN0cmluZzwvVHlwZT4KCQkJCTxSYW5nZUVudW1lcmF0aW9uPjMyIGJ5dGVzPC9SYW5nZUVudW1lcmF0aW9uPgoJCQkJPFVuaXRzIC8+CgkJCQk8RGVzY3JpcHRpb24+PCFbQ0RBVEFbSW5kaWNhdGVzIHRoZSBkYXRhIGRlc2NyaXB0aW9uLgplLmcuICJtZXRlciByZWFkaW5nIi5dXT48L0Rlc2NyaXB0aW9uPgoJCQk8L0l0ZW0+CgkJCTxJdGVtIElEPSI0Ij48TmFtZT5EYXRhIEZvcm1hdDwvTmFtZT4KCQkJCTxPcGVyYXRpb25zPlJXPC9PcGVyYXRpb25zPgoJCQkJPE11bHRpcGxlSW5zdGFuY2VzPlNpbmdsZTwvTXVsdGlwbGVJbnN0YW5jZXM+CgkJCQk8TWFuZGF0b3J5Pk9wdGlvbmFsPC9NYW5kYXRvcnk+CgkJCQk8VHlwZT5TdHJpbmc8L1R5cGU+CgkJCQk8UmFuZ2VFbnVtZXJhdGlvbj4zMiBieXRlczwvUmFuZ2VFbnVtZXJhdGlvbj4KCQkJCTxVbml0cyAvPgoJCQkJPERlc2NyaXB0aW9uPjwhW0NEQVRBW0luZGljYXRlcyB0aGUgZm9ybWF0IG9mIHRoZSBBcHBsaWNhdGlvbiBEYXRhLgplLmcuIFlHLU1ldGVyLVdhdGVyLVJlYWRpbmcKVVRGOC1zdHJpbmcKXV0+PC9EZXNjcmlwdGlvbj4KCQkJPC9JdGVtPgoJCQk8SXRlbSBJRD0iNSI+PE5hbWU+QXBwIElEPC9OYW1lPgoJCQkJPE9wZXJhdGlvbnM+Ulc8L09wZXJhdGlvbnM+CgkJCQk8TXVsdGlwbGVJbnN0YW5jZXM+U2luZ2xlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQkJCTxNYW5kYXRvcnk+T3B0aW9uYWw8L01hbmRhdG9yeT4KCQkJCTxUeXBlPkludGVnZXI8L1R5cGU+CgkJCQk8UmFuZ2VFbnVtZXJhdGlvbj4yIGJ5dGVzPC9SYW5nZUVudW1lcmF0aW9uPgoJCQkJPFVuaXRzIC8+CgkJCQk8RGVzY3JpcHRpb24+PCFbQ0RBVEFbSW5kaWNhdGVzIHRoZSBkZXN0aW5hdGlvbiBBcHBsaWNhdGlvbiBJRC5dXT48L0Rlc2NyaXB0aW9uPgoJCQk8L0l0ZW0+PC9SZXNvdXJjZXM+CgkJPERlc2NyaXB0aW9uMj48IVtDREFUQVtdXT48L0Rlc2NyaXB0aW9uMj4KCTwvT2JqZWN0Pgo8L0xXTTJNPgo="; + private static final byte[] TEST_DATA = Base64.getDecoder().decode(TEST_BASE64_DATA); private IdComparator idComparator = new IdComparator<>(); @@ -151,7 +154,8 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { long limit = 4; EntityInfo defaultTenantProfileInfo = doGet("/api/tenantProfileInfo/default", EntityInfo.class); TenantProfile defaultTenantProfile = doGet("/api/tenantProfile/" + defaultTenantProfileInfo.getId().getId().toString(), TenantProfile.class); - defaultTenantProfile.getProfileData().setConfiguration(DefaultTenantProfileConfiguration.builder().maxResourcesInBytes(limit).build()); + defaultTenantProfile.getProfileData().setConfiguration(DefaultTenantProfileConfiguration.builder() + .maxResourcesInBytes(limit).build()); doPost("/api/tenantProfile", defaultTenantProfile, TenantProfile.class); loginTenantAdmin(); @@ -165,7 +169,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { try { assertThatThrownBy(() -> createResource("test1", 1 + DEFAULT_FILE_NAME)) .isInstanceOf(DataValidationException.class) - .hasMessageContaining("Failed to create the tb resource, files size limit is exhausted %d bytes!", limit); + .hasMessageContaining("Resources total size exceeds the maximum of %s bytes", limit); } finally { defaultTenantProfile.getProfileData().setConfiguration(DefaultTenantProfileConfiguration.builder().maxResourcesInBytes(0).build()); loginSysAdmin(); @@ -173,6 +177,15 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { } } + @Test + public void testMaxResourceSizeValidation() throws ThingsboardException { + updateDefaultTenantProfileConfig(profileConfig -> { + profileConfig.setMaxResourceSize(2); + }); + assertThatThrownBy(() -> createResource("Test", DEFAULT_FILE_NAME)) + .hasMessageContaining("Resource exceeds the maximum size of 2 bytes"); + } + @Test public void sumDataSizeByTenantId() throws Exception { assertEquals(0, resourceService.sumDataSizeByTenantId(tenantId)); @@ -192,8 +205,8 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setTitle(title); resource.setResourceType(ResourceType.JKS); resource.setFileName(filename); - byte[] b = new byte[1]; - resource.setData(Base64.getEncoder().encodeToString(b)); + byte[] b = new byte[]{1, 2, 3, 4}; + resource.setBase64Data(Base64.getEncoder().encodeToString(b)); return resourceService.save(resource); } @@ -204,7 +217,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JKS); resource.setTitle("My first resource"); resource.setFileName(DEFAULT_FILE_NAME); - resource.setData(TEST_DATA); + resource.setBase64Data(TEST_BASE64_DATA); TbResource savedResource = resourceService.save(resource); @@ -214,13 +227,16 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { assertEquals(resource.getTenantId(), savedResource.getTenantId()); assertEquals(resource.getTitle(), savedResource.getTitle()); assertEquals(resource.getResourceKey(), savedResource.getResourceKey()); - assertEquals(resource.getData(), savedResource.getData()); + assertArrayEquals(TEST_DATA, savedResource.getData()); savedResource.setTitle("My new resource"); + savedResource.setBase64Data(null); + savedResource.setData(null); resourceService.save(savedResource); TbResource foundResource = resourceService.findResourceById(tenantId, savedResource.getId()); assertEquals(foundResource.getTitle(), savedResource.getTitle()); + assertArrayEquals(foundResource.getData(), TEST_DATA); resourceService.delete(savedResource, null); } @@ -231,7 +247,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setTenantId(tenantId); resource.setResourceType(ResourceType.LWM2M_MODEL); resource.setFileName("test_model.xml"); - resource.setData(Base64.getEncoder().encodeToString(LWM2M_TEST_MODEL.getBytes())); + resource.setBase64Data(Base64.getEncoder().encodeToString(LWM2M_TEST_MODEL.getBytes())); TbResource savedResource = resourceService.save(resource); @@ -241,7 +257,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { assertEquals(resource.getTenantId(), savedResource.getTenantId()); assertEquals("My first resource id=0 v1.0", savedResource.getTitle()); assertEquals("0_1.0", savedResource.getResourceKey()); - assertEquals(resource.getData(), savedResource.getData()); + assertArrayEquals(savedResource.getData(), LWM2M_TEST_MODEL.getBytes()); resourceService.delete(savedResource, null); } @@ -252,7 +268,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JKS); resource.setTitle("My resource"); resource.setFileName(DEFAULT_FILE_NAME); - resource.setData(TEST_DATA); + resource.setBase64Data(TEST_BASE64_DATA); TbResource savedResource = resourceService.save(resource); assertEquals(TenantId.SYS_TENANT_ID, savedResource.getTenantId()); @@ -267,7 +283,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JKS); resource.setTitle("My resource"); resource.setFileName(DEFAULT_FILE_NAME); - resource.setData(TEST_DATA); + resource.setBase64Data(TEST_BASE64_DATA); TbResource savedResource = resourceService.save(resource); @@ -276,7 +292,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JKS); resource.setTitle("My resource"); resource.setFileName(DEFAULT_FILE_NAME); - resource.setData(TEST_DATA); + resource.setBase64Data(TEST_BASE64_DATA); try { Assertions.assertThrows(DataValidationException.class, () -> { @@ -293,7 +309,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setTenantId(tenantId); resource.setResourceType(ResourceType.JKS); resource.setFileName(DEFAULT_FILE_NAME); - resource.setData(TEST_DATA); + resource.setBase64Data(TEST_BASE64_DATA); Assertions.assertThrows(DataValidationException.class, () -> { resourceService.save(resource); }); @@ -306,7 +322,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JKS); resource.setTitle("My resource"); resource.setFileName(DEFAULT_FILE_NAME); - resource.setData(TEST_DATA); + resource.setBase64Data(TEST_BASE64_DATA); Assertions.assertThrows(DataValidationException.class, () -> { resourceService.save(resource); }); @@ -318,7 +334,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setTenantId(tenantId); resource.setResourceType(ResourceType.LWM2M_MODEL); resource.setFileName("xxe_test_model.xml"); - resource.setData(Base64.getEncoder().encodeToString(LWM2M_TEST_MODEL_WITH_XXE.getBytes())); + resource.setData(LWM2M_TEST_MODEL_WITH_XXE.getBytes()); DataValidationException thrown = assertThrows(DataValidationException.class, () -> { resourceService.save(resource); @@ -333,7 +349,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JKS); resource.setTitle("My resource"); resource.setFileName(DEFAULT_FILE_NAME); - resource.setData(TEST_DATA); + resource.setBase64Data(TEST_BASE64_DATA); TbResource savedResource = resourceService.save(resource); TbResource foundResource = resourceService.findResourceById(tenantId, savedResource.getId()); @@ -349,7 +365,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setTenantId(tenantId); resource.setTitle("My resource"); resource.setFileName(DEFAULT_FILE_NAME); - resource.setData(TEST_DATA); + resource.setBase64Data(TEST_BASE64_DATA); TbResource savedResource = resourceService.save(resource); TbResource foundResource = resourceService.getResource(tenantId, savedResource.getResourceType(), savedResource.getResourceKey()); @@ -364,7 +380,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JKS); resource.setTitle("My resource"); resource.setFileName(DEFAULT_FILE_NAME); - resource.setData(TEST_DATA); + resource.setBase64Data(TEST_BASE64_DATA); TbResource savedResource = resourceService.save(resource); TbResource foundResource = resourceService.findResourceById(tenantId, savedResource.getId()); @@ -390,7 +406,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setTitle("Resource" + i); resource.setResourceType(ResourceType.JKS); resource.setFileName(i + DEFAULT_FILE_NAME); - resource.setData(TEST_DATA); + resource.setBase64Data(TEST_BASE64_DATA); resources.add(new TbResourceInfo(resourceService.save(resource))); } @@ -444,7 +460,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setTitle("System Resource" + i); resource.setResourceType(ResourceType.JKS); resource.setFileName(i + DEFAULT_FILE_NAME); - resource.setData(TEST_DATA); + resource.setBase64Data(TEST_BASE64_DATA); TbResourceInfo tbResourceInfo = new TbResourceInfo(resourceService.save(resource)); if (i >= 50) { resources.add(tbResourceInfo); @@ -457,7 +473,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setTitle("Tenant Resource" + i); resource.setResourceType(ResourceType.JKS); resource.setFileName(i + DEFAULT_FILE_NAME); - resource.setData(TEST_DATA); + resource.setBase64Data(TEST_BASE64_DATA); resources.add(new TbResourceInfo(resourceService.save(resource))); } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java index 64382d5d51..e3b0d0d9a2 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java @@ -206,7 +206,7 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte lwModel.setFileName(resourceName); lwModel.setTenantId(tenantId); byte[] bytes = IOUtils.toByteArray(AbstractLwM2MIntegrationTest.class.getClassLoader().getResourceAsStream("lwm2m/" + resourceName)); - lwModel.setData(Base64.getEncoder().encodeToString(bytes)); + lwModel.setBase64Data(Base64.getEncoder().encodeToString(bytes)); lwModel = doPostWithTypedResponse("/api/resource", lwModel, new TypeReference<>() { }); Assert.assertNotNull(lwModel); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/TbResource.java b/common/data/src/main/java/org/thingsboard/server/common/data/TbResource.java index 8b82db196a..d003328418 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/TbResource.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/TbResource.java @@ -15,13 +15,14 @@ */ package org.thingsboard.server.common.data; +import com.fasterxml.jackson.annotation.JsonIgnore; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.id.TbResourceId; -import org.thingsboard.server.common.data.validation.Length; -import org.thingsboard.server.common.data.validation.NoXss; + +import java.util.Base64; @Slf4j @Data @@ -30,13 +31,11 @@ public class TbResource extends TbResourceInfo { private static final long serialVersionUID = 7379609705527272306L; - @NoXss - @Length(fieldName = "file name") - @ApiModelProperty(position = 8, value = "Resource file name.", example = "19.xml", accessMode = ApiModelProperty.AccessMode.READ_ONLY) - private String fileName; + private String base64Data; @ApiModelProperty(position = 9, value = "Resource data.", example = "77u/PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCEtLQpGSUxFIElORk9STUFUSU9OCgpPTUEgUGVybWFuZW50IERvY3VtZW50CiAgIEZpbGU6IE9NQS1TVVAtTHdNMk1fQmluYXJ5QXBwRGF0YUNvbnRhaW5lci1WMV8wXzEtMjAxOTAyMjEtQQogICBUeXBlOiB4bWwKClB1YmxpYyBSZWFjaGFibGUgSW5mb3JtYXRpb24KICAgUGF0aDogaHR0cDovL3d3dy5vcGVubW9iaWxlYWxsaWFuY2Uub3JnL3RlY2gvcHJvZmlsZXMKICAgTmFtZTogTHdNMk1fQmluYXJ5QXBwRGF0YUNvbnRhaW5lci12MV8wXzEueG1sCgpOT1JNQVRJVkUgSU5GT1JNQVRJT04KCiAgSW5mb3JtYXRpb24gYWJvdXQgdGhpcyBmaWxlIGNhbiBiZSBmb3VuZCBpbiB0aGUgbGF0ZXN0IHJldmlzaW9uIG9mCgogIE9NQS1UUy1MV00yTV9CaW5hcnlBcHBEYXRhQ29udGFpbmVyLVYxXzBfMQoKICBUaGlzIGlzIGF2YWlsYWJsZSBhdCBodHRwOi8vd3d3Lm9wZW5tb2JpbGVhbGxpYW5jZS5vcmcvCgogIFNlbmQgY29tbWVudHMgdG8gaHR0cHM6Ly9naXRodWIuY29tL09wZW5Nb2JpbGVBbGxpYW5jZS9PTUFfTHdNMk1fZm9yX0RldmVsb3BlcnMvaXNzdWVzCgpDSEFOR0UgSElTVE9SWQoKMTUwNjIwMTggU3RhdHVzIGNoYW5nZWQgdG8gQXBwcm92ZWQgYnkgRE0sIERvYyBSZWYgIyBPTUEtRE0mU0UtMjAxOC0wMDYxLUlOUF9MV00yTV9BUFBEQVRBX1YxXzBfRVJQX2Zvcl9maW5hbF9BcHByb3ZhbAoyMTAyMjAxOSBTdGF0dXMgY2hhbmdlZCB0byBBcHByb3ZlZCBieSBJUFNPLCBEb2MgUmVmICMgT01BLUlQU08tMjAxOS0wMDI1LUlOUF9Md00yTV9PYmplY3RfQXBwX0RhdGFfQ29udGFpbmVyXzFfMF8xX2Zvcl9GaW5hbF9BcHByb3ZhbAoKTEVHQUwgRElTQ0xBSU1FUgoKQ29weXJpZ2h0IDIwMTkgT3BlbiBNb2JpbGUgQWxsaWFuY2UuCgpSZWRpc3RyaWJ1dGlvbiBhbmQgdXNlIGluIHNvdXJjZSBhbmQgYmluYXJ5IGZvcm1zLCB3aXRoIG9yIHdpdGhvdXQKbW9kaWZpY2F0aW9uLCBhcmUgcGVybWl0dGVkIHByb3ZpZGVkIHRoYXQgdGhlIGZvbGxvd2luZyBjb25kaXRpb25zCmFyZSBtZXQ6CgoxLiBSZWRpc3RyaWJ1dGlvbnMgb2Ygc291cmNlIGNvZGUgbXVzdCByZXRhaW4gdGhlIGFib3ZlIGNvcHlyaWdodApub3RpY2UsIHRoaXMgbGlzdCBvZiBjb25kaXRpb25zIGFuZCB0aGUgZm9sbG93aW5nIGRpc2NsYWltZXIuCjIuIFJlZGlzdHJpYnV0aW9ucyBpbiBiaW5hcnkgZm9ybSBtdXN0IHJlcHJvZHVjZSB0aGUgYWJvdmUgY29weXJpZ2h0Cm5vdGljZSwgdGhpcyBsaXN0IG9mIGNvbmRpdGlvbnMgYW5kIHRoZSBmb2xsb3dpbmcgZGlzY2xhaW1lciBpbiB0aGUKZG9jdW1lbnRhdGlvbiBhbmQvb3Igb3RoZXIgbWF0ZXJpYWxzIHByb3ZpZGVkIHdpdGggdGhlIGRpc3RyaWJ1dGlvbi4KMy4gTmVpdGhlciB0aGUgbmFtZSBvZiB0aGUgY29weXJpZ2h0IGhvbGRlciBub3IgdGhlIG5hbWVzIG9mIGl0cwpjb250cmlidXRvcnMgbWF5IGJlIHVzZWQgdG8gZW5kb3JzZSBvciBwcm9tb3RlIHByb2R1Y3RzIGRlcml2ZWQKZnJvbSB0aGlzIHNvZnR3YXJlIHdpdGhvdXQgc3BlY2lmaWMgcHJpb3Igd3JpdHRlbiBwZXJtaXNzaW9uLgoKVEhJUyBTT0ZUV0FSRSBJUyBQUk9WSURFRCBCWSBUSEUgQ09QWVJJR0hUIEhPTERFUlMgQU5EIENPTlRSSUJVVE9SUwoiQVMgSVMiIEFORCBBTlkgRVhQUkVTUyBPUiBJTVBMSUVEIFdBUlJBTlRJRVMsIElOQ0xVRElORywgQlVUIE5PVApMSU1JVEVEIFRPLCBUSEUgSU1QTElFRCBXQVJSQU5USUVTIE9GIE1FUkNIQU5UQUJJTElUWSBBTkQgRklUTkVTUwpGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UgQVJFIERJU0NMQUlNRUQuIElOIE5PIEVWRU5UIFNIQUxMIFRIRQpDT1BZUklHSFQgSE9MREVSIE9SIENPTlRSSUJVVE9SUyBCRSBMSUFCTEUgRk9SIEFOWSBESVJFQ1QsIElORElSRUNULApJTkNJREVOVEFMLCBTUEVDSUFMLCBFWEVNUExBUlksIE9SIENPTlNFUVVFTlRJQUwgREFNQUdFUyAoSU5DTFVESU5HLApCVVQgTk9UIExJTUlURUQgVE8sIFBST0NVUkVNRU5UIE9GIFNVQlNUSVRVVEUgR09PRFMgT1IgU0VSVklDRVM7CkxPU1MgT0YgVVNFLCBEQVRBLCBPUiBQUk9GSVRTOyBPUiBCVVNJTkVTUyBJTlRFUlJVUFRJT04pIEhPV0VWRVIKQ0FVU0VEIEFORCBPTiBBTlkgVEhFT1JZIE9GIExJQUJJTElUWSwgV0hFVEhFUiBJTiBDT05UUkFDVCwgU1RSSUNUCkxJQUJJTElUWSwgT1IgVE9SVCAoSU5DTFVESU5HIE5FR0xJR0VOQ0UgT1IgT1RIRVJXSVNFKSBBUklTSU5HIElOCkFOWSBXQVkgT1VUIE9GIFRIRSBVU0UgT0YgVEhJUyBTT0ZUV0FSRSwgRVZFTiBJRiBBRFZJU0VEIE9GIFRIRQpQT1NTSUJJTElUWSBPRiBTVUNIIERBTUFHRS4KClRoZSBhYm92ZSBsaWNlbnNlIGlzIHVzZWQgYXMgYSBsaWNlbnNlIHVuZGVyIGNvcHlyaWdodCBvbmx5LiBQbGVhc2UKcmVmZXJlbmNlIHRoZSBPTUEgSVBSIFBvbGljeSBmb3IgcGF0ZW50IGxpY2Vuc2luZyB0ZXJtczoKaHR0cHM6Ly93d3cub21hc3BlY3dvcmtzLm9yZy9hYm91dC9pbnRlbGxlY3R1YWwtcHJvcGVydHktcmlnaHRzLwoKLS0+CjxMV00yTSB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4c2k6bm9OYW1lc3BhY2VTY2hlbWFMb2NhdGlvbj0iaHR0cDovL29wZW5tb2JpbGVhbGxpYW5jZS5vcmcvdGVjaC9wcm9maWxlcy9MV00yTS54c2QiPgoJPE9iamVjdCBPYmplY3RUeXBlPSJNT0RlZmluaXRpb24iPgoJCTxOYW1lPkJpbmFyeUFwcERhdGFDb250YWluZXI8L05hbWU+CgkJPERlc2NyaXB0aW9uMT48IVtDREFUQVtUaGlzIEx3TTJNIE9iamVjdHMgcHJvdmlkZXMgdGhlIGFwcGxpY2F0aW9uIHNlcnZpY2UgZGF0YSByZWxhdGVkIHRvIGEgTHdNMk0gU2VydmVyLCBlZy4gV2F0ZXIgbWV0ZXIgZGF0YS4gClRoZXJlIGFyZSBzZXZlcmFsIG1ldGhvZHMgdG8gY3JlYXRlIGluc3RhbmNlIHRvIGluZGljYXRlIHRoZSBtZXNzYWdlIGRpcmVjdGlvbiBiYXNlZCBvbiB0aGUgbmVnb3RpYXRpb24gYmV0d2VlbiBBcHBsaWNhdGlvbiBhbmQgTHdNMk0uIFRoZSBDbGllbnQgYW5kIFNlcnZlciBzaG91bGQgbmVnb3RpYXRlIHRoZSBpbnN0YW5jZShzKSB1c2VkIHRvIGV4Y2hhbmdlIHRoZSBkYXRhLiBGb3IgZXhhbXBsZToKIC0gVXNpbmcgYSBzaW5nbGUgaW5zdGFuY2UgZm9yIGJvdGggZGlyZWN0aW9ucyBjb21tdW5pY2F0aW9uLCBmcm9tIENsaWVudCB0byBTZXJ2ZXIgYW5kIGZyb20gU2VydmVyIHRvIENsaWVudC4KIC0gVXNpbmcgYW4gaW5zdGFuY2UgZm9yIGNvbW11bmljYXRpb24gZnJvbSBDbGllbnQgdG8gU2VydmVyIGFuZCBhbm90aGVyIG9uZSBmb3IgY29tbXVuaWNhdGlvbiBmcm9tIFNlcnZlciB0byBDbGllbnQKIC0gVXNpbmcgc2V2ZXJhbCBpbnN0YW5jZXMKXV0+PC9EZXNjcmlwdGlvbjE+CgkJPE9iamVjdElEPjE5PC9PYmplY3RJRD4KCQk8T2JqZWN0VVJOPnVybjpvbWE6bHdtMm06b21hOjE5PC9PYmplY3RVUk4+CgkJPExXTTJNVmVyc2lvbj4xLjA8L0xXTTJNVmVyc2lvbj4KCQk8T2JqZWN0VmVyc2lvbj4xLjA8L09iamVjdFZlcnNpb24+CgkJPE11bHRpcGxlSW5zdGFuY2VzPk11bHRpcGxlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQk8TWFuZGF0b3J5Pk9wdGlvbmFsPC9NYW5kYXRvcnk+CgkJPFJlc291cmNlcz4KCQkJPEl0ZW0gSUQ9IjAiPjxOYW1lPkRhdGE8L05hbWU+CgkJCQk8T3BlcmF0aW9ucz5SVzwvT3BlcmF0aW9ucz4KCQkJCTxNdWx0aXBsZUluc3RhbmNlcz5NdWx0aXBsZTwvTXVsdGlwbGVJbnN0YW5jZXM+CgkJCQk8TWFuZGF0b3J5Pk1hbmRhdG9yeTwvTWFuZGF0b3J5PgoJCQkJPFR5cGU+T3BhcXVlPC9UeXBlPgoJCQkJPFJhbmdlRW51bWVyYXRpb24gLz4KCQkJCTxVbml0cyAvPgoJCQkJPERlc2NyaXB0aW9uPjwhW0NEQVRBW0luZGljYXRlcyB0aGUgYXBwbGljYXRpb24gZGF0YSBjb250ZW50Ll1dPjwvRGVzY3JpcHRpb24+CgkJCTwvSXRlbT4KCQkJPEl0ZW0gSUQ9IjEiPjxOYW1lPkRhdGEgUHJpb3JpdHk8L05hbWU+CgkJCQk8T3BlcmF0aW9ucz5SVzwvT3BlcmF0aW9ucz4KCQkJCTxNdWx0aXBsZUluc3RhbmNlcz5TaW5nbGU8L011bHRpcGxlSW5zdGFuY2VzPgoJCQkJPE1hbmRhdG9yeT5PcHRpb25hbDwvTWFuZGF0b3J5PgoJCQkJPFR5cGU+SW50ZWdlcjwvVHlwZT4KCQkJCTxSYW5nZUVudW1lcmF0aW9uPjEgYnl0ZXM8L1JhbmdlRW51bWVyYXRpb24+CgkJCQk8VW5pdHMgLz4KCQkJCTxEZXNjcmlwdGlvbj48IVtDREFUQVtJbmRpY2F0ZXMgdGhlIEFwcGxpY2F0aW9uIGRhdGEgcHJpb3JpdHk6CjA6SW1tZWRpYXRlCjE6QmVzdEVmZm9ydAoyOkxhdGVzdAozLTEwMDogUmVzZXJ2ZWQgZm9yIGZ1dHVyZSB1c2UuCjEwMS0yNTQ6IFByb3ByaWV0YXJ5IG1vZGUuXV0+PC9EZXNjcmlwdGlvbj4KCQkJPC9JdGVtPgoJCQk8SXRlbSBJRD0iMiI+PE5hbWU+RGF0YSBDcmVhdGlvbiBUaW1lPC9OYW1lPgoJCQkJPE9wZXJhdGlvbnM+Ulc8L09wZXJhdGlvbnM+CgkJCQk8TXVsdGlwbGVJbnN0YW5jZXM+U2luZ2xlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQkJCTxNYW5kYXRvcnk+T3B0aW9uYWw8L01hbmRhdG9yeT4KCQkJCTxUeXBlPlRpbWU8L1R5cGU+CgkJCQk8UmFuZ2VFbnVtZXJhdGlvbiAvPgoJCQkJPFVuaXRzIC8+CgkJCQk8RGVzY3JpcHRpb24+PCFbQ0RBVEFbSW5kaWNhdGVzIHRoZSBEYXRhIGluc3RhbmNlIGNyZWF0aW9uIHRpbWVzdGFtcC5dXT48L0Rlc2NyaXB0aW9uPgoJCQk8L0l0ZW0+CgkJCTxJdGVtIElEPSIzIj48TmFtZT5EYXRhIERlc2NyaXB0aW9uPC9OYW1lPgoJCQkJPE9wZXJhdGlvbnM+Ulc8L09wZXJhdGlvbnM+CgkJCQk8TXVsdGlwbGVJbnN0YW5jZXM+U2luZ2xlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQkJCTxNYW5kYXRvcnk+T3B0aW9uYWw8L01hbmRhdG9yeT4KCQkJCTxUeXBlPlN0cmluZzwvVHlwZT4KCQkJCTxSYW5nZUVudW1lcmF0aW9uPjMyIGJ5dGVzPC9SYW5nZUVudW1lcmF0aW9uPgoJCQkJPFVuaXRzIC8+CgkJCQk8RGVzY3JpcHRpb24+PCFbQ0RBVEFbSW5kaWNhdGVzIHRoZSBkYXRhIGRlc2NyaXB0aW9uLgplLmcuICJtZXRlciByZWFkaW5nIi5dXT48L0Rlc2NyaXB0aW9uPgoJCQk8L0l0ZW0+CgkJCTxJdGVtIElEPSI0Ij48TmFtZT5EYXRhIEZvcm1hdDwvTmFtZT4KCQkJCTxPcGVyYXRpb25zPlJXPC9PcGVyYXRpb25zPgoJCQkJPE11bHRpcGxlSW5zdGFuY2VzPlNpbmdsZTwvTXVsdGlwbGVJbnN0YW5jZXM+CgkJCQk8TWFuZGF0b3J5Pk9wdGlvbmFsPC9NYW5kYXRvcnk+CgkJCQk8VHlwZT5TdHJpbmc8L1R5cGU+CgkJCQk8UmFuZ2VFbnVtZXJhdGlvbj4zMiBieXRlczwvUmFuZ2VFbnVtZXJhdGlvbj4KCQkJCTxVbml0cyAvPgoJCQkJPERlc2NyaXB0aW9uPjwhW0NEQVRBW0luZGljYXRlcyB0aGUgZm9ybWF0IG9mIHRoZSBBcHBsaWNhdGlvbiBEYXRhLgplLmcuIFlHLU1ldGVyLVdhdGVyLVJlYWRpbmcKVVRGOC1zdHJpbmcKXV0+PC9EZXNjcmlwdGlvbj4KCQkJPC9JdGVtPgoJCQk8SXRlbSBJRD0iNSI+PE5hbWU+QXBwIElEPC9OYW1lPgoJCQkJPE9wZXJhdGlvbnM+Ulc8L09wZXJhdGlvbnM+CgkJCQk8TXVsdGlwbGVJbnN0YW5jZXM+U2luZ2xlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQkJCTxNYW5kYXRvcnk+T3B0aW9uYWw8L01hbmRhdG9yeT4KCQkJCTxUeXBlPkludGVnZXI8L1R5cGU+CgkJCQk8UmFuZ2VFbnVtZXJhdGlvbj4yIGJ5dGVzPC9SYW5nZUVudW1lcmF0aW9uPgoJCQkJPFVuaXRzIC8+CgkJCQk8RGVzY3JpcHRpb24+PCFbQ0RBVEFbSW5kaWNhdGVzIHRoZSBkZXN0aW5hdGlvbiBBcHBsaWNhdGlvbiBJRC5dXT48L0Rlc2NyaXB0aW9uPgoJCQk8L0l0ZW0+PC9SZXNvdXJjZXM+CgkJPERlc2NyaXB0aW9uMj48IVtDREFUQVtdXT48L0Rlc2NyaXB0aW9uMj4KCTwvT2JqZWN0Pgo8L0xXTTJNPgo=", accessMode = ApiModelProperty.AccessMode.READ_ONLY) - private String data; + @JsonIgnore + private byte[] data; public TbResource() { super(); @@ -52,31 +51,25 @@ public class TbResource extends TbResourceInfo { public TbResource(TbResource resource) { super(resource); - this.data = resource.getData(); + this.base64Data = resource.base64Data; + this.data = resource.data; + } + + @JsonIgnore + public byte[] getData() { + if (data == null && base64Data != null) { + try { + data = Base64.getDecoder().decode(base64Data); + } catch (Exception e) { + throw new IllegalArgumentException("Failed to decode resource data from Base64", e); + } + base64Data = null; + } + return data; } @Override public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("Resource [tenantId="); - builder.append(getTenantId()); - builder.append(", id="); - builder.append(getUuidId()); - builder.append(", createdTime="); - builder.append(createdTime); - builder.append(", title="); - builder.append(getTitle()); - builder.append(", resourceType="); - builder.append(getResourceType()); - builder.append(", resourceKey="); - builder.append(getResourceKey()); - builder.append(", fileName="); - builder.append(fileName); - builder.append(", data="); - builder.append(data); - builder.append(", hashCode="); - builder.append(getEtag()); - builder.append("]"); - return builder.toString(); + return super.toString(); } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java index af3c71f226..9d83d5922e 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java @@ -50,6 +50,10 @@ public class TbResourceInfo extends BaseData implements HasName, H private String searchText; @ApiModelProperty(position = 8, value = "Resource etag.", example = "33a64df551425fcc55e4d42a148795d9f25f89d4", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private String etag; + @NoXss + @Length(fieldName = "file name") + @ApiModelProperty(position = 9, value = "Resource file name.", example = "19.xml", accessMode = ApiModelProperty.AccessMode.READ_ONLY) + private String fileName; public TbResourceInfo() { super(); @@ -61,12 +65,13 @@ public class TbResourceInfo extends BaseData implements HasName, H public TbResourceInfo(TbResourceInfo resourceInfo) { super(resourceInfo); - this.tenantId = resourceInfo.getTenantId(); - this.title = resourceInfo.getTitle(); - this.resourceType = resourceInfo.getResourceType(); - this.resourceKey = resourceInfo.getResourceKey(); - this.searchText = resourceInfo.getSearchText(); - this.etag = resourceInfo.getEtag(); + this.tenantId = resourceInfo.tenantId; + this.title = resourceInfo.title; + this.resourceType = resourceInfo.resourceType; + this.resourceKey = resourceInfo.resourceKey; + this.searchText = resourceInfo.searchText; + this.etag = resourceInfo.etag; + this.fileName = resourceInfo.fileName; } @ApiModelProperty(position = 1, value = "JSON object with the Resource Id. " + @@ -95,24 +100,4 @@ public class TbResourceInfo extends BaseData implements HasName, H return searchText != null ? searchText : title; } - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("ResourceInfo [tenantId="); - builder.append(tenantId); - builder.append(", id="); - builder.append(getUuidId()); - builder.append(", createdTime="); - builder.append(createdTime); - builder.append(", title="); - builder.append(title); - builder.append(", resourceType="); - builder.append(resourceType); - builder.append(", resourceKey="); - builder.append(resourceKey); - builder.append(", hashCode="); - builder.append(etag); - builder.append("]"); - return builder.toString(); - } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java index a1d6da34d8..dfc6f5206b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java @@ -39,6 +39,7 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura private long maxRuleChains; private long maxResourcesInBytes; private long maxOtaPackagesInBytes; + private long maxResourceSize; private String transportTenantMsgRateLimit; private String transportTenantTelemetryMsgRateLimit; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mVersionedModelProvider.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mVersionedModelProvider.java index 3dd13cd171..7ba78193b7 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mVersionedModelProvider.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mVersionedModelProvider.java @@ -152,8 +152,7 @@ public class LwM2mVersionedModelProvider implements LwM2mModelProvider { private ObjectModel getObjectModel(String key) { Optional tbResource = context.getTransportResourceCache().get(this.tenantId, LWM2M_MODEL, key); - return tbResource.map(resource -> helper.parseFromXmlToObjectModel( - Base64.getDecoder().decode(resource.getData()), + return tbResource.map(resource -> helper.parseFromXmlToObjectModel(resource.getData(), key + ".xml")).orElse(null); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceEntity.java index 4fbc50fd69..502477699b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceEntity.java @@ -30,8 +30,8 @@ import javax.persistence.Table; import java.util.UUID; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_DATA_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_FILE_NAME_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_ETAG_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_FILE_NAME_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_KEY_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TABLE_NAME; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TENANT_ID_COLUMN; @@ -64,7 +64,7 @@ public class TbResourceEntity extends BaseSqlEntity implements BaseE private String fileName; @Column(name = RESOURCE_DATA_COLUMN) - private String data; + private byte[] data; @Column(name = RESOURCE_ETAG_COLUMN) private String etag; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceInfoEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceInfoEntity.java index e8504bc388..ced72920dc 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceInfoEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceInfoEntity.java @@ -30,6 +30,7 @@ import javax.persistence.Table; import java.util.UUID; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_ETAG_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_FILE_NAME_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_KEY_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TABLE_NAME; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TENANT_ID_COLUMN; @@ -61,6 +62,9 @@ public class TbResourceInfoEntity extends BaseSqlEntity implemen @Column(name = RESOURCE_ETAG_COLUMN) private String hashCode; + @Column(name = RESOURCE_FILE_NAME_COLUMN) + private String fileName; + public TbResourceInfoEntity() { } @@ -75,6 +79,7 @@ public class TbResourceInfoEntity extends BaseSqlEntity implemen this.resourceKey = resource.getResourceKey(); this.searchText = resource.getSearchText(); this.hashCode = resource.getEtag(); + this.fileName = resource.getFileName(); } @Override @@ -87,6 +92,7 @@ public class TbResourceInfoEntity extends BaseSqlEntity implemen resource.setResourceKey(resourceKey); resource.setSearchText(searchText); resource.setEtag(hashCode); + resource.setFileName(fileName); return resource; } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java index 7697217b6b..8bfb0ccfc5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java @@ -15,16 +15,17 @@ */ package org.thingsboard.server.dao.resource; +import com.datastax.oss.driver.api.core.uuid.Uuids; +import com.google.common.hash.Hashing; import com.google.common.util.concurrent.ListenableFuture; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.hibernate.exception.ConstraintViolationException; import org.springframework.stereotype.Service; import org.springframework.transaction.event.TransactionalEventListener; -import org.thingsboard.server.cache.device.DeviceCacheKey; +import org.thingsboard.server.cache.resourceInfo.ResourceInfoCacheKey; import org.thingsboard.server.cache.resourceInfo.ResourceInfoEvictEvent; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.cache.resourceInfo.ResourceInfoCacheKey; import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.TbResourceInfo; @@ -43,6 +44,7 @@ import org.thingsboard.server.dao.service.Validator; import java.util.List; import java.util.Optional; +import java.util.UUID; import static org.thingsboard.server.dao.device.DeviceServiceImpl.INCORRECT_TENANT_ID; import static org.thingsboard.server.dao.service.Validator.validateId; @@ -59,9 +61,26 @@ public class BaseResourceService extends AbstractCachedEntityService tenantResourcesRemover = new PaginatedRemover<>() { diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java index d81c05044f..5773e49fc3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java @@ -128,8 +128,7 @@ public abstract class DataValidator> { EntityType entityType) { if (maxSumDataSize > 0) { if (dataDao.sumDataSizeByTenantId(tenantId) + currentDataSize > maxSumDataSize) { - throw new DataValidationException(String.format("Failed to create the %s, files size limit is exhausted %d bytes!", - entityType.name().toLowerCase().replaceAll("_", " "), maxSumDataSize)); + throw new DataValidationException(String.format("%ss total size exceeds the maximum of " + maxSumDataSize + " bytes", entityType.getNormalName())); } } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/ResourceDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/ResourceDataValidator.java index d620950628..3c6fc53e19 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/ResourceDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/ResourceDataValidator.java @@ -56,12 +56,28 @@ public class ResourceDataValidator extends DataValidator { @Override protected void validateCreate(TenantId tenantId, TbResource resource) { + if (resource.getData() == null || resource.getData().length == 0) { + throw new DataValidationException("Resource data should be specified"); + } if (tenantId != null && !TenantId.SYS_TENANT_ID.equals(tenantId)) { - DefaultTenantProfileConfiguration profileConfiguration = - (DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); + DefaultTenantProfileConfiguration profileConfiguration = tenantProfileCache.get(tenantId).getDefaultProfileConfiguration(); + long maxResourceSize = profileConfiguration.getMaxResourceSize(); + if (maxResourceSize > 0 && resource.getData().length > maxResourceSize) { + throw new IllegalArgumentException("Resource exceeds the maximum size of " + maxResourceSize + " bytes"); + } long maxSumResourcesDataInBytes = profileConfiguration.getMaxResourcesInBytes(); - validateMaxSumDataSizePerTenant(tenantId, resourceDao, maxSumResourcesDataInBytes, resource.getData().length(), TB_RESOURCE); + validateMaxSumDataSizePerTenant(tenantId, resourceDao, maxSumResourcesDataInBytes, resource.getData().length, TB_RESOURCE); + } + } + + @Override + protected TbResource validateUpdate(TenantId tenantId, TbResource resource) { + if (!tenantId.isSysTenantId()) { + if (resource.getData() != null) { + throw new DataValidationException("Resource data can't be updated"); + } } + return resource; } @Override diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 8a79c0cfde..2ec9c868e1 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -713,7 +713,7 @@ CREATE TABLE IF NOT EXISTS resource ( resource_key varchar(255) NOT NULL, search_text varchar(255), file_name varchar(255) NOT NULL, - data varchar, + data bytea, etag varchar, CONSTRAINT resource_unq_key UNIQUE (tenant_id, resource_type, resource_key) ); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/TenantServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/TenantServiceTest.java index ce1a83514a..c7ae0aabb6 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/TenantServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/TenantServiceTest.java @@ -73,6 +73,7 @@ import org.thingsboard.server.dao.usagerecord.ApiUsageStateService; import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.dao.widget.WidgetsBundleService; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -615,7 +616,7 @@ public class TenantServiceTest extends AbstractServiceTest { resource.setResourceType(ResourceType.LWM2M_MODEL); resource.setFileName("filename.txt"); resource.setResourceKey("Test resource key"); - resource.setData("Some super test data"); + resource.setData("Some super test data".getBytes(StandardCharsets.UTF_8)); return resourceService.saveResource(resource); } diff --git a/ui-ngx/src/app/core/http/resource.service.ts b/ui-ngx/src/app/core/http/resource.service.ts index f7cb8441d1..76f28fbe45 100644 --- a/ui-ngx/src/app/core/http/resource.service.ts +++ b/ui-ngx/src/app/core/http/resource.service.ts @@ -42,10 +42,6 @@ export class ResourceService { return this.http.get>(url, defaultHttpOptionsFromConfig(config)); } - public getResource(resourceId: string, config?: RequestConfig): Observable { - return this.http.get(`/api/resource/${resourceId}`, defaultHttpOptionsFromConfig(config)); - } - public getResourceInfo(resourceId: string, config?: RequestConfig): Observable { return this.http.get(`/api/resource/info/${resourceId}`, defaultHttpOptionsFromConfig(config)); } diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html index 8b225af53b..f2511bdcd9 100644 --- a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html @@ -386,6 +386,22 @@ +
+ + tenant-profile.maximum-resource-size + + + {{ 'tenant-profile.maximum-resource-size-required' | translate}} + + + {{ 'tenant-profile.maximum-resource-size-range' | translate}} + + + +
+
diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.ts index 5b48e02e07..7718cd4b2f 100644 --- a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.ts @@ -67,6 +67,7 @@ export class DefaultTenantProfileConfigurationComponent implements ControlValueA maxRuleChains: [null, [Validators.required, Validators.min(0)]], maxResourcesInBytes: [null, [Validators.required, Validators.min(0)]], maxOtaPackagesInBytes: [null, [Validators.required, Validators.min(0)]], + maxResourceSize: [null, [Validators.required, Validators.min(0)]], transportTenantMsgRateLimit: [null, []], transportTenantTelemetryMsgRateLimit: [null, []], transportTenantTelemetryDataPointsRateLimit: [null, []], diff --git a/ui-ngx/src/app/modules/home/pages/admin/resource/resources-library-table-config.resolve.ts b/ui-ngx/src/app/modules/home/pages/admin/resource/resources-library-table-config.resolve.ts index 733e5ef2b3..52ded64e29 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/resource/resources-library-table-config.resolve.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/resource/resources-library-table-config.resolve.ts @@ -84,7 +84,7 @@ export class ResourcesLibraryTableConfigResolver implements Resolve this.translate.instant('resource.delete-resources-text'); this.config.entitiesFetchFunction = pageLink => this.resourceService.getResources(pageLink, this.config.componentsData.resourceType); - this.config.loadEntity = id => this.resourceService.getResource(id.id); + this.config.loadEntity = id => this.resourceService.getResourceInfo(id.id); this.config.saveEntity = resource => this.saveResource(resource); this.config.deleteEntity = id => this.resourceService.deleteResource(id.id); diff --git a/ui-ngx/src/app/modules/home/pages/admin/resource/resources-library.component.html b/ui-ngx/src/app/modules/home/pages/admin/resource/resources-library.component.html index f955d26f0d..cb7dbf9ddc 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/resource/resources-library.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/resource/resources-library.component.html @@ -67,7 +67,7 @@ impleme this.entityForm.get('title').enable({emitEvent: false}); } this.entityForm.patchValue({ - data: null, + base64Data: null, fileName: null }, {emitEvent: false}); }); @@ -93,7 +93,7 @@ export class ResourcesLibraryComponent extends EntityComponent impleme title: [entity ? entity.title : '', [Validators.required, Validators.maxLength(255)]], resourceType: [entity?.resourceType ? entity.resourceType : ResourceType.JS_MODULE, Validators.required], fileName: [entity ? entity.fileName : null, Validators.required], - data: [entity ? entity.data : null, Validators.required] + base64Data: [entity ? entity.base64Data : null, Validators.required] }); } @@ -102,20 +102,20 @@ export class ResourcesLibraryComponent extends EntityComponent impleme this.entityForm.get('resourceType').disable({emitEvent: false}); if (entity.resourceType !== ResourceType.JS_MODULE) { this.entityForm.get('fileName').disable({emitEvent: false}); - this.entityForm.get('data').disable({emitEvent: false}); + this.entityForm.get('base64Data').disable({emitEvent: false}); } } this.entityForm.patchValue({ resourceType: entity.resourceType, fileName: entity.fileName, title: entity.title, - data: entity.data + base64Data: entity.base64Data }); } prepareFormValue(formValue: Resource): Resource { - if (this.isEdit && !isDefinedAndNotNull(formValue.data)) { - delete formValue.data; + if (this.isEdit && !isDefinedAndNotNull(formValue.base64Data)) { + delete formValue.base64Data; } return super.prepareFormValue(formValue); } diff --git a/ui-ngx/src/app/shared/models/resource.models.ts b/ui-ngx/src/app/shared/models/resource.models.ts index 6621f10c42..5c0d2c3ccc 100644 --- a/ui-ngx/src/app/shared/models/resource.models.ts +++ b/ui-ngx/src/app/shared/models/resource.models.ts @@ -57,14 +57,15 @@ export interface ResourceInfo extends Omit, 'name' | 'lab resourceKey?: string; title?: string; resourceType: ResourceType; + fileName: string; } export interface Resource extends ResourceInfo { - data: string; - fileName: string; + base64Data: string; name?: string; } +// @ts-ignore FIXME export interface Resources extends ResourceInfo { data: Array; fileName: Array; diff --git a/ui-ngx/src/app/shared/models/tenant.model.ts b/ui-ngx/src/app/shared/models/tenant.model.ts index 73b3d9de02..6cde443f62 100644 --- a/ui-ngx/src/app/shared/models/tenant.model.ts +++ b/ui-ngx/src/app/shared/models/tenant.model.ts @@ -33,6 +33,7 @@ export interface DefaultTenantProfileConfiguration { maxRuleChains: number; maxResourcesInBytes: number; maxOtaPackagesInBytes: number; + maxResourceSize: number; transportTenantMsgRateLimit?: string; transportTenantTelemetryMsgRateLimit?: string; @@ -101,6 +102,7 @@ export function createTenantProfileConfiguration(type: TenantProfileType): Tenan maxRuleChains: 0, maxResourcesInBytes: 0, maxOtaPackagesInBytes: 0, + maxResourceSize: 0, maxTransportMessages: 0, maxTransportDataPoints: 0, maxREExecutions: 0, 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 f5c09ce75f..745ac8ecb4 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -3948,7 +3948,7 @@ "rule-engine": "Rule Engine", "time-to-live": "Time-to-live", "alarms-and-notifications": "Alarms and notifications", - "ota-files-in-bytes": "OTA files in bytes", + "ota-files-in-bytes": "Files", "ws-title": "WS", "unlimited": "(0 - unlimited)", "maximum-devices": "Devices maximum number", @@ -3971,10 +3971,13 @@ "maximum-rule-chains-range": "Rule chains maximum number can't be negative", "maximum-resources-sum-data-size": "Resource files sum size", "maximum-resources-sum-data-size-required": "Resource files sum size is required.", - "maximum-resources-sum-data-size-range": "Resource files sum size can`t be negative", + "maximum-resources-sum-data-size-range": "Resource files sum size can't be negative", + "maximum-resource-size": "Resource maximum size", + "maximum-resource-size-required": "Resource maximum size is required", + "maximum-resource-size-range": "Resource maximum size can't be negative", "maximum-ota-packages-sum-data-size": "OTA package files sum size", "maximum-ota-package-sum-data-size-required": "OTA package files sum size is required.", - "maximum-ota-package-sum-data-size-range": "OTA package files sum size can`t be negative", + "maximum-ota-package-sum-data-size-range": "OTA package files sum size can't be negative", "rest-requests-for-tenant": "REST requests for tenant", "transport-tenant-telemetry-msg-rate-limit": "Transport tenant telemetry messages", "transport-tenant-telemetry-data-points-rate-limit": "Transport tenant telemetry data points", From b206061e602f8cbb699bb4835588bf4604350112 Mon Sep 17 00:00:00 2001 From: Dmitriymush Date: Tue, 31 Oct 2023 13:35:56 +0200 Subject: [PATCH 011/252] UI: bug-fix to tables basic settings columns error appearence --- .../basic/alarm/alarms-table-basic-config.component.html | 1 + ui-ngx/src/app/shared/components/tb-error.component.ts | 6 +++++- ui-ngx/src/assets/locale/locale.constant-en_US.json | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/alarm/alarms-table-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/alarm/alarms-table-basic-config.component.html index 860072fbbd..c2e8de37c4 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/alarm/alarms-table-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/alarm/alarms-table-basic-config.component.html @@ -40,6 +40,7 @@ keySettingsTitle="{{ 'widgets.table.column-settings' | translate }}" removeKeyTitle="{{ 'widgets.table.remove-column' | translate }}" noKeysText="{{ 'widgets.table.no-columns' | translate }}" + requiredKeysText="{{ 'widgets.table.alarm-column-error' | translate }}" hideDataKeyColor hideUnits hideDecimals diff --git a/ui-ngx/src/app/shared/components/tb-error.component.ts b/ui-ngx/src/app/shared/components/tb-error.component.ts index 5ddd2d7896..3cd842ee6a 100644 --- a/ui-ngx/src/app/shared/components/tb-error.component.ts +++ b/ui-ngx/src/app/shared/components/tb-error.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, Input } from '@angular/core'; +import { ChangeDetectorRef, Component, Input } from '@angular/core'; import { animate, state, style, transition, trigger } from '@angular/animations'; import { coerceBoolean } from '@shared/decorators/coercion'; @@ -61,12 +61,16 @@ export class TbErrorComponent { if (value && !this.message) { this.message = value; this.state = 'hide'; + setTimeout(() => { this.state = 'show'; + this.cd.markForCheck(); }); } else { this.errorValue = value; this.state = value ? 'show' : 'hide'; } } + + constructor(private cd: ChangeDetectorRef) {} } 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 f5c09ce75f..efb94c7782 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -6193,6 +6193,7 @@ "pagination": "Pagination", "rows": "Rows", "timeseries-column-error": "At least one timeseries column should be specified", + "alarm-column-error": "At least one alarm column should be specified", "table-tabs": "Table tabs", "show-cell-actions-menu-mobile": "Show cell actions dropdown menu in mobile mode" }, From 7d87ee4829374c0c5400c58e2f3e451a4a00346c Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Tue, 31 Oct 2023 17:00:17 +0200 Subject: [PATCH 012/252] UI: Refactoring --- ui-ngx/src/app/core/http/entity.service.ts | 4 ++-- ui-ngx/src/app/core/http/widget.service.ts | 4 ++-- .../components/vc/entity-types-version-create.component.html | 1 + 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/ui-ngx/src/app/core/http/entity.service.ts b/ui-ngx/src/app/core/http/entity.service.ts index 4e04304754..ac13aa31c8 100644 --- a/ui-ngx/src/app/core/http/entity.service.ts +++ b/ui-ngx/src/app/core/http/entity.service.ts @@ -418,11 +418,11 @@ export class EntityService { break; case EntityType.WIDGETS_BUNDLE: pageLink.sortOrder.property = 'title'; - entitiesObservable = this.widgetService.getWidgetBundles(pageLink, true, false, config); + entitiesObservable = this.widgetService.getWidgetBundles(pageLink, false, subType === 'tenant', config); break; case EntityType.WIDGET_TYPE: pageLink.sortOrder.property = 'name'; - entitiesObservable = this.widgetService.getWidgetTypes(pageLink, true, false, DeprecatedFilter.ALL, null, config); + entitiesObservable = this.widgetService.getWidgetTypes(pageLink, subType === 'tenant', false, DeprecatedFilter.ALL, null, config); break; case EntityType.NOTIFICATION_TARGET: pageLink.sortOrder.property = 'name'; diff --git a/ui-ngx/src/app/core/http/widget.service.ts b/ui-ngx/src/app/core/http/widget.service.ts index 3d33aad9e7..6905a0f594 100644 --- a/ui-ngx/src/app/core/http/widget.service.ts +++ b/ui-ngx/src/app/core/http/widget.service.ts @@ -85,8 +85,8 @@ export class WidgetService { ); } - public getWidgetBundles(pageLink: PageLink, tenantOnly = false, - fullSearch = false, config?: RequestConfig): Observable> { + public getWidgetBundles(pageLink: PageLink, fullSearch = false, + tenantOnly = false, config?: RequestConfig): Observable> { return this.http.get>( `/api/widgetsBundles${pageLink.toQuery()}&tenantOnly=${tenantOnly}&fullSearch=${fullSearch}`, defaultHttpOptionsFromConfig(config) diff --git a/ui-ngx/src/app/modules/home/components/vc/entity-types-version-create.component.html b/ui-ngx/src/app/modules/home/components/vc/entity-types-version-create.component.html index ef09a2b382..11b37245fa 100644 --- a/ui-ngx/src/app/modules/home/components/vc/entity-types-version-create.component.html +++ b/ui-ngx/src/app/modules/home/components/vc/entity-types-version-create.component.html @@ -84,6 +84,7 @@ fxFlex [fxShow]="!entityTypeFormGroup.get('config').get('allEntities').value" [entityType]="entityTypeFormGroup.get('entityType').value" + subType="tenant" required formControlName="entityIds"> From 900bb9a57ca4ea284f5fe7eb8099cf22ef3d0f6f Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Wed, 1 Nov 2023 12:28:24 +0200 Subject: [PATCH 013/252] UI: Refactoring entity subtype list --- .../components/entity/entity-subtype-list.component.html | 8 ++------ .../components/entity/entity-subtype-list.component.ts | 6 ++++++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/ui-ngx/src/app/shared/components/entity/entity-subtype-list.component.html b/ui-ngx/src/app/shared/components/entity/entity-subtype-list.component.html index 44c61d605a..fa2d716530 100644 --- a/ui-ngx/src/app/shared/components/entity/entity-subtype-list.component.html +++ b/ui-ngx/src/app/shared/components/entity/entity-subtype-list.component.html @@ -24,7 +24,7 @@ *ngFor="let entitySubtype of entitySubtypeList" [removable]="!disabled" (removed)="remove(entitySubtype)"> - {{entitySubtype}} + {{customTranslate(entitySubtype)}} close - - - {{ translate.get(noSubtypesMathingText, {entitySubtype: searchText}) | async }} - -
diff --git a/ui-ngx/src/app/shared/components/entity/entity-subtype-list.component.ts b/ui-ngx/src/app/shared/components/entity/entity-subtype-list.component.ts index b2ea6a185d..6661d257b4 100644 --- a/ui-ngx/src/app/shared/components/entity/entity-subtype-list.component.ts +++ b/ui-ngx/src/app/shared/components/entity/entity-subtype-list.component.ts @@ -33,6 +33,7 @@ import { FloatLabelType, MatFormFieldAppearance, SubscriptSizing } from '@angula import { coerceArray, coerceBoolean } from '@shared/decorators/coercion'; import { PageLink } from '@shared/models/page/page-link'; import { PageData } from '@shared/models/page/page-data'; +import { UtilsService } from '@core/services/utils.service'; @Component({ selector: 'tb-entity-subtype-list', @@ -129,6 +130,7 @@ export class EntitySubTypeListComponent implements ControlValueAccessor, OnInit, private edgeService: EdgeService, private entityViewService: EntityViewService, private alarmService: AlarmService, + private utils: UtilsService, private fb: FormBuilder) { this.entitySubtypeListFormGroup = this.fb.group({ entitySubtypeList: [this.entitySubtypeList, this.required ? [Validators.required] : []], @@ -372,4 +374,8 @@ export class EntitySubTypeListComponent implements ControlValueAccessor, OnInit, }, 0); } + customTranslate(entity: string) { + return this.utils.customTranslation(entity, entity); + } + } From 883c51f334cc67197e666abc927044ef70286a1b Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Wed, 1 Nov 2023 13:14:19 +0200 Subject: [PATCH 014/252] UI: Refactoring --- .../profile/add-device-profile-dialog.component.scss | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.scss b/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.scss index c2cc55fb83..0018264075 100644 --- a/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.scss +++ b/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.scss @@ -40,13 +40,10 @@ padding: 0 !important; .mat-stepper-horizontal { - display: flex; - height: 100%; overflow: hidden; .mat-horizontal-stepper-wrapper { - max-width: 100%; - flex: 1 1 100%; + max-height: 100%; } .mat-horizontal-content-container { From 70eb9030c26a6a2de07ce4ba04e535ad368a64f1 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Wed, 1 Nov 2023 14:16:57 +0200 Subject: [PATCH 015/252] Image resources --- .../main/data/upgrade/3.6.1/schema_update.sql | 6 + .../server/controller/BaseController.java | 6 +- .../controller/TbResourceController.java | 44 +++++-- .../service/install/InstallScripts.java | 3 +- .../resource/DefaultTbResourceService.java | 109 +++++------------- .../service/resource/TbResourceService.java | 18 --- .../transport/DefaultTransportApiService.java | 7 +- .../controller/TbResourceControllerTest.java | 100 +++++++++++++++- .../sql/BaseTbResourceServiceTest.java | 53 +++++---- .../server/dao/resource/ResourceService.java | 5 +- .../server/common/data/ResourceType.java | 19 +-- .../server/common/data/TbResource.java | 2 +- .../server/common/data/TbResourceInfo.java | 6 + .../server/dao/model/ModelConstants.java | 2 + .../dao/model/sql/TbResourceEntity.java | 12 ++ .../dao/model/sql/TbResourceInfoEntity.java | 12 ++ .../dao/resource/BaseResourceService.java | 35 ++++-- .../server/dao/resource/TbResourceDao.java | 2 +- .../dao/resource/TbResourceInfoDao.java | 6 +- .../validator/ResourceDataValidator.java | 44 ++++--- .../dao/sql/resource/JpaTbResourceDao.java | 3 +- .../sql/resource/JpaTbResourceInfoDao.java | 5 + .../resource/TbResourceInfoRepository.java | 2 + .../main/resources/sql/schema-entities.sql | 2 + .../dao/service/OtaPackageServiceTest.java | 2 +- ui-ngx/src/app/core/http/resource.service.ts | 4 + .../src/app/shared/models/resource.models.ts | 17 ++- .../assets/locale/locale.constant-en_US.json | 3 +- 28 files changed, 335 insertions(+), 194 deletions(-) diff --git a/application/src/main/data/upgrade/3.6.1/schema_update.sql b/application/src/main/data/upgrade/3.6.1/schema_update.sql index 5ebe39fb6e..7f5838f8bf 100644 --- a/application/src/main/data/upgrade/3.6.1/schema_update.sql +++ b/application/src/main/data/upgrade/3.6.1/schema_update.sql @@ -14,6 +14,8 @@ -- limitations under the License. -- +-- RESOURCES UPDATE START + DO $$ BEGIN @@ -25,3 +27,7 @@ $$ END IF; END; $$; +ALTER TABLE resource ADD COLUMN IF NOT EXISTS media_type varchar(255); +ALTER TABLE resource ADD COLUMN IF NOT EXISTS link varchar(255); + +-- RESOURCES UPDATE END diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index 365f1fc379..8226610a7d 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -124,6 +124,7 @@ import org.thingsboard.server.dao.oauth2.OAuth2Service; import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.relation.RelationService; +import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.rpc.RpcService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.service.ConstraintValidator; @@ -145,7 +146,6 @@ import org.thingsboard.server.service.entitiy.user.TbUserSettingsService; import org.thingsboard.server.service.ota.OtaPackageStateService; import org.thingsboard.server.service.profile.TbAssetProfileCache; import org.thingsboard.server.service.profile.TbDeviceProfileCache; -import org.thingsboard.server.service.resource.TbResourceService; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.AccessControlService; import org.thingsboard.server.service.security.permission.Operation; @@ -272,7 +272,7 @@ public abstract class BaseController { protected PartitionService partitionService; @Autowired - protected TbResourceService resourceService; + protected ResourceService resourceService; @Autowired protected OtaPackageService otaPackageService; @@ -587,7 +587,7 @@ public abstract class BaseController { checkWidgetTypeId(new WidgetTypeId(entityId.getId()), operation); return; case TB_RESOURCE: - checkResourceId(new TbResourceId(entityId.getId()), operation); + checkResourceInfoId(new TbResourceId(entityId.getId()), operation); return; case OTA_PACKAGE: checkOtaPackageId(new OtaPackageId(entityId.getId()), operation); diff --git a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java index de18849b3a..9874db3002 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java @@ -44,6 +44,7 @@ import org.thingsboard.server.common.data.TbResourceInfo; import org.thingsboard.server.common.data.TbResourceInfoFilter; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.TbResourceId; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.lwm2m.LwM2mObject; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; @@ -55,6 +56,7 @@ import org.thingsboard.server.service.security.permission.Resource; import java.util.Base64; import java.util.List; +import java.util.function.Supplier; import static org.thingsboard.server.controller.ControllerConstants.AVAILABLE_FOR_ANY_AUTHORIZED_USER; import static org.thingsboard.server.controller.ControllerConstants.LWM2M_OBJECT_DESCRIPTION; @@ -107,6 +109,24 @@ public class TbResourceController extends BaseController { .body(resource); } + @ApiOperation(value = "Download Image (downloadImageIfChanged)", notes = DOWNLOAD_RESOURCE_IF_NOT_CHANGED + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @GetMapping(value = "/images/{tenantId}/{resourceKey}", produces = "image/*") + @ResponseBody + public ResponseEntity downloadImageIfChanged(@PathVariable("tenantId") String tenantIdStr, + @PathVariable("resourceKey") String resourceKey, + @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws ThingsboardException { + TenantId tenantId; + if (tenantIdStr.equals("system")) { + tenantId = TenantId.SYS_TENANT_ID; + } else { + tenantId = TenantId.fromUUID(toUUID(tenantIdStr)); + } + return downloadResourceIfChanged(ResourceType.IMAGE, etag, + () -> resourceService.findResourceInfoByTenantIdAndKey(tenantId, ResourceType.IMAGE, resourceKey), + () -> resourceService.findResourceByTenantIdAndKey(tenantId, ResourceType.IMAGE, resourceKey)); + } + @ApiOperation(value = "Download LWM2M Resource (downloadLwm2mResourceIfChanged)", notes = DOWNLOAD_RESOURCE_IF_NOT_CHANGED + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @GetMapping(value = "/resource/lwm2m/{resourceId}/download", produces = "application/xml") @@ -249,7 +269,7 @@ public class TbResourceController extends BaseController { @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { PageLink pageLink = new PageLink(pageSize, page, textSearch); - return checkNotNull(resourceService.findLwM2mObjectPage(getTenantId(), sortProperty, sortOrder, pageLink)); + return checkNotNull(tbResourceService.findLwM2mObjectPage(getTenantId(), sortProperty, sortOrder, pageLink)); } @ApiOperation(value = "Get LwM2M Objects (getLwm2mListObjects)", @@ -265,7 +285,7 @@ public class TbResourceController extends BaseController { @RequestParam String sortProperty, @ApiParam(value = "LwM2M Object ids.", required = true) @RequestParam(required = false) String[] objectIds) throws ThingsboardException { - return checkNotNull(resourceService.findLwM2mObject(getTenantId(), sortOrder, sortProperty, objectIds)); + return checkNotNull(tbResourceService.findLwM2mObject(getTenantId(), sortOrder, sortProperty, objectIds)); } @ApiOperation(value = "Delete Resource (deleteResource)", @@ -281,12 +301,21 @@ public class TbResourceController extends BaseController { tbResourceService.delete(tbResource, getCurrentUser()); } - private ResponseEntity downloadResourceIfChanged(ResourceType type, String strResourceId, String etag) throws ThingsboardException { + private ResponseEntity downloadResourceIfChanged(ResourceType resourceType, String strResourceId, String etag) throws ThingsboardException { checkParameter(RESOURCE_ID, strResourceId); + TenantId tenantId = getTenantId(); TbResourceId resourceId = new TbResourceId(toUUID(strResourceId)); + return downloadResourceIfChanged(resourceType, etag, + () -> resourceService.findResourceInfoById(tenantId, resourceId), + () -> resourceService.findResourceById(tenantId, resourceId)); + } + private ResponseEntity downloadResourceIfChanged(ResourceType resourceType, String etag, + Supplier resourceInfoSupplier, + Supplier resourceSupplier) throws ThingsboardException { if (etag != null) { - TbResourceInfo tbResourceInfo = checkResourceInfoId(resourceId, Operation.READ); + TbResourceInfo tbResourceInfo = resourceInfoSupplier.get(); + checkEntity(getCurrentUser(), tbResourceInfo, Operation.READ); if (etag.equals(tbResourceInfo.getEtag())) { return ResponseEntity.status(HttpStatus.NOT_MODIFIED) .eTag(tbResourceInfo.getEtag()) @@ -294,14 +323,15 @@ public class TbResourceController extends BaseController { } } - TbResource tbResource = checkResourceId(resourceId, Operation.READ); - ByteArrayResource resource = new ByteArrayResource(tbResource.getData()); + TbResource tbResource = resourceSupplier.get(); + checkEntity(getCurrentUser(), tbResource, Operation.READ); + ByteArrayResource resource = new ByteArrayResource(tbResource.getData()); return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + tbResource.getFileName()) .header("x-filename", tbResource.getFileName()) .contentLength(resource.contentLength()) - .header("Content-Type", type.getMediaType()) + .header("Content-Type", tbResource.getMediaType() != null ? tbResource.getMediaType() : resourceType.getDefaultMediaType()) .cacheControl(CacheControl.noCache()) .eTag(tbResource.getEtag()) .body(resource); diff --git a/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java b/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java index 00627e0421..49f487ebf0 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java +++ b/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java @@ -333,8 +333,7 @@ public class InstallScripts { throw new DataValidationException("Resource data should be specified!"); } toLwm2mResource(resource); - TbResource foundResource = - resourceService.getResource(TenantId.SYS_TENANT_ID, ResourceType.LWM2M_MODEL, resource.getResourceKey()); + TbResource foundResource = resourceService.findResourceByTenantIdAndKey(TenantId.SYS_TENANT_ID, ResourceType.LWM2M_MODEL, resource.getResourceKey()); if (foundResource == null) { resourceService.saveResource(resource); } diff --git a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java index d1ce667345..2cdbd8d7e2 100644 --- a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java +++ b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java @@ -15,13 +15,12 @@ */ package org.thingsboard.server.service.resource; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.TbResource; -import org.thingsboard.server.common.data.TbResourceInfo; -import org.thingsboard.server.common.data.TbResourceInfoFilter; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.exception.ThingsboardException; @@ -31,7 +30,6 @@ import org.thingsboard.server.common.data.lwm2m.LwM2mObject; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.resource.ResourceService; -import org.thingsboard.server.dao.widget.WidgetTypeService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.entitiy.AbstractTbEntityService; @@ -48,39 +46,45 @@ import static org.thingsboard.server.utils.LwM2mObjectModelUtils.toLwm2mResource @Slf4j @Service @TbCoreComponent +@RequiredArgsConstructor public class DefaultTbResourceService extends AbstractTbEntityService implements TbResourceService { private final ResourceService resourceService; - private final WidgetTypeService widgetTypeService; - - public DefaultTbResourceService(ResourceService resourceService, WidgetTypeService widgetTypeService) { - this.resourceService = resourceService; - this.widgetTypeService = widgetTypeService; - } - - @Override - public TbResource getResource(TenantId tenantId, ResourceType resourceType, String resourceId) { - return resourceService.getResource(tenantId, resourceType, resourceId); - } @Override - public TbResource findResourceById(TenantId tenantId, TbResourceId resourceId) { - return resourceService.findResourceById(tenantId, resourceId); - } - - @Override - public TbResourceInfo findResourceInfoById(TenantId tenantId, TbResourceId resourceId) { - return resourceService.findResourceInfoById(tenantId, resourceId); - } - - @Override - public PageData findAllTenantResourcesByTenantId(TbResourceInfoFilter filter, PageLink pageLink) { - return resourceService.findAllTenantResourcesByTenantId(filter, pageLink); + public TbResource save(TbResource resource, User user) throws ThingsboardException { + ActionType actionType = resource.getId() == null ? ActionType.ADDED : ActionType.UPDATED; + TenantId tenantId = resource.getTenantId(); + try { + if (ResourceType.LWM2M_MODEL.equals(resource.getResourceType())) { + toLwm2mResource(resource); + } else if (resource.getResourceKey() == null) { + resource.setResourceKey(resource.getFileName()); + } + TbResource savedResource = resourceService.saveResource(resource); + tbClusterService.onResourceChange(savedResource, null); + notificationEntityService.logEntityAction(tenantId, savedResource.getId(), savedResource, actionType, user); + return savedResource; + } catch (Exception e) { + notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.TB_RESOURCE), + resource, actionType, user, e); + throw e; + } } @Override - public PageData findTenantResourcesByTenantId(TbResourceInfoFilter filter, PageLink pageLink) { - return resourceService.findTenantResourcesByTenantId(filter, pageLink); + public void delete(TbResource tbResource, User user) { + TbResourceId resourceId = tbResource.getId(); + TenantId tenantId = tbResource.getTenantId(); + try { + resourceService.deleteResource(tenantId, resourceId); + tbClusterService.onResourceDeleted(tbResource, null); + notificationEntityService.logEntityAction(tenantId, resourceId, tbResource, ActionType.DELETED, user, resourceId.toString()); + } catch (Exception e) { + notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.TB_RESOURCE), + ActionType.DELETED, user, e, resourceId.toString()); + throw e; + } } @Override @@ -106,16 +110,6 @@ public class DefaultTbResourceService extends AbstractTbEntityService implements .collect(Collectors.toList()); } - @Override - public void deleteResourcesByTenantId(TenantId tenantId) { - resourceService.deleteResourcesByTenantId(tenantId); - } - - @Override - public long sumDataSizeByTenantId(TenantId tenantId) { - return resourceService.sumDataSizeByTenantId(tenantId); - } - private Comparator getComparator(String sortProperty, String sortOrder) { Comparator comparator; if ("name".equals(sortProperty)) { @@ -126,43 +120,4 @@ public class DefaultTbResourceService extends AbstractTbEntityService implements return "DESC".equals(sortOrder) ? comparator.reversed() : comparator; } - @Override - public TbResource save(TbResource tbResource, User user) throws ThingsboardException { - ActionType actionType = tbResource.getId() == null ? ActionType.ADDED : ActionType.UPDATED; - TenantId tenantId = tbResource.getTenantId(); - try { - TbResource savedResource = checkNotNull(doSave(tbResource)); - tbClusterService.onResourceChange(savedResource, null); - notificationEntityService.logEntityAction(tenantId, savedResource.getId(), savedResource, actionType, user); - return savedResource; - } catch (Exception e) { - notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.TB_RESOURCE), - tbResource, actionType, user, e); - throw e; - } - } - - @Override - public void delete(TbResource tbResource, User user) { - TbResourceId resourceId = tbResource.getId(); - TenantId tenantId = tbResource.getTenantId(); - try { - resourceService.deleteResource(tenantId, resourceId); - tbClusterService.onResourceDeleted(tbResource, null); - notificationEntityService.logEntityAction(tenantId, resourceId, tbResource, ActionType.DELETED, user, resourceId.toString()); - } catch (Exception e) { - notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.TB_RESOURCE), - ActionType.DELETED, user, e, resourceId.toString()); - throw e; - } - } - - private TbResource doSave(TbResource resource) throws ThingsboardException { - if (ResourceType.LWM2M_MODEL.equals(resource.getResourceType())) { - toLwm2mResource(resource); - } else { - resource.setResourceKey(resource.getFileName()); - } - return resourceService.saveResource(resource); - } } diff --git a/application/src/main/java/org/thingsboard/server/service/resource/TbResourceService.java b/application/src/main/java/org/thingsboard/server/service/resource/TbResourceService.java index 40fe7dda9a..e6dbc0f071 100644 --- a/application/src/main/java/org/thingsboard/server/service/resource/TbResourceService.java +++ b/application/src/main/java/org/thingsboard/server/service/resource/TbResourceService.java @@ -15,14 +15,9 @@ */ package org.thingsboard.server.service.resource; -import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.TbResource; -import org.thingsboard.server.common.data.TbResourceInfo; -import org.thingsboard.server.common.data.TbResourceInfoFilter; -import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.lwm2m.LwM2mObject; -import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.service.entitiy.SimpleTbEntityService; @@ -30,16 +25,6 @@ import java.util.List; public interface TbResourceService extends SimpleTbEntityService { - TbResource getResource(TenantId tenantId, ResourceType resourceType, String resourceKey); - - TbResource findResourceById(TenantId tenantId, TbResourceId resourceId); - - TbResourceInfo findResourceInfoById(TenantId tenantId, TbResourceId resourceId); - - PageData findAllTenantResourcesByTenantId(TbResourceInfoFilter filter, PageLink pageLink); - - PageData findTenantResourcesByTenantId(TbResourceInfoFilter filter, PageLink pageLink); - List findLwM2mObject(TenantId tenantId, String sortOrder, String sortProperty, @@ -50,7 +35,4 @@ public interface TbResourceService extends SimpleTbEntityService { String sortOrder, PageLink pageLink); - void deleteResourcesByTenantId(TenantId tenantId); - - long sumDataSizeByTenantId(TenantId tenantId); } diff --git a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java index 89a7480708..d197e1b4a4 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java @@ -77,6 +77,7 @@ import org.thingsboard.server.dao.device.provision.ProvisionResponseStatus; import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.relation.RelationService; +import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto; @@ -139,7 +140,7 @@ public class DefaultTransportApiService implements TransportApiService { private final TbClusterService tbClusterService; private final DataDecodingEncodingService dataDecodingEncodingService; private final DeviceProvisionService deviceProvisionService; - private final TbResourceService resourceService; + private final ResourceService resourceService; private final OtaPackageService otaPackageService; private final OtaPackageDataCache otaPackageDataCache; private final QueueService queueService; @@ -489,10 +490,10 @@ public class DefaultTransportApiService implements TransportApiService { ResourceType resourceType = ResourceType.valueOf(requestMsg.getResourceType()); String resourceKey = requestMsg.getResourceKey(); TransportProtos.GetResourceResponseMsg.Builder builder = TransportProtos.GetResourceResponseMsg.newBuilder(); - TbResource resource = resourceService.getResource(tenantId, resourceType, resourceKey); + TbResource resource = resourceService.findResourceByTenantIdAndKey(tenantId, resourceType, resourceKey); if (resource == null && !tenantId.equals(TenantId.SYS_TENANT_ID)) { - resource = resourceService.getResource(TenantId.SYS_TENANT_ID, resourceType, resourceKey); + resource = resourceService.findResourceByTenantIdAndKey(TenantId.SYS_TENANT_ID, resourceType, resourceKey); } if (resource != null) { diff --git a/application/src/test/java/org/thingsboard/server/controller/TbResourceControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/TbResourceControllerTest.java index 0e05165c77..d4c21c63fc 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TbResourceControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/TbResourceControllerTest.java @@ -47,17 +47,13 @@ import java.util.Base64; import java.util.Collections; import java.util.List; +import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.containsString; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @DaoSqlTest public class TbResourceControllerTest extends AbstractControllerTest { - /* - * TODO: - * test maxResourceSize limit in tenant profile - * */ - private IdComparator idComparator = new IdComparator<>(); private static final String DEFAULT_FILE_NAME = "test.jks"; @@ -621,6 +617,100 @@ public class TbResourceControllerTest extends AbstractControllerTest { .andExpect(status().isForbidden()); } + @Test + public void testUpdateResourceData_nonUpdatableResourceType() throws Exception { + TbResource resource = new TbResource(); + resource.setResourceType(ResourceType.IMAGE); + resource.setTitle("My resource"); + resource.setFileName("image.png"); + resource.setMediaType("image/png"); + resource.setBase64Data(TEST_DATA); + TbResource savedResource = save(resource); + resource.setEtag(savedResource.getEtag()); + + savedResource.setBase64Data(TEST_DATA); + doPost("/api/resource", savedResource) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString("can't be updated"))); + + savedResource.setBase64Data(null); + savedResource.setTitle("Updated resource"); + savedResource = doPost("/api/resource", savedResource, TbResource.class); + assertThat(savedResource.getTitle()).isEqualTo("Updated resource"); + assertThat(savedResource.getFileName()).isEqualTo(resource.getFileName()); + assertThat(savedResource.getMediaType()).isEqualTo(resource.getMediaType()); + assertThat(savedResource.getEtag()).isEqualTo(resource.getEtag()); + assertThat(download(savedResource.getId())).asBase64Encoded().isEqualTo(TEST_DATA); + } + + @Test + public void testUpdateResourceData_updatableResourceType() throws Exception { + TbResource resource = new TbResource(); + resource.setResourceType(ResourceType.JS_MODULE); + resource.setTitle("My resource"); + resource.setFileName("module.js"); + resource.setBase64Data(TEST_DATA); + TbResource savedResource = save(resource); + resource.setEtag(savedResource.getEtag()); + + String newData = Base64.getEncoder().encodeToString(new byte[]{1, 2, 3}); + savedResource.setBase64Data(newData); + savedResource.setFileName("new-module.js"); + savedResource.setTitle("Updated title"); + savedResource = save(savedResource); + + assertThat(savedResource.getTitle()).isEqualTo("Updated title"); + assertThat(savedResource.getFileName()).isEqualTo("new-module.js"); + assertThat(savedResource.getEtag()).isNotEqualTo(resource.getEtag()); + assertThat(download(savedResource.getId())).asBase64Encoded().isEqualTo(newData); + } + + @Test + public void testSaveImage_systemLevel() throws Exception { + loginSysAdmin(); + TbResource resource = new TbResource(); + resource.setResourceType(ResourceType.IMAGE); + resource.setTitle("System image"); + resource.setFileName("image.png"); + resource.setMediaType("image/png"); + resource.setBase64Data(TEST_DATA); + + TbResource savedResource = save(resource); + assertThat(savedResource.getLink()).isEqualTo("/api/images/system/image.png"); + + loginTenantAdmin(); + MockHttpServletResponse imageResponse = doGet(savedResource.getLink()).andExpect(status().isOk()) + .andReturn().getResponse(); + assertThat(imageResponse.getContentAsByteArray()) + .isEqualTo(download(savedResource.getId())) + .isEqualTo(Base64.getDecoder().decode(TEST_DATA)); + assertThat(imageResponse.getContentType()).isEqualTo("image/png"); + } + + @Test + public void testSaveImage_tenantLevel() throws Exception { + loginTenantAdmin(); + TbResource resource = new TbResource(); + resource.setResourceType(ResourceType.IMAGE); + resource.setTitle("My image"); + resource.setFileName("image.jpg"); + resource.setMediaType("image/jpeg"); + resource.setBase64Data(TEST_DATA); + + TbResource savedResource = save(resource); + assertThat(savedResource.getLink()).isEqualTo("/api/images/" + tenantId + "/image.jpg"); + + MockHttpServletResponse imageResponse = doGet(savedResource.getLink()).andExpect(status().isOk()) + .andReturn().getResponse(); + assertThat(imageResponse.getContentAsByteArray()) + .isEqualTo(download(savedResource.getId())) + .isEqualTo(Base64.getDecoder().decode(TEST_DATA)); + assertThat(imageResponse.getContentType()).isEqualTo("image/jpeg"); + + loginDifferentTenant(); + doGet(savedResource.getLink()).andExpect(status().isForbidden()); + } + private TbResource save(TbResource tbResource) throws Exception { return doPostWithTypedResponse("/api/resource", tbResource, new TypeReference<>(){}); } diff --git a/application/src/test/java/org/thingsboard/server/service/resource/sql/BaseTbResourceServiceTest.java b/application/src/test/java/org/thingsboard/server/service/resource/sql/BaseTbResourceServiceTest.java index ab55840f3c..e441a0b144 100644 --- a/application/src/test/java/org/thingsboard/server/service/resource/sql/BaseTbResourceServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/resource/sql/BaseTbResourceServiceTest.java @@ -38,6 +38,7 @@ import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.controller.AbstractControllerTest; import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.service.resource.TbResourceService; @@ -115,7 +116,9 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { private TenantId tenantId; @Autowired - private TbResourceService resourceService; + private ResourceService resourceService; + @Autowired + private TbResourceService tbResourceService; private Tenant savedTenant; private User tenantAdmin; @@ -207,7 +210,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setFileName(filename); byte[] b = new byte[]{1, 2, 3, 4}; resource.setBase64Data(Base64.getEncoder().encodeToString(b)); - return resourceService.save(resource); + return tbResourceService.save(resource); } @Test @@ -219,7 +222,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setFileName(DEFAULT_FILE_NAME); resource.setBase64Data(TEST_BASE64_DATA); - TbResource savedResource = resourceService.save(resource); + TbResource savedResource = tbResourceService.save(resource); Assert.assertNotNull(savedResource); Assert.assertNotNull(savedResource.getId()); @@ -233,12 +236,12 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { savedResource.setBase64Data(null); savedResource.setData(null); - resourceService.save(savedResource); + tbResourceService.save(savedResource); TbResource foundResource = resourceService.findResourceById(tenantId, savedResource.getId()); assertEquals(foundResource.getTitle(), savedResource.getTitle()); assertArrayEquals(foundResource.getData(), TEST_DATA); - resourceService.delete(savedResource, null); + tbResourceService.delete(savedResource, null); } @Test @@ -249,7 +252,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setFileName("test_model.xml"); resource.setBase64Data(Base64.getEncoder().encodeToString(LWM2M_TEST_MODEL.getBytes())); - TbResource savedResource = resourceService.save(resource); + TbResource savedResource = tbResourceService.save(resource); Assert.assertNotNull(savedResource); Assert.assertNotNull(savedResource.getId()); @@ -259,7 +262,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { assertEquals("0_1.0", savedResource.getResourceKey()); assertArrayEquals(savedResource.getData(), LWM2M_TEST_MODEL.getBytes()); - resourceService.delete(savedResource, null); + tbResourceService.delete(savedResource, null); } @Test @@ -269,11 +272,11 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setTitle("My resource"); resource.setFileName(DEFAULT_FILE_NAME); resource.setBase64Data(TEST_BASE64_DATA); - TbResource savedResource = resourceService.save(resource); + TbResource savedResource = tbResourceService.save(resource); assertEquals(TenantId.SYS_TENANT_ID, savedResource.getTenantId()); - resourceService.delete(savedResource, null); + tbResourceService.delete(savedResource, null); } @Test @@ -285,7 +288,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setFileName(DEFAULT_FILE_NAME); resource.setBase64Data(TEST_BASE64_DATA); - TbResource savedResource = resourceService.save(resource); + TbResource savedResource = tbResourceService.save(resource); TbResource resource2 = new TbResource(); resource.setTenantId(tenantId); @@ -296,10 +299,10 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { try { Assertions.assertThrows(DataValidationException.class, () -> { - resourceService.save(resource2); + tbResourceService.save(resource2); }); } finally { - resourceService.delete(savedResource, null); + tbResourceService.delete(savedResource, null); } } @@ -311,7 +314,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setFileName(DEFAULT_FILE_NAME); resource.setBase64Data(TEST_BASE64_DATA); Assertions.assertThrows(DataValidationException.class, () -> { - resourceService.save(resource); + tbResourceService.save(resource); }); } @@ -324,7 +327,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setFileName(DEFAULT_FILE_NAME); resource.setBase64Data(TEST_BASE64_DATA); Assertions.assertThrows(DataValidationException.class, () -> { - resourceService.save(resource); + tbResourceService.save(resource); }); } @@ -337,7 +340,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setData(LWM2M_TEST_MODEL_WITH_XXE.getBytes()); DataValidationException thrown = assertThrows(DataValidationException.class, () -> { - resourceService.save(resource); + tbResourceService.save(resource); }); assertEquals("Failed to parse file xxe_test_model.xml", thrown.getMessage()); } @@ -350,12 +353,12 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setTitle("My resource"); resource.setFileName(DEFAULT_FILE_NAME); resource.setBase64Data(TEST_BASE64_DATA); - TbResource savedResource = resourceService.save(resource); + TbResource savedResource = tbResourceService.save(resource); TbResource foundResource = resourceService.findResourceById(tenantId, savedResource.getId()); Assert.assertNotNull(foundResource); assertEquals(savedResource, foundResource); - resourceService.delete(savedResource, null); + tbResourceService.delete(savedResource, null); } @Test @@ -366,12 +369,12 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setTitle("My resource"); resource.setFileName(DEFAULT_FILE_NAME); resource.setBase64Data(TEST_BASE64_DATA); - TbResource savedResource = resourceService.save(resource); + TbResource savedResource = tbResourceService.save(resource); - TbResource foundResource = resourceService.getResource(tenantId, savedResource.getResourceType(), savedResource.getResourceKey()); + TbResource foundResource = resourceService.findResourceByTenantIdAndKey(tenantId, savedResource.getResourceType(), savedResource.getResourceKey()); Assert.assertNotNull(foundResource); assertEquals(savedResource, foundResource); - resourceService.delete(savedResource, null); + tbResourceService.delete(savedResource, null); } @Test @@ -381,11 +384,11 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setTitle("My resource"); resource.setFileName(DEFAULT_FILE_NAME); resource.setBase64Data(TEST_BASE64_DATA); - TbResource savedResource = resourceService.save(resource); + TbResource savedResource = tbResourceService.save(resource); TbResource foundResource = resourceService.findResourceById(tenantId, savedResource.getId()); Assert.assertNotNull(foundResource); - resourceService.delete(savedResource, null); + tbResourceService.delete(savedResource, null); foundResource = resourceService.findResourceById(tenantId, savedResource.getId()); Assert.assertNull(foundResource); } @@ -407,7 +410,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JKS); resource.setFileName(i + DEFAULT_FILE_NAME); resource.setBase64Data(TEST_BASE64_DATA); - resources.add(new TbResourceInfo(resourceService.save(resource))); + resources.add(new TbResourceInfo(tbResourceService.save(resource))); } List loadedResources = new ArrayList<>(); @@ -461,7 +464,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JKS); resource.setFileName(i + DEFAULT_FILE_NAME); resource.setBase64Data(TEST_BASE64_DATA); - TbResourceInfo tbResourceInfo = new TbResourceInfo(resourceService.save(resource)); + TbResourceInfo tbResourceInfo = new TbResourceInfo(tbResourceService.save(resource)); if (i >= 50) { resources.add(tbResourceInfo); } @@ -474,7 +477,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JKS); resource.setFileName(i + DEFAULT_FILE_NAME); resource.setBase64Data(TEST_BASE64_DATA); - resources.add(new TbResourceInfo(resourceService.save(resource))); + resources.add(new TbResourceInfo(tbResourceService.save(resource))); } List loadedResources = new ArrayList<>(); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java index 6f0e362209..c1ce27c266 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java @@ -32,12 +32,14 @@ public interface ResourceService extends EntityDaoService { TbResource saveResource(TbResource resource); - TbResource getResource(TenantId tenantId, ResourceType resourceType, String resourceId); + TbResource findResourceByTenantIdAndKey(TenantId tenantId, ResourceType resourceType, String resourceId); TbResource findResourceById(TenantId tenantId, TbResourceId resourceId); TbResourceInfo findResourceInfoById(TenantId tenantId, TbResourceId resourceId); + TbResourceInfo findResourceInfoByTenantIdAndKey(TenantId tenantId, ResourceType resourceType, String resourceKey); + ListenableFuture findResourceInfoByIdAsync(TenantId tenantId, TbResourceId resourceId); PageData findAllTenantResourcesByTenantId(TbResourceInfoFilter filter, PageLink pageLink); @@ -53,4 +55,5 @@ public interface ResourceService extends EntityDaoService { void deleteResourcesByTenantId(TenantId tenantId); long sumDataSizeByTenantId(TenantId tenantId); + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/ResourceType.java b/common/data/src/main/java/org/thingsboard/server/common/data/ResourceType.java index 3c2b9a4bc0..9ea39c43fd 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/ResourceType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ResourceType.java @@ -16,20 +16,21 @@ package org.thingsboard.server.common.data; import lombok.Getter; +import lombok.RequiredArgsConstructor; +@RequiredArgsConstructor public enum ResourceType { - LWM2M_MODEL("application/xml", false), - JKS("application/x-java-keystore", false), - PKCS_12("application/x-pkcs12", false), - JS_MODULE("application/javascript", true); + LWM2M_MODEL("application/xml", false, false), + JKS("application/x-java-keystore", false, false), + PKCS_12("application/x-pkcs12", false, false), + JS_MODULE("application/javascript", true, true), + IMAGE(null, true, false); @Getter - private final String mediaType; + private final String defaultMediaType; @Getter private final boolean customerAccess; + @Getter + private final boolean updatable; - ResourceType(String mediaType, boolean customerAccess) { - this.mediaType = mediaType; - this.customerAccess = customerAccess; - } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/TbResource.java b/common/data/src/main/java/org/thingsboard/server/common/data/TbResource.java index d003328418..7c44943deb 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/TbResource.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/TbResource.java @@ -31,9 +31,9 @@ public class TbResource extends TbResourceInfo { private static final long serialVersionUID = 7379609705527272306L; + @ApiModelProperty(position = 12, value = "Resource data.", example = "77u/PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCEtLQpGSUxFIElORk9STUFUSU9OCgpPTUEgUGVybWFuZW50IERvY3VtZW50CiAgIEZpbGU6IE9NQS1TVVAtTHdNMk1fQmluYXJ5QXBwRGF0YUNvbnRhaW5lci1WMV8wXzEtMjAxOTAyMjEtQQogICBUeXBlOiB4bWwKClB1YmxpYyBSZWFjaGFibGUgSW5mb3JtYXRpb24KICAgUGF0aDogaHR0cDovL3d3dy5vcGVubW9iaWxlYWxsaWFuY2Uub3JnL3RlY2gvcHJvZmlsZXMKICAgTmFtZTogTHdNMk1fQmluYXJ5QXBwRGF0YUNvbnRhaW5lci12MV8wXzEueG1sCgpOT1JNQVRJVkUgSU5GT1JNQVRJT04KCiAgSW5mb3JtYXRpb24gYWJvdXQgdGhpcyBmaWxlIGNhbiBiZSBmb3VuZCBpbiB0aGUgbGF0ZXN0IHJldmlzaW9uIG9mCgogIE9NQS1UUy1MV00yTV9CaW5hcnlBcHBEYXRhQ29udGFpbmVyLVYxXzBfMQoKICBUaGlzIGlzIGF2YWlsYWJsZSBhdCBodHRwOi8vd3d3Lm9wZW5tb2JpbGVhbGxpYW5jZS5vcmcvCgogIFNlbmQgY29tbWVudHMgdG8gaHR0cHM6Ly9naXRodWIuY29tL09wZW5Nb2JpbGVBbGxpYW5jZS9PTUFfTHdNMk1fZm9yX0RldmVsb3BlcnMvaXNzdWVzCgpDSEFOR0UgSElTVE9SWQoKMTUwNjIwMTggU3RhdHVzIGNoYW5nZWQgdG8gQXBwcm92ZWQgYnkgRE0sIERvYyBSZWYgIyBPTUEtRE0mU0UtMjAxOC0wMDYxLUlOUF9MV00yTV9BUFBEQVRBX1YxXzBfRVJQX2Zvcl9maW5hbF9BcHByb3ZhbAoyMTAyMjAxOSBTdGF0dXMgY2hhbmdlZCB0byBBcHByb3ZlZCBieSBJUFNPLCBEb2MgUmVmICMgT01BLUlQU08tMjAxOS0wMDI1LUlOUF9Md00yTV9PYmplY3RfQXBwX0RhdGFfQ29udGFpbmVyXzFfMF8xX2Zvcl9GaW5hbF9BcHByb3ZhbAoKTEVHQUwgRElTQ0xBSU1FUgoKQ29weXJpZ2h0IDIwMTkgT3BlbiBNb2JpbGUgQWxsaWFuY2UuCgpSZWRpc3RyaWJ1dGlvbiBhbmQgdXNlIGluIHNvdXJjZSBhbmQgYmluYXJ5IGZvcm1zLCB3aXRoIG9yIHdpdGhvdXQKbW9kaWZpY2F0aW9uLCBhcmUgcGVybWl0dGVkIHByb3ZpZGVkIHRoYXQgdGhlIGZvbGxvd2luZyBjb25kaXRpb25zCmFyZSBtZXQ6CgoxLiBSZWRpc3RyaWJ1dGlvbnMgb2Ygc291cmNlIGNvZGUgbXVzdCByZXRhaW4gdGhlIGFib3ZlIGNvcHlyaWdodApub3RpY2UsIHRoaXMgbGlzdCBvZiBjb25kaXRpb25zIGFuZCB0aGUgZm9sbG93aW5nIGRpc2NsYWltZXIuCjIuIFJlZGlzdHJpYnV0aW9ucyBpbiBiaW5hcnkgZm9ybSBtdXN0IHJlcHJvZHVjZSB0aGUgYWJvdmUgY29weXJpZ2h0Cm5vdGljZSwgdGhpcyBsaXN0IG9mIGNvbmRpdGlvbnMgYW5kIHRoZSBmb2xsb3dpbmcgZGlzY2xhaW1lciBpbiB0aGUKZG9jdW1lbnRhdGlvbiBhbmQvb3Igb3RoZXIgbWF0ZXJpYWxzIHByb3ZpZGVkIHdpdGggdGhlIGRpc3RyaWJ1dGlvbi4KMy4gTmVpdGhlciB0aGUgbmFtZSBvZiB0aGUgY29weXJpZ2h0IGhvbGRlciBub3IgdGhlIG5hbWVzIG9mIGl0cwpjb250cmlidXRvcnMgbWF5IGJlIHVzZWQgdG8gZW5kb3JzZSBvciBwcm9tb3RlIHByb2R1Y3RzIGRlcml2ZWQKZnJvbSB0aGlzIHNvZnR3YXJlIHdpdGhvdXQgc3BlY2lmaWMgcHJpb3Igd3JpdHRlbiBwZXJtaXNzaW9uLgoKVEhJUyBTT0ZUV0FSRSBJUyBQUk9WSURFRCBCWSBUSEUgQ09QWVJJR0hUIEhPTERFUlMgQU5EIENPTlRSSUJVVE9SUwoiQVMgSVMiIEFORCBBTlkgRVhQUkVTUyBPUiBJTVBMSUVEIFdBUlJBTlRJRVMsIElOQ0xVRElORywgQlVUIE5PVApMSU1JVEVEIFRPLCBUSEUgSU1QTElFRCBXQVJSQU5USUVTIE9GIE1FUkNIQU5UQUJJTElUWSBBTkQgRklUTkVTUwpGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UgQVJFIERJU0NMQUlNRUQuIElOIE5PIEVWRU5UIFNIQUxMIFRIRQpDT1BZUklHSFQgSE9MREVSIE9SIENPTlRSSUJVVE9SUyBCRSBMSUFCTEUgRk9SIEFOWSBESVJFQ1QsIElORElSRUNULApJTkNJREVOVEFMLCBTUEVDSUFMLCBFWEVNUExBUlksIE9SIENPTlNFUVVFTlRJQUwgREFNQUdFUyAoSU5DTFVESU5HLApCVVQgTk9UIExJTUlURUQgVE8sIFBST0NVUkVNRU5UIE9GIFNVQlNUSVRVVEUgR09PRFMgT1IgU0VSVklDRVM7CkxPU1MgT0YgVVNFLCBEQVRBLCBPUiBQUk9GSVRTOyBPUiBCVVNJTkVTUyBJTlRFUlJVUFRJT04pIEhPV0VWRVIKQ0FVU0VEIEFORCBPTiBBTlkgVEhFT1JZIE9GIExJQUJJTElUWSwgV0hFVEhFUiBJTiBDT05UUkFDVCwgU1RSSUNUCkxJQUJJTElUWSwgT1IgVE9SVCAoSU5DTFVESU5HIE5FR0xJR0VOQ0UgT1IgT1RIRVJXSVNFKSBBUklTSU5HIElOCkFOWSBXQVkgT1VUIE9GIFRIRSBVU0UgT0YgVEhJUyBTT0ZUV0FSRSwgRVZFTiBJRiBBRFZJU0VEIE9GIFRIRQpQT1NTSUJJTElUWSBPRiBTVUNIIERBTUFHRS4KClRoZSBhYm92ZSBsaWNlbnNlIGlzIHVzZWQgYXMgYSBsaWNlbnNlIHVuZGVyIGNvcHlyaWdodCBvbmx5LiBQbGVhc2UKcmVmZXJlbmNlIHRoZSBPTUEgSVBSIFBvbGljeSBmb3IgcGF0ZW50IGxpY2Vuc2luZyB0ZXJtczoKaHR0cHM6Ly93d3cub21hc3BlY3dvcmtzLm9yZy9hYm91dC9pbnRlbGxlY3R1YWwtcHJvcGVydHktcmlnaHRzLwoKLS0+CjxMV00yTSB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4c2k6bm9OYW1lc3BhY2VTY2hlbWFMb2NhdGlvbj0iaHR0cDovL29wZW5tb2JpbGVhbGxpYW5jZS5vcmcvdGVjaC9wcm9maWxlcy9MV00yTS54c2QiPgoJPE9iamVjdCBPYmplY3RUeXBlPSJNT0RlZmluaXRpb24iPgoJCTxOYW1lPkJpbmFyeUFwcERhdGFDb250YWluZXI8L05hbWU+CgkJPERlc2NyaXB0aW9uMT48IVtDREFUQVtUaGlzIEx3TTJNIE9iamVjdHMgcHJvdmlkZXMgdGhlIGFwcGxpY2F0aW9uIHNlcnZpY2UgZGF0YSByZWxhdGVkIHRvIGEgTHdNMk0gU2VydmVyLCBlZy4gV2F0ZXIgbWV0ZXIgZGF0YS4gClRoZXJlIGFyZSBzZXZlcmFsIG1ldGhvZHMgdG8gY3JlYXRlIGluc3RhbmNlIHRvIGluZGljYXRlIHRoZSBtZXNzYWdlIGRpcmVjdGlvbiBiYXNlZCBvbiB0aGUgbmVnb3RpYXRpb24gYmV0d2VlbiBBcHBsaWNhdGlvbiBhbmQgTHdNMk0uIFRoZSBDbGllbnQgYW5kIFNlcnZlciBzaG91bGQgbmVnb3RpYXRlIHRoZSBpbnN0YW5jZShzKSB1c2VkIHRvIGV4Y2hhbmdlIHRoZSBkYXRhLiBGb3IgZXhhbXBsZToKIC0gVXNpbmcgYSBzaW5nbGUgaW5zdGFuY2UgZm9yIGJvdGggZGlyZWN0aW9ucyBjb21tdW5pY2F0aW9uLCBmcm9tIENsaWVudCB0byBTZXJ2ZXIgYW5kIGZyb20gU2VydmVyIHRvIENsaWVudC4KIC0gVXNpbmcgYW4gaW5zdGFuY2UgZm9yIGNvbW11bmljYXRpb24gZnJvbSBDbGllbnQgdG8gU2VydmVyIGFuZCBhbm90aGVyIG9uZSBmb3IgY29tbXVuaWNhdGlvbiBmcm9tIFNlcnZlciB0byBDbGllbnQKIC0gVXNpbmcgc2V2ZXJhbCBpbnN0YW5jZXMKXV0+PC9EZXNjcmlwdGlvbjE+CgkJPE9iamVjdElEPjE5PC9PYmplY3RJRD4KCQk8T2JqZWN0VVJOPnVybjpvbWE6bHdtMm06b21hOjE5PC9PYmplY3RVUk4+CgkJPExXTTJNVmVyc2lvbj4xLjA8L0xXTTJNVmVyc2lvbj4KCQk8T2JqZWN0VmVyc2lvbj4xLjA8L09iamVjdFZlcnNpb24+CgkJPE11bHRpcGxlSW5zdGFuY2VzPk11bHRpcGxlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQk8TWFuZGF0b3J5Pk9wdGlvbmFsPC9NYW5kYXRvcnk+CgkJPFJlc291cmNlcz4KCQkJPEl0ZW0gSUQ9IjAiPjxOYW1lPkRhdGE8L05hbWU+CgkJCQk8T3BlcmF0aW9ucz5SVzwvT3BlcmF0aW9ucz4KCQkJCTxNdWx0aXBsZUluc3RhbmNlcz5NdWx0aXBsZTwvTXVsdGlwbGVJbnN0YW5jZXM+CgkJCQk8TWFuZGF0b3J5Pk1hbmRhdG9yeTwvTWFuZGF0b3J5PgoJCQkJPFR5cGU+T3BhcXVlPC9UeXBlPgoJCQkJPFJhbmdlRW51bWVyYXRpb24gLz4KCQkJCTxVbml0cyAvPgoJCQkJPERlc2NyaXB0aW9uPjwhW0NEQVRBW0luZGljYXRlcyB0aGUgYXBwbGljYXRpb24gZGF0YSBjb250ZW50Ll1dPjwvRGVzY3JpcHRpb24+CgkJCTwvSXRlbT4KCQkJPEl0ZW0gSUQ9IjEiPjxOYW1lPkRhdGEgUHJpb3JpdHk8L05hbWU+CgkJCQk8T3BlcmF0aW9ucz5SVzwvT3BlcmF0aW9ucz4KCQkJCTxNdWx0aXBsZUluc3RhbmNlcz5TaW5nbGU8L011bHRpcGxlSW5zdGFuY2VzPgoJCQkJPE1hbmRhdG9yeT5PcHRpb25hbDwvTWFuZGF0b3J5PgoJCQkJPFR5cGU+SW50ZWdlcjwvVHlwZT4KCQkJCTxSYW5nZUVudW1lcmF0aW9uPjEgYnl0ZXM8L1JhbmdlRW51bWVyYXRpb24+CgkJCQk8VW5pdHMgLz4KCQkJCTxEZXNjcmlwdGlvbj48IVtDREFUQVtJbmRpY2F0ZXMgdGhlIEFwcGxpY2F0aW9uIGRhdGEgcHJpb3JpdHk6CjA6SW1tZWRpYXRlCjE6QmVzdEVmZm9ydAoyOkxhdGVzdAozLTEwMDogUmVzZXJ2ZWQgZm9yIGZ1dHVyZSB1c2UuCjEwMS0yNTQ6IFByb3ByaWV0YXJ5IG1vZGUuXV0+PC9EZXNjcmlwdGlvbj4KCQkJPC9JdGVtPgoJCQk8SXRlbSBJRD0iMiI+PE5hbWU+RGF0YSBDcmVhdGlvbiBUaW1lPC9OYW1lPgoJCQkJPE9wZXJhdGlvbnM+Ulc8L09wZXJhdGlvbnM+CgkJCQk8TXVsdGlwbGVJbnN0YW5jZXM+U2luZ2xlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQkJCTxNYW5kYXRvcnk+T3B0aW9uYWw8L01hbmRhdG9yeT4KCQkJCTxUeXBlPlRpbWU8L1R5cGU+CgkJCQk8UmFuZ2VFbnVtZXJhdGlvbiAvPgoJCQkJPFVuaXRzIC8+CgkJCQk8RGVzY3JpcHRpb24+PCFbQ0RBVEFbSW5kaWNhdGVzIHRoZSBEYXRhIGluc3RhbmNlIGNyZWF0aW9uIHRpbWVzdGFtcC5dXT48L0Rlc2NyaXB0aW9uPgoJCQk8L0l0ZW0+CgkJCTxJdGVtIElEPSIzIj48TmFtZT5EYXRhIERlc2NyaXB0aW9uPC9OYW1lPgoJCQkJPE9wZXJhdGlvbnM+Ulc8L09wZXJhdGlvbnM+CgkJCQk8TXVsdGlwbGVJbnN0YW5jZXM+U2luZ2xlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQkJCTxNYW5kYXRvcnk+T3B0aW9uYWw8L01hbmRhdG9yeT4KCQkJCTxUeXBlPlN0cmluZzwvVHlwZT4KCQkJCTxSYW5nZUVudW1lcmF0aW9uPjMyIGJ5dGVzPC9SYW5nZUVudW1lcmF0aW9uPgoJCQkJPFVuaXRzIC8+CgkJCQk8RGVzY3JpcHRpb24+PCFbQ0RBVEFbSW5kaWNhdGVzIHRoZSBkYXRhIGRlc2NyaXB0aW9uLgplLmcuICJtZXRlciByZWFkaW5nIi5dXT48L0Rlc2NyaXB0aW9uPgoJCQk8L0l0ZW0+CgkJCTxJdGVtIElEPSI0Ij48TmFtZT5EYXRhIEZvcm1hdDwvTmFtZT4KCQkJCTxPcGVyYXRpb25zPlJXPC9PcGVyYXRpb25zPgoJCQkJPE11bHRpcGxlSW5zdGFuY2VzPlNpbmdsZTwvTXVsdGlwbGVJbnN0YW5jZXM+CgkJCQk8TWFuZGF0b3J5Pk9wdGlvbmFsPC9NYW5kYXRvcnk+CgkJCQk8VHlwZT5TdHJpbmc8L1R5cGU+CgkJCQk8UmFuZ2VFbnVtZXJhdGlvbj4zMiBieXRlczwvUmFuZ2VFbnVtZXJhdGlvbj4KCQkJCTxVbml0cyAvPgoJCQkJPERlc2NyaXB0aW9uPjwhW0NEQVRBW0luZGljYXRlcyB0aGUgZm9ybWF0IG9mIHRoZSBBcHBsaWNhdGlvbiBEYXRhLgplLmcuIFlHLU1ldGVyLVdhdGVyLVJlYWRpbmcKVVRGOC1zdHJpbmcKXV0+PC9EZXNjcmlwdGlvbj4KCQkJPC9JdGVtPgoJCQk8SXRlbSBJRD0iNSI+PE5hbWU+QXBwIElEPC9OYW1lPgoJCQkJPE9wZXJhdGlvbnM+Ulc8L09wZXJhdGlvbnM+CgkJCQk8TXVsdGlwbGVJbnN0YW5jZXM+U2luZ2xlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQkJCTxNYW5kYXRvcnk+T3B0aW9uYWw8L01hbmRhdG9yeT4KCQkJCTxUeXBlPkludGVnZXI8L1R5cGU+CgkJCQk8UmFuZ2VFbnVtZXJhdGlvbj4yIGJ5dGVzPC9SYW5nZUVudW1lcmF0aW9uPgoJCQkJPFVuaXRzIC8+CgkJCQk8RGVzY3JpcHRpb24+PCFbQ0RBVEFbSW5kaWNhdGVzIHRoZSBkZXN0aW5hdGlvbiBBcHBsaWNhdGlvbiBJRC5dXT48L0Rlc2NyaXB0aW9uPgoJCQk8L0l0ZW0+PC9SZXNvdXJjZXM+CgkJPERlc2NyaXB0aW9uMj48IVtDREFUQVtdXT48L0Rlc2NyaXB0aW9uMj4KCTwvT2JqZWN0Pgo8L0xXTTJNPgo=", accessMode = ApiModelProperty.AccessMode.READ_WRITE) private String base64Data; - @ApiModelProperty(position = 9, value = "Resource data.", example = "77u/PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCEtLQpGSUxFIElORk9STUFUSU9OCgpPTUEgUGVybWFuZW50IERvY3VtZW50CiAgIEZpbGU6IE9NQS1TVVAtTHdNMk1fQmluYXJ5QXBwRGF0YUNvbnRhaW5lci1WMV8wXzEtMjAxOTAyMjEtQQogICBUeXBlOiB4bWwKClB1YmxpYyBSZWFjaGFibGUgSW5mb3JtYXRpb24KICAgUGF0aDogaHR0cDovL3d3dy5vcGVubW9iaWxlYWxsaWFuY2Uub3JnL3RlY2gvcHJvZmlsZXMKICAgTmFtZTogTHdNMk1fQmluYXJ5QXBwRGF0YUNvbnRhaW5lci12MV8wXzEueG1sCgpOT1JNQVRJVkUgSU5GT1JNQVRJT04KCiAgSW5mb3JtYXRpb24gYWJvdXQgdGhpcyBmaWxlIGNhbiBiZSBmb3VuZCBpbiB0aGUgbGF0ZXN0IHJldmlzaW9uIG9mCgogIE9NQS1UUy1MV00yTV9CaW5hcnlBcHBEYXRhQ29udGFpbmVyLVYxXzBfMQoKICBUaGlzIGlzIGF2YWlsYWJsZSBhdCBodHRwOi8vd3d3Lm9wZW5tb2JpbGVhbGxpYW5jZS5vcmcvCgogIFNlbmQgY29tbWVudHMgdG8gaHR0cHM6Ly9naXRodWIuY29tL09wZW5Nb2JpbGVBbGxpYW5jZS9PTUFfTHdNMk1fZm9yX0RldmVsb3BlcnMvaXNzdWVzCgpDSEFOR0UgSElTVE9SWQoKMTUwNjIwMTggU3RhdHVzIGNoYW5nZWQgdG8gQXBwcm92ZWQgYnkgRE0sIERvYyBSZWYgIyBPTUEtRE0mU0UtMjAxOC0wMDYxLUlOUF9MV00yTV9BUFBEQVRBX1YxXzBfRVJQX2Zvcl9maW5hbF9BcHByb3ZhbAoyMTAyMjAxOSBTdGF0dXMgY2hhbmdlZCB0byBBcHByb3ZlZCBieSBJUFNPLCBEb2MgUmVmICMgT01BLUlQU08tMjAxOS0wMDI1LUlOUF9Md00yTV9PYmplY3RfQXBwX0RhdGFfQ29udGFpbmVyXzFfMF8xX2Zvcl9GaW5hbF9BcHByb3ZhbAoKTEVHQUwgRElTQ0xBSU1FUgoKQ29weXJpZ2h0IDIwMTkgT3BlbiBNb2JpbGUgQWxsaWFuY2UuCgpSZWRpc3RyaWJ1dGlvbiBhbmQgdXNlIGluIHNvdXJjZSBhbmQgYmluYXJ5IGZvcm1zLCB3aXRoIG9yIHdpdGhvdXQKbW9kaWZpY2F0aW9uLCBhcmUgcGVybWl0dGVkIHByb3ZpZGVkIHRoYXQgdGhlIGZvbGxvd2luZyBjb25kaXRpb25zCmFyZSBtZXQ6CgoxLiBSZWRpc3RyaWJ1dGlvbnMgb2Ygc291cmNlIGNvZGUgbXVzdCByZXRhaW4gdGhlIGFib3ZlIGNvcHlyaWdodApub3RpY2UsIHRoaXMgbGlzdCBvZiBjb25kaXRpb25zIGFuZCB0aGUgZm9sbG93aW5nIGRpc2NsYWltZXIuCjIuIFJlZGlzdHJpYnV0aW9ucyBpbiBiaW5hcnkgZm9ybSBtdXN0IHJlcHJvZHVjZSB0aGUgYWJvdmUgY29weXJpZ2h0Cm5vdGljZSwgdGhpcyBsaXN0IG9mIGNvbmRpdGlvbnMgYW5kIHRoZSBmb2xsb3dpbmcgZGlzY2xhaW1lciBpbiB0aGUKZG9jdW1lbnRhdGlvbiBhbmQvb3Igb3RoZXIgbWF0ZXJpYWxzIHByb3ZpZGVkIHdpdGggdGhlIGRpc3RyaWJ1dGlvbi4KMy4gTmVpdGhlciB0aGUgbmFtZSBvZiB0aGUgY29weXJpZ2h0IGhvbGRlciBub3IgdGhlIG5hbWVzIG9mIGl0cwpjb250cmlidXRvcnMgbWF5IGJlIHVzZWQgdG8gZW5kb3JzZSBvciBwcm9tb3RlIHByb2R1Y3RzIGRlcml2ZWQKZnJvbSB0aGlzIHNvZnR3YXJlIHdpdGhvdXQgc3BlY2lmaWMgcHJpb3Igd3JpdHRlbiBwZXJtaXNzaW9uLgoKVEhJUyBTT0ZUV0FSRSBJUyBQUk9WSURFRCBCWSBUSEUgQ09QWVJJR0hUIEhPTERFUlMgQU5EIENPTlRSSUJVVE9SUwoiQVMgSVMiIEFORCBBTlkgRVhQUkVTUyBPUiBJTVBMSUVEIFdBUlJBTlRJRVMsIElOQ0xVRElORywgQlVUIE5PVApMSU1JVEVEIFRPLCBUSEUgSU1QTElFRCBXQVJSQU5USUVTIE9GIE1FUkNIQU5UQUJJTElUWSBBTkQgRklUTkVTUwpGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UgQVJFIERJU0NMQUlNRUQuIElOIE5PIEVWRU5UIFNIQUxMIFRIRQpDT1BZUklHSFQgSE9MREVSIE9SIENPTlRSSUJVVE9SUyBCRSBMSUFCTEUgRk9SIEFOWSBESVJFQ1QsIElORElSRUNULApJTkNJREVOVEFMLCBTUEVDSUFMLCBFWEVNUExBUlksIE9SIENPTlNFUVVFTlRJQUwgREFNQUdFUyAoSU5DTFVESU5HLApCVVQgTk9UIExJTUlURUQgVE8sIFBST0NVUkVNRU5UIE9GIFNVQlNUSVRVVEUgR09PRFMgT1IgU0VSVklDRVM7CkxPU1MgT0YgVVNFLCBEQVRBLCBPUiBQUk9GSVRTOyBPUiBCVVNJTkVTUyBJTlRFUlJVUFRJT04pIEhPV0VWRVIKQ0FVU0VEIEFORCBPTiBBTlkgVEhFT1JZIE9GIExJQUJJTElUWSwgV0hFVEhFUiBJTiBDT05UUkFDVCwgU1RSSUNUCkxJQUJJTElUWSwgT1IgVE9SVCAoSU5DTFVESU5HIE5FR0xJR0VOQ0UgT1IgT1RIRVJXSVNFKSBBUklTSU5HIElOCkFOWSBXQVkgT1VUIE9GIFRIRSBVU0UgT0YgVEhJUyBTT0ZUV0FSRSwgRVZFTiBJRiBBRFZJU0VEIE9GIFRIRQpQT1NTSUJJTElUWSBPRiBTVUNIIERBTUFHRS4KClRoZSBhYm92ZSBsaWNlbnNlIGlzIHVzZWQgYXMgYSBsaWNlbnNlIHVuZGVyIGNvcHlyaWdodCBvbmx5LiBQbGVhc2UKcmVmZXJlbmNlIHRoZSBPTUEgSVBSIFBvbGljeSBmb3IgcGF0ZW50IGxpY2Vuc2luZyB0ZXJtczoKaHR0cHM6Ly93d3cub21hc3BlY3dvcmtzLm9yZy9hYm91dC9pbnRlbGxlY3R1YWwtcHJvcGVydHktcmlnaHRzLwoKLS0+CjxMV00yTSB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4c2k6bm9OYW1lc3BhY2VTY2hlbWFMb2NhdGlvbj0iaHR0cDovL29wZW5tb2JpbGVhbGxpYW5jZS5vcmcvdGVjaC9wcm9maWxlcy9MV00yTS54c2QiPgoJPE9iamVjdCBPYmplY3RUeXBlPSJNT0RlZmluaXRpb24iPgoJCTxOYW1lPkJpbmFyeUFwcERhdGFDb250YWluZXI8L05hbWU+CgkJPERlc2NyaXB0aW9uMT48IVtDREFUQVtUaGlzIEx3TTJNIE9iamVjdHMgcHJvdmlkZXMgdGhlIGFwcGxpY2F0aW9uIHNlcnZpY2UgZGF0YSByZWxhdGVkIHRvIGEgTHdNMk0gU2VydmVyLCBlZy4gV2F0ZXIgbWV0ZXIgZGF0YS4gClRoZXJlIGFyZSBzZXZlcmFsIG1ldGhvZHMgdG8gY3JlYXRlIGluc3RhbmNlIHRvIGluZGljYXRlIHRoZSBtZXNzYWdlIGRpcmVjdGlvbiBiYXNlZCBvbiB0aGUgbmVnb3RpYXRpb24gYmV0d2VlbiBBcHBsaWNhdGlvbiBhbmQgTHdNMk0uIFRoZSBDbGllbnQgYW5kIFNlcnZlciBzaG91bGQgbmVnb3RpYXRlIHRoZSBpbnN0YW5jZShzKSB1c2VkIHRvIGV4Y2hhbmdlIHRoZSBkYXRhLiBGb3IgZXhhbXBsZToKIC0gVXNpbmcgYSBzaW5nbGUgaW5zdGFuY2UgZm9yIGJvdGggZGlyZWN0aW9ucyBjb21tdW5pY2F0aW9uLCBmcm9tIENsaWVudCB0byBTZXJ2ZXIgYW5kIGZyb20gU2VydmVyIHRvIENsaWVudC4KIC0gVXNpbmcgYW4gaW5zdGFuY2UgZm9yIGNvbW11bmljYXRpb24gZnJvbSBDbGllbnQgdG8gU2VydmVyIGFuZCBhbm90aGVyIG9uZSBmb3IgY29tbXVuaWNhdGlvbiBmcm9tIFNlcnZlciB0byBDbGllbnQKIC0gVXNpbmcgc2V2ZXJhbCBpbnN0YW5jZXMKXV0+PC9EZXNjcmlwdGlvbjE+CgkJPE9iamVjdElEPjE5PC9PYmplY3RJRD4KCQk8T2JqZWN0VVJOPnVybjpvbWE6bHdtMm06b21hOjE5PC9PYmplY3RVUk4+CgkJPExXTTJNVmVyc2lvbj4xLjA8L0xXTTJNVmVyc2lvbj4KCQk8T2JqZWN0VmVyc2lvbj4xLjA8L09iamVjdFZlcnNpb24+CgkJPE11bHRpcGxlSW5zdGFuY2VzPk11bHRpcGxlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQk8TWFuZGF0b3J5Pk9wdGlvbmFsPC9NYW5kYXRvcnk+CgkJPFJlc291cmNlcz4KCQkJPEl0ZW0gSUQ9IjAiPjxOYW1lPkRhdGE8L05hbWU+CgkJCQk8T3BlcmF0aW9ucz5SVzwvT3BlcmF0aW9ucz4KCQkJCTxNdWx0aXBsZUluc3RhbmNlcz5NdWx0aXBsZTwvTXVsdGlwbGVJbnN0YW5jZXM+CgkJCQk8TWFuZGF0b3J5Pk1hbmRhdG9yeTwvTWFuZGF0b3J5PgoJCQkJPFR5cGU+T3BhcXVlPC9UeXBlPgoJCQkJPFJhbmdlRW51bWVyYXRpb24gLz4KCQkJCTxVbml0cyAvPgoJCQkJPERlc2NyaXB0aW9uPjwhW0NEQVRBW0luZGljYXRlcyB0aGUgYXBwbGljYXRpb24gZGF0YSBjb250ZW50Ll1dPjwvRGVzY3JpcHRpb24+CgkJCTwvSXRlbT4KCQkJPEl0ZW0gSUQ9IjEiPjxOYW1lPkRhdGEgUHJpb3JpdHk8L05hbWU+CgkJCQk8T3BlcmF0aW9ucz5SVzwvT3BlcmF0aW9ucz4KCQkJCTxNdWx0aXBsZUluc3RhbmNlcz5TaW5nbGU8L011bHRpcGxlSW5zdGFuY2VzPgoJCQkJPE1hbmRhdG9yeT5PcHRpb25hbDwvTWFuZGF0b3J5PgoJCQkJPFR5cGU+SW50ZWdlcjwvVHlwZT4KCQkJCTxSYW5nZUVudW1lcmF0aW9uPjEgYnl0ZXM8L1JhbmdlRW51bWVyYXRpb24+CgkJCQk8VW5pdHMgLz4KCQkJCTxEZXNjcmlwdGlvbj48IVtDREFUQVtJbmRpY2F0ZXMgdGhlIEFwcGxpY2F0aW9uIGRhdGEgcHJpb3JpdHk6CjA6SW1tZWRpYXRlCjE6QmVzdEVmZm9ydAoyOkxhdGVzdAozLTEwMDogUmVzZXJ2ZWQgZm9yIGZ1dHVyZSB1c2UuCjEwMS0yNTQ6IFByb3ByaWV0YXJ5IG1vZGUuXV0+PC9EZXNjcmlwdGlvbj4KCQkJPC9JdGVtPgoJCQk8SXRlbSBJRD0iMiI+PE5hbWU+RGF0YSBDcmVhdGlvbiBUaW1lPC9OYW1lPgoJCQkJPE9wZXJhdGlvbnM+Ulc8L09wZXJhdGlvbnM+CgkJCQk8TXVsdGlwbGVJbnN0YW5jZXM+U2luZ2xlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQkJCTxNYW5kYXRvcnk+T3B0aW9uYWw8L01hbmRhdG9yeT4KCQkJCTxUeXBlPlRpbWU8L1R5cGU+CgkJCQk8UmFuZ2VFbnVtZXJhdGlvbiAvPgoJCQkJPFVuaXRzIC8+CgkJCQk8RGVzY3JpcHRpb24+PCFbQ0RBVEFbSW5kaWNhdGVzIHRoZSBEYXRhIGluc3RhbmNlIGNyZWF0aW9uIHRpbWVzdGFtcC5dXT48L0Rlc2NyaXB0aW9uPgoJCQk8L0l0ZW0+CgkJCTxJdGVtIElEPSIzIj48TmFtZT5EYXRhIERlc2NyaXB0aW9uPC9OYW1lPgoJCQkJPE9wZXJhdGlvbnM+Ulc8L09wZXJhdGlvbnM+CgkJCQk8TXVsdGlwbGVJbnN0YW5jZXM+U2luZ2xlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQkJCTxNYW5kYXRvcnk+T3B0aW9uYWw8L01hbmRhdG9yeT4KCQkJCTxUeXBlPlN0cmluZzwvVHlwZT4KCQkJCTxSYW5nZUVudW1lcmF0aW9uPjMyIGJ5dGVzPC9SYW5nZUVudW1lcmF0aW9uPgoJCQkJPFVuaXRzIC8+CgkJCQk8RGVzY3JpcHRpb24+PCFbQ0RBVEFbSW5kaWNhdGVzIHRoZSBkYXRhIGRlc2NyaXB0aW9uLgplLmcuICJtZXRlciByZWFkaW5nIi5dXT48L0Rlc2NyaXB0aW9uPgoJCQk8L0l0ZW0+CgkJCTxJdGVtIElEPSI0Ij48TmFtZT5EYXRhIEZvcm1hdDwvTmFtZT4KCQkJCTxPcGVyYXRpb25zPlJXPC9PcGVyYXRpb25zPgoJCQkJPE11bHRpcGxlSW5zdGFuY2VzPlNpbmdsZTwvTXVsdGlwbGVJbnN0YW5jZXM+CgkJCQk8TWFuZGF0b3J5Pk9wdGlvbmFsPC9NYW5kYXRvcnk+CgkJCQk8VHlwZT5TdHJpbmc8L1R5cGU+CgkJCQk8UmFuZ2VFbnVtZXJhdGlvbj4zMiBieXRlczwvUmFuZ2VFbnVtZXJhdGlvbj4KCQkJCTxVbml0cyAvPgoJCQkJPERlc2NyaXB0aW9uPjwhW0NEQVRBW0luZGljYXRlcyB0aGUgZm9ybWF0IG9mIHRoZSBBcHBsaWNhdGlvbiBEYXRhLgplLmcuIFlHLU1ldGVyLVdhdGVyLVJlYWRpbmcKVVRGOC1zdHJpbmcKXV0+PC9EZXNjcmlwdGlvbj4KCQkJPC9JdGVtPgoJCQk8SXRlbSBJRD0iNSI+PE5hbWU+QXBwIElEPC9OYW1lPgoJCQkJPE9wZXJhdGlvbnM+Ulc8L09wZXJhdGlvbnM+CgkJCQk8TXVsdGlwbGVJbnN0YW5jZXM+U2luZ2xlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQkJCTxNYW5kYXRvcnk+T3B0aW9uYWw8L01hbmRhdG9yeT4KCQkJCTxUeXBlPkludGVnZXI8L1R5cGU+CgkJCQk8UmFuZ2VFbnVtZXJhdGlvbj4yIGJ5dGVzPC9SYW5nZUVudW1lcmF0aW9uPgoJCQkJPFVuaXRzIC8+CgkJCQk8RGVzY3JpcHRpb24+PCFbQ0RBVEFbSW5kaWNhdGVzIHRoZSBkZXN0aW5hdGlvbiBBcHBsaWNhdGlvbiBJRC5dXT48L0Rlc2NyaXB0aW9uPgoJCQk8L0l0ZW0+PC9SZXNvdXJjZXM+CgkJPERlc2NyaXB0aW9uMj48IVtDREFUQVtdXT48L0Rlc2NyaXB0aW9uMj4KCTwvT2JqZWN0Pgo8L0xXTTJNPgo=", accessMode = ApiModelProperty.AccessMode.READ_ONLY) @JsonIgnore private byte[] data; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java index 9d83d5922e..102029814f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java @@ -54,6 +54,10 @@ public class TbResourceInfo extends BaseData implements HasName, H @Length(fieldName = "file name") @ApiModelProperty(position = 9, value = "Resource file name.", example = "19.xml", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private String fileName; + @ApiModelProperty(position = 10, value = "Resource media type.", example = "image/png", accessMode = ApiModelProperty.AccessMode.READ_ONLY) + private String mediaType; + @ApiModelProperty(position = 11, value = "Resource link (for IMAGE resource type).", example = "/api/images/system/my-image.png", accessMode = ApiModelProperty.AccessMode.READ_ONLY) + private String link; public TbResourceInfo() { super(); @@ -72,6 +76,8 @@ public class TbResourceInfo extends BaseData implements HasName, H this.searchText = resourceInfo.searchText; this.etag = resourceInfo.etag; this.fileName = resourceInfo.fileName; + this.mediaType = resourceInfo.mediaType; + this.link = resourceInfo.link; } @ApiModelProperty(position = 1, value = "JSON object with the Resource Id. " + diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index 527cbb8957..c5f6d839a0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -498,6 +498,8 @@ public class ModelConstants { public static final String RESOURCE_FILE_NAME_COLUMN = "file_name"; public static final String RESOURCE_DATA_COLUMN = "data"; public static final String RESOURCE_ETAG_COLUMN = "etag"; + public static final String RESOURCE_MEDIA_TYPE_COLUMN = "media_type"; + public static final String RESOURCE_LINK_COLUMN = "link"; /** * Ota Package constants. diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceEntity.java index 502477699b..6a366c7621 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceEntity.java @@ -33,6 +33,8 @@ import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_DATA_COLU import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_ETAG_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_FILE_NAME_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_KEY_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_LINK_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_MEDIA_TYPE_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TABLE_NAME; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TENANT_ID_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TITLE_COLUMN; @@ -69,6 +71,12 @@ public class TbResourceEntity extends BaseSqlEntity implements BaseE @Column(name = RESOURCE_ETAG_COLUMN) private String etag; + @Column(name = RESOURCE_MEDIA_TYPE_COLUMN) + private String mediaType; + + @Column(name = RESOURCE_LINK_COLUMN) + private String link; + public TbResourceEntity() { } @@ -87,6 +95,8 @@ public class TbResourceEntity extends BaseSqlEntity implements BaseE this.fileName = resource.getFileName(); this.data = resource.getData(); this.etag = resource.getEtag(); + this.mediaType = resource.getMediaType(); + this.link = resource.getLink(); } @Override @@ -101,6 +111,8 @@ public class TbResourceEntity extends BaseSqlEntity implements BaseE resource.setFileName(fileName); resource.setData(data); resource.setEtag(etag); + resource.setMediaType(mediaType); + resource.setLink(link); return resource; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceInfoEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceInfoEntity.java index ced72920dc..c8d962c9af 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceInfoEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceInfoEntity.java @@ -32,6 +32,8 @@ import java.util.UUID; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_ETAG_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_FILE_NAME_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_KEY_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_LINK_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_MEDIA_TYPE_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TABLE_NAME; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TENANT_ID_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TITLE_COLUMN; @@ -65,6 +67,12 @@ public class TbResourceInfoEntity extends BaseSqlEntity implemen @Column(name = RESOURCE_FILE_NAME_COLUMN) private String fileName; + @Column(name = RESOURCE_MEDIA_TYPE_COLUMN) + private String mediaType; + + @Column(name = RESOURCE_LINK_COLUMN) + private String link; + public TbResourceInfoEntity() { } @@ -80,6 +88,8 @@ public class TbResourceInfoEntity extends BaseSqlEntity implemen this.searchText = resource.getSearchText(); this.hashCode = resource.getEtag(); this.fileName = resource.getFileName(); + this.mediaType = resource.getMediaType(); + this.link = resource.getLink(); } @Override @@ -93,6 +103,8 @@ public class TbResourceInfoEntity extends BaseSqlEntity implemen resource.setSearchText(searchText); resource.setEtag(hashCode); resource.setFileName(fileName); + resource.setMediaType(mediaType); + resource.setLink(link); return resource; } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java index 8bfb0ccfc5..5585580a8a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java @@ -38,9 +38,9 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.entity.AbstractCachedEntityService; import org.thingsboard.server.dao.exception.DataValidationException; -import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.service.Validator; +import org.thingsboard.server.dao.service.validator.ResourceDataValidator; import java.util.List; import java.util.Optional; @@ -57,14 +57,12 @@ public class BaseResourceService extends AbstractCachedEntityService resourceValidator; + private final ResourceDataValidator resourceValidator; @Override public TbResource saveResource(TbResource resource) { log.trace("Executing saveResource [{}]", resource); - if (resource.getData() != null) { - resource.setEtag(calculateEtag(resource.getData())); - } + TenantId tenantId = resource.getTenantId(); resourceValidator.validate(resource, TbResourceInfo::getTenantId); boolean newResource = resource.getId() == null; @@ -73,18 +71,26 @@ public class BaseResourceService extends AbstractCachedEntityService resourceInfoDao.findById(tenantId, resourceId.getId()), true); } + @Override + public TbResourceInfo findResourceInfoByTenantIdAndKey(TenantId tenantId, ResourceType resourceType, String resourceKey) { + log.trace("Executing findResourceInfoByTenantIdAndKey [{}] [{}] [{}]", tenantId, resourceType, resourceKey); + // TODO: add caching + return resourceInfoDao.findByTenantIdAndKey(tenantId, resourceType, resourceKey); + } + @Override public ListenableFuture findResourceInfoByIdAsync(TenantId tenantId, TbResourceId resourceId) { log.trace("Executing findResourceInfoById [{}] [{}]", tenantId, resourceId); diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/TbResourceDao.java b/dao/src/main/java/org/thingsboard/server/dao/resource/TbResourceDao.java index 4117563805..f3cc299721 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/TbResourceDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/TbResourceDao.java @@ -27,7 +27,7 @@ import java.util.List; public interface TbResourceDao extends Dao, TenantEntityWithDataDao { - TbResource getResource(TenantId tenantId, ResourceType resourceType, String resourceId); + TbResource findResourceByTenantIdAndKey(TenantId tenantId, ResourceType resourceType, String resourceId); PageData findAllByTenantId(TenantId tenantId, PageLink pageLink); diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/TbResourceInfoDao.java b/dao/src/main/java/org/thingsboard/server/dao/resource/TbResourceInfoDao.java index f0f0b6fdad..2354217f1c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/TbResourceInfoDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/TbResourceInfoDao.java @@ -15,18 +15,20 @@ */ package org.thingsboard.server.dao.resource; +import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.TbResourceInfo; import org.thingsboard.server.common.data.TbResourceInfoFilter; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.Dao; -import java.util.UUID; - public interface TbResourceInfoDao extends Dao { PageData findAllTenantResourcesByTenantId(TbResourceInfoFilter filter, PageLink pageLink); PageData findTenantResourcesByTenantId(TbResourceInfoFilter filter, PageLink pageLink); + TbResourceInfo findByTenantIdAndKey(TenantId tenantId, ResourceType resourceType, String resourceKey); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/ResourceDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/ResourceDataValidator.java index 3c6fc53e19..fec5f8cc6e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/ResourceDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/ResourceDataValidator.java @@ -17,6 +17,8 @@ package org.thingsboard.server.dao.service.validator; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; +import org.springframework.http.InvalidMediaTypeException; +import org.springframework.http.MediaType; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.TbResource; @@ -26,7 +28,6 @@ import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileCon import org.thingsboard.server.common.data.widget.BaseWidgetType; import org.thingsboard.server.common.data.widget.WidgetTypeDetails; import org.thingsboard.server.dao.exception.DataValidationException; -import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.resource.TbResourceDao; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; @@ -59,23 +60,12 @@ public class ResourceDataValidator extends DataValidator { if (resource.getData() == null || resource.getData().length == 0) { throw new DataValidationException("Resource data should be specified"); } - if (tenantId != null && !TenantId.SYS_TENANT_ID.equals(tenantId)) { - DefaultTenantProfileConfiguration profileConfiguration = tenantProfileCache.get(tenantId).getDefaultProfileConfiguration(); - long maxResourceSize = profileConfiguration.getMaxResourceSize(); - if (maxResourceSize > 0 && resource.getData().length > maxResourceSize) { - throw new IllegalArgumentException("Resource exceeds the maximum size of " + maxResourceSize + " bytes"); - } - long maxSumResourcesDataInBytes = profileConfiguration.getMaxResourcesInBytes(); - validateMaxSumDataSizePerTenant(tenantId, resourceDao, maxSumResourcesDataInBytes, resource.getData().length, TB_RESOURCE); - } } @Override protected TbResource validateUpdate(TenantId tenantId, TbResource resource) { - if (!tenantId.isSysTenantId()) { - if (resource.getData() != null) { - throw new DataValidationException("Resource data can't be updated"); - } + if (resource.getData() != null && !resource.getResourceType().isUpdatable()) { + throw new DataValidationException("This type of resource can't be updated"); } return resource; } @@ -86,6 +76,28 @@ public class ResourceDataValidator extends DataValidator { if (resource.getResourceType() == null) { throw new DataValidationException("Resource type should be specified!"); } + if (tenantId != null && !tenantId.isSysTenantId() && resource.getData() != null) { + DefaultTenantProfileConfiguration profileConfiguration = tenantProfileCache.get(tenantId).getDefaultProfileConfiguration(); + long maxResourceSize = profileConfiguration.getMaxResourceSize(); + if (maxResourceSize > 0 && resource.getData().length > maxResourceSize) { + throw new IllegalArgumentException("Resource exceeds the maximum size of " + maxResourceSize + " bytes"); + } + long maxSumResourcesDataInBytes = profileConfiguration.getMaxResourcesInBytes(); + validateMaxSumDataSizePerTenant(tenantId, resourceDao, maxSumResourcesDataInBytes, resource.getData().length, TB_RESOURCE); + } + if (resource.getResourceType().getDefaultMediaType() != null) { + resource.setMediaType(resource.getResourceType().getDefaultMediaType()); + } else { + if (resource.getMediaType() == null) { + throw new DataValidationException("Media type is required"); + } else { + try { + MediaType.parseMediaType(resource.getMediaType()); + } catch (InvalidMediaTypeException e) { + throw new DataValidationException("Invalid media type", e); + } + } + } if (StringUtils.isEmpty(resource.getFileName())) { throw new DataValidationException("Resource file name should be specified!"); } @@ -93,9 +105,9 @@ public class ResourceDataValidator extends DataValidator { throw new DataValidationException("Resource key should be specified!"); } if (resource.getTenantId() == null) { - resource.setTenantId(TenantId.fromUUID(ModelConstants.NULL_UUID)); + resource.setTenantId(TenantId.SYS_TENANT_ID); } - if (!resource.getTenantId().getId().equals(ModelConstants.NULL_UUID)) { + if (!resource.getTenantId().isSysTenantId()) { if (!tenantService.tenantExists(resource.getTenantId())) { throw new DataValidationException("Resource is referencing to non-existent tenant!"); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceDao.java index 641fda927b..36fa05e00e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceDao.java @@ -56,8 +56,7 @@ public class JpaTbResourceDao extends JpaAbstractDao createAndSaveFirmware(tenantId, "2")) .isInstanceOf(DataValidationException.class) - .hasMessageContaining("Failed to create the ota package, files size limit is exhausted %d bytes!", DATA_SIZE); + .hasMessageContaining("Ota packages total size exceeds the maximum of %d bytes", DATA_SIZE); } @Test diff --git a/ui-ngx/src/app/core/http/resource.service.ts b/ui-ngx/src/app/core/http/resource.service.ts index 76f28fbe45..f7cb8441d1 100644 --- a/ui-ngx/src/app/core/http/resource.service.ts +++ b/ui-ngx/src/app/core/http/resource.service.ts @@ -42,6 +42,10 @@ export class ResourceService { return this.http.get>(url, defaultHttpOptionsFromConfig(config)); } + public getResource(resourceId: string, config?: RequestConfig): Observable { + return this.http.get(`/api/resource/${resourceId}`, defaultHttpOptionsFromConfig(config)); + } + public getResourceInfo(resourceId: string, config?: RequestConfig): Observable { return this.http.get(`/api/resource/info/${resourceId}`, defaultHttpOptionsFromConfig(config)); } diff --git a/ui-ngx/src/app/shared/models/resource.models.ts b/ui-ngx/src/app/shared/models/resource.models.ts index 5c0d2c3ccc..b9a73a416c 100644 --- a/ui-ngx/src/app/shared/models/resource.models.ts +++ b/ui-ngx/src/app/shared/models/resource.models.ts @@ -22,7 +22,8 @@ export enum ResourceType { LWM2M_MODEL = 'LWM2M_MODEL', PKCS_12 = 'PKCS_12', JKS = 'JKS', - JS_MODULE = 'JS_MODULE' + JS_MODULE = 'JS_MODULE', + IMAGE = 'IMAGE' } export const ResourceTypeMIMETypes = new Map( @@ -30,7 +31,8 @@ export const ResourceTypeMIMETypes = new Map( [ResourceType.LWM2M_MODEL, 'application/xml,text/xml'], [ResourceType.PKCS_12, 'application/x-pkcs12'], [ResourceType.JKS, 'application/x-java-keystore'], - [ResourceType.JS_MODULE, 'text/javascript,application/javascript'] + [ResourceType.JS_MODULE, 'text/javascript,application/javascript'], + [ResourceType.IMAGE, 'image/*'] ] ); @@ -48,7 +50,8 @@ export const ResourceTypeTranslationMap = new Map( [ResourceType.LWM2M_MODEL, 'resource.type.lwm2m-model'], [ResourceType.PKCS_12, 'resource.type.pkcs-12'], [ResourceType.JKS, 'resource.type.jks'], - [ResourceType.JS_MODULE, 'resource.type.js-module'] + [ResourceType.JS_MODULE, 'resource.type.js-module'], + [ResourceType.IMAGE, 'resource.type.image'] ] ); @@ -58,15 +61,11 @@ export interface ResourceInfo extends Omit, 'name' | 'lab title?: string; resourceType: ResourceType; fileName: string; + mediaType: string; + link: string; } export interface Resource extends ResourceInfo { base64Data: string; name?: string; } - -// @ts-ignore FIXME -export interface Resources extends ResourceInfo { - data: Array; - fileName: Array; -} 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 745ac8ecb4..b1e7e1d179 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -3622,7 +3622,8 @@ "jks": "JKS", "js-module": "JS module", "lwm2m-model": "LWM2M model", - "pkcs-12": "PKCS #12" + "pkcs-12": "PKCS #12", + "image": "Image" } }, "rulechain": { From 8f9ac73ee34565806fc4f45e60ab79e167e8fd03 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Wed, 1 Nov 2023 15:55:08 +0200 Subject: [PATCH 016/252] UI: Refactoring display columns for tables --- .../alarm/alarms-table-widget.component.ts | 32 ++++++++----- .../lib/display-columns-panel.component.scss | 1 - .../entity/entities-table-widget.component.ts | 47 +++++++++++-------- .../lib/timeseries-table-widget.component.ts | 39 +++++++++------ ui-ngx/src/styles.scss | 2 +- 5 files changed, 73 insertions(+), 48 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/alarm/alarms-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/alarm/alarms-table-widget.component.ts index 034c131769..7fbd741fe1 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 @@ -503,17 +503,16 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, $event.stopPropagation(); } const target = $event.target || $event.srcElement || $event.currentTarget; - const config = new OverlayConfig(); - config.backdropClass = 'cdk-overlay-transparent-backdrop'; - config.hasBackdrop = true; - const connectedPosition: ConnectedPosition = { - originX: 'end', - originY: 'bottom', - overlayX: 'end', - overlayY: 'top' - }; - config.positionStrategy = this.overlay.position().flexibleConnectedTo(target as HTMLElement) - .withPositions([connectedPosition]); + const config = new OverlayConfig({ + panelClass: 'tb-display-columns', + backdropClass: 'cdk-overlay-transparent-backdrop', + hasBackdrop: true, + height: 'fit-content', + maxHeight: '75vh' + }); + config.positionStrategy = this.overlay.position() + .flexibleConnectedTo(target as HTMLElement) + .withPositions(DEFAULT_OVERLAY_POSITIONS); const overlayRef = this.overlay.create(config); overlayRef.backdropClick().subscribe(() => { @@ -549,9 +548,18 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, useValue: overlayRef } ]; + const injector = Injector.create({parent: this.viewContainerRef.injector, providers}); - overlayRef.attach(new ComponentPortal(DisplayColumnsPanelComponent, + const componentRef = overlayRef.attach(new ComponentPortal(DisplayColumnsPanelComponent, this.viewContainerRef, injector)); + + const resizeWindows$ = fromEvent(window, 'resize').subscribe(() => { + overlayRef.updatePosition(); + }); + componentRef.onDestroy(() => { + resizeWindows$.unsubscribe(); + }); + this.ctx.detectChanges(); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/display-columns-panel.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/display-columns-panel.component.scss index f3b531f32e..7017c61de0 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/display-columns-panel.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/display-columns-panel.component.scss @@ -16,7 +16,6 @@ :host { width: 100%; height: 100%; - max-height: 80vh; min-width: 300px; overflow: auto; background: #fff; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-table-widget.component.ts index df71fce4f8..f7e5025a26 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-table-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-table-widget.component.ts @@ -46,7 +46,7 @@ import { deepClone, hashCode, isDefined, isNumber, isObject, isUndefined } from import cssjs from '@core/css/css'; import { CollectionViewer, DataSource } from '@angular/cdk/collections'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; -import { BehaviorSubject, merge, Observable, Subject } from 'rxjs'; +import { BehaviorSubject, fromEvent, merge, Observable, Subject } from 'rxjs'; import { emptyPageData, PageData } from '@shared/models/page/page-data'; import { EntityId } from '@shared/models/id/entity-id'; import { entityTypeTranslations } from '@shared/models/entity-type.models'; @@ -106,6 +106,7 @@ import { ResizeObserver } from '@juggle/resize-observer'; import { hidePageSizePixelValue } from '@shared/models/constants'; import { AggregationType } from '@shared/models/time/time.models'; import { FormBuilder } from '@angular/forms'; +import { DEFAULT_OVERLAY_POSITIONS } from '@shared/models/overlay.models'; interface EntitiesTableWidgetSettings extends TableWidgetSettings { entitiesTitle: string; @@ -462,18 +463,17 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni if ($event) { $event.stopPropagation(); } - const target = $event.target || $event.currentTarget; - const config = new OverlayConfig(); - config.backdropClass = 'cdk-overlay-transparent-backdrop'; - config.hasBackdrop = true; - const connectedPosition: ConnectedPosition = { - originX: 'end', - originY: 'bottom', - overlayX: 'end', - overlayY: 'top' - }; - config.positionStrategy = this.overlay.position().flexibleConnectedTo(target as HTMLElement) - .withPositions([connectedPosition]); + const target = $event.target || $event.srcElement || $event.currentTarget; + const config = new OverlayConfig({ + panelClass: 'tb-display-columns', + backdropClass: 'cdk-overlay-transparent-backdrop', + hasBackdrop: true, + height: 'fit-content', + maxHeight: '75vh' + }); + config.positionStrategy = this.overlay.position() + .flexibleConnectedTo(target as HTMLElement) + .withPositions(DEFAULT_OVERLAY_POSITIONS); const overlayRef = this.overlay.create(config); overlayRef.backdropClick().subscribe(() => { @@ -481,11 +481,11 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni }); const columns: DisplayColumn[] = this.columns.map(column => ({ - title: column.title, - def: column.def, - display: this.displayedColumns.indexOf(column.def) > -1, - selectable: this.columnSelectionAvailability[column.def] - })); + title: column.title, + def: column.def, + display: this.displayedColumns.indexOf(column.def) > -1, + selectable: this.columnSelectionAvailability[column.def] + })); const providers: StaticProvider[] = [ { @@ -506,9 +506,18 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni useValue: overlayRef } ]; + const injector = Injector.create({parent: this.viewContainerRef.injector, providers}); - overlayRef.attach(new ComponentPortal(DisplayColumnsPanelComponent, + const componentRef = overlayRef.attach(new ComponentPortal(DisplayColumnsPanelComponent, this.viewContainerRef, injector)); + + const resizeWindows$ = fromEvent(window, 'resize').subscribe(() => { + overlayRef.updatePosition(); + }); + componentRef.onDestroy(() => { + resizeWindows$.unsubscribe(); + }); + this.ctx.detectChanges(); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts index 20dff4d85a..5e9dabbeb2 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts @@ -56,7 +56,7 @@ import cssjs from '@core/css/css'; import { PageLink } from '@shared/models/page/page-link'; import { Direction, SortOrder, sortOrderFromString } from '@shared/models/page/sort-order'; import { CollectionViewer, DataSource } from '@angular/cdk/collections'; -import { BehaviorSubject, merge, Observable, of, Subject, Subscription } from 'rxjs'; +import { BehaviorSubject, fromEvent, merge, Observable, of, Subject, Subscription } from 'rxjs'; import { emptyPageData, PageData } from '@shared/models/page/page-data'; import { catchError, debounceTime, distinctUntilChanged, map, skip, startWith, takeUntil } from 'rxjs/operators'; import { MatPaginator } from '@angular/material/paginator'; @@ -81,7 +81,7 @@ import { TableWidgetDataKeySettings, TableWidgetSettings } from '@home/components/widget/lib/table-widget.models'; -import { ConnectedPosition, Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay'; +import { Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay'; import { SubscriptionEntityInfo } from '@core/api/widget-api.models'; import { DatePipe } from '@angular/common'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; @@ -93,6 +93,7 @@ import { } from '@home/components/widget/lib/display-columns-panel.component'; import { ComponentPortal } from '@angular/cdk/portal'; import { FormBuilder } from '@angular/forms'; +import { DEFAULT_OVERLAY_POSITIONS } from '@shared/models/overlay.models'; export interface TimeseriesTableWidgetSettings extends TableWidgetSettings { showTimestamp: boolean; @@ -411,23 +412,23 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI $event.stopPropagation(); } if (this.sources.length) { - const target = $event.target || $event.currentTarget; - const config = new OverlayConfig(); - config.backdropClass = 'cdk-overlay-transparent-backdrop'; - config.hasBackdrop = true; - const connectedPosition: ConnectedPosition = { - originX: 'end', - originY: 'bottom', - overlayX: 'end', - overlayY: 'top' - }; - config.positionStrategy = this.overlay.position().flexibleConnectedTo(target as HTMLElement) - .withPositions([connectedPosition]); + const target = $event.target || $event.srcElement || $event.currentTarget; + const config = new OverlayConfig({ + panelClass: 'tb-display-columns', + backdropClass: 'cdk-overlay-transparent-backdrop', + hasBackdrop: true, + height: 'fit-content', + maxHeight: '75vh' + }); + config.positionStrategy = this.overlay.position() + .flexibleConnectedTo(target as HTMLElement) + .withPositions(DEFAULT_OVERLAY_POSITIONS); const overlayRef = this.overlay.create(config); overlayRef.backdropClick().subscribe(() => { overlayRef.dispose(); }); + const source = this.sources[this.sourceIndex]; this.prepareDisplayedColumn(); @@ -450,8 +451,16 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI ]; const injector = Injector.create({parent: this.viewContainerRef.injector, providers}); - overlayRef.attach(new ComponentPortal(DisplayColumnsPanelComponent, + const componentRef = overlayRef.attach(new ComponentPortal(DisplayColumnsPanelComponent, this.viewContainerRef, injector)); + + const resizeWindows$ = fromEvent(window, 'resize').subscribe(() => { + overlayRef.updatePosition(); + }); + componentRef.onDestroy(() => { + resizeWindows$.unsubscribe(); + }); + this.ctx.detectChanges(); } } diff --git a/ui-ngx/src/styles.scss b/ui-ngx/src/styles.scss index 0ee154cf7d..716af758f2 100644 --- a/ui-ngx/src/styles.scss +++ b/ui-ngx/src/styles.scss @@ -268,7 +268,7 @@ pre.tb-highlight { letter-spacing: normal; } -.tb-timewindow-panel, .tb-legend-config-panel, .tb-filter-panel { +.tb-timewindow-panel, .tb-legend-config-panel, .tb-filter-panel, .tb-display-columns { overflow: hidden; background: #fff; border-radius: 4px; From 3407c210b5af83fa13a3a85e6aa9cf3c42ff4c2d Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Wed, 1 Nov 2023 17:28:33 +0200 Subject: [PATCH 017/252] testFindTenantWidgetsBundlesByPageLink - cleanup system widgets --- .../server/controller/WidgetsBundleControllerTest.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/application/src/test/java/org/thingsboard/server/controller/WidgetsBundleControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/WidgetsBundleControllerTest.java index 80d70c1b8b..82dfa82d5a 100644 --- a/application/src/test/java/org/thingsboard/server/controller/WidgetsBundleControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/WidgetsBundleControllerTest.java @@ -268,6 +268,13 @@ public class WidgetsBundleControllerTest extends AbstractControllerTest { Collections.sort(loadedWidgetsBundles2, idComparator); Assert.assertEquals(tenantWidgetsBundles, loadedWidgetsBundles2); + + // cleanup + loginSysAdmin(); + for (WidgetsBundle sysWidgetsBundle : sysWidgetsBundles) { + doDelete("/api/widgetsBundle/" + sysWidgetsBundle.getId().getId().toString()) + .andExpect(status().isOk()); + } } @Test From 7ab9051a52dabd92ffe75b40432c8a941a9c2094 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Thu, 2 Nov 2023 13:03:21 +0200 Subject: [PATCH 018/252] UI: Change panel class name --- .../widget/lib/alarm/alarms-table-widget.component.ts | 2 +- .../widget/lib/entity/entities-table-widget.component.ts | 4 ++-- .../widget/lib/timeseries-table-widget.component.ts | 2 +- ui-ngx/src/styles.scss | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/alarm/alarms-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/alarm/alarms-table-widget.component.ts index 7fbd741fe1..b6129b74a7 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 @@ -504,7 +504,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, } const target = $event.target || $event.srcElement || $event.currentTarget; const config = new OverlayConfig({ - panelClass: 'tb-display-columns', + panelClass: 'tb-panel-container', backdropClass: 'cdk-overlay-transparent-backdrop', hasBackdrop: true, height: 'fit-content', diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-table-widget.component.ts index f7e5025a26..b7d1841431 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-table-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-table-widget.component.ts @@ -83,7 +83,7 @@ import { TableWidgetSettings, widthStyle } from '@home/components/widget/lib/table-widget.models'; -import { ConnectedPosition, Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay'; +import { Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay'; import { ComponentPortal } from '@angular/cdk/portal'; import { DISPLAY_COLUMNS_PANEL_DATA, @@ -465,7 +465,7 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni } const target = $event.target || $event.srcElement || $event.currentTarget; const config = new OverlayConfig({ - panelClass: 'tb-display-columns', + panelClass: 'tb-panel-container', backdropClass: 'cdk-overlay-transparent-backdrop', hasBackdrop: true, height: 'fit-content', diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts index 5e9dabbeb2..2b7d538a4d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts @@ -414,7 +414,7 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI if (this.sources.length) { const target = $event.target || $event.srcElement || $event.currentTarget; const config = new OverlayConfig({ - panelClass: 'tb-display-columns', + panelClass: 'tb-panel-container', backdropClass: 'cdk-overlay-transparent-backdrop', hasBackdrop: true, height: 'fit-content', diff --git a/ui-ngx/src/styles.scss b/ui-ngx/src/styles.scss index 716af758f2..2f068fd9ca 100644 --- a/ui-ngx/src/styles.scss +++ b/ui-ngx/src/styles.scss @@ -268,7 +268,7 @@ pre.tb-highlight { letter-spacing: normal; } -.tb-timewindow-panel, .tb-legend-config-panel, .tb-filter-panel, .tb-display-columns { +.tb-timewindow-panel, .tb-legend-config-panel, .tb-filter-panel, .tb-panel-container { overflow: hidden; background: #fff; border-radius: 4px; From df00add81b71ddde699f5c977d631cd658fcb1d3 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Thu, 2 Nov 2023 13:47:02 +0200 Subject: [PATCH 019/252] Edge resources support improvements; fixes for tests --- .../constructor/ResourceMsgConstructor.java | 4 +++- .../resource/BaseResourceProcessor.java | 2 +- .../resource/ResourceEdgeProcessor.java | 2 +- .../controller/TbResourceControllerTest.java | 3 +++ .../server/edge/ResourceEdgeTest.java | 3 ++- .../DefaultTransportApiServiceTest.java | 4 ++-- .../dao/resource/BaseResourceService.java | 12 ++++++------ .../validator/ResourceDataValidator.java | 18 +++++++++--------- 8 files changed, 27 insertions(+), 21 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ResourceMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ResourceMsgConstructor.java index 27b7dc3978..14da03c37d 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ResourceMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ResourceMsgConstructor.java @@ -23,6 +23,8 @@ import org.thingsboard.server.gen.edge.v1.ResourceUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; +import java.util.Base64; + @Component @TbCoreComponent public class ResourceMsgConstructor { @@ -37,7 +39,7 @@ public class ResourceMsgConstructor { .setResourceType(tbResource.getResourceType().name()) .setFileName(tbResource.getFileName()); if (tbResource.getData() != null) { - builder.setData(tbResource.getData()); + builder.setData(Base64.getEncoder().encodeToString(tbResource.getData())); } if (tbResource.getEtag() != null) { builder.setEtag(tbResource.getEtag()); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/BaseResourceProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/BaseResourceProcessor.java index 13ca012f3c..88d382a6ed 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/BaseResourceProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/BaseResourceProcessor.java @@ -46,7 +46,7 @@ public abstract class BaseResourceProcessor extends BaseEdgeProcessor { resource.setResourceKey(resourceUpdateMsg.getResourceKey()); resource.setResourceType(ResourceType.valueOf(resourceUpdateMsg.getResourceType())); resource.setFileName(resourceUpdateMsg.getFileName()); - resource.setData(resourceUpdateMsg.hasData() ? resourceUpdateMsg.getData() : null); + resource.setBase64Data(resourceUpdateMsg.hasData() ? resourceUpdateMsg.getData() : null); resource.setEtag(resourceUpdateMsg.hasEtag() ? resourceUpdateMsg.getEtag() : null); resourceValidator.validate(resource, TbResourceInfo::getTenantId); if (created) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessor.java index e7c5b21dbd..d2365f5fda 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessor.java @@ -58,7 +58,7 @@ public class ResourceEdgeProcessor extends BaseResourceProcessor { return handleUnsupportedMsgType(resourceUpdateMsg.getMsgType()); } } catch (DataValidationException e) { - if (e.getMessage().contains("files size limit is exhausted")) { + if (e.getMessage().contains("exceeds the maximum")) { log.warn("[{}] Resource data size has been exhausted {}", tenantId, resourceUpdateMsg, e); return Futures.immediateFuture(null); } else { diff --git a/application/src/test/java/org/thingsboard/server/controller/TbResourceControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/TbResourceControllerTest.java index 239c189243..f84e786edd 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TbResourceControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/TbResourceControllerTest.java @@ -685,6 +685,9 @@ public class TbResourceControllerTest extends AbstractControllerTest { .isEqualTo(download(savedResource.getId())) .isEqualTo(Base64.getDecoder().decode(TEST_DATA)); assertThat(imageResponse.getContentType()).isEqualTo("image/png"); + + loginSysAdmin(); + doDelete("/api/resource/" + savedResource.getId()).andExpect(status().isOk()); } @Test diff --git a/application/src/test/java/org/thingsboard/server/edge/ResourceEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/ResourceEdgeTest.java index 6a3001f292..7a0564debf 100644 --- a/application/src/test/java/org/thingsboard/server/edge/ResourceEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/ResourceEdgeTest.java @@ -45,7 +45,7 @@ public class ResourceEdgeTest extends AbstractEdgeTest { resource.setResourceType(ResourceType.JKS); resource.setTitle("Edge Test Resource"); resource.setFileName(FILE_NAME); - resource.setData(TEST_DATA); + resource.setBase64Data(TEST_DATA); edgeImitator.expectMessageAmount(1); TbResource savedResource = doPost("/api/resource", resource, TbResource.class); @@ -119,5 +119,6 @@ public class ResourceEdgeTest extends AbstractEdgeTest { TbResource tbResource = doGet("/api/resource/" + uuid, TbResource.class); Assert.assertNotNull(tbResource); Assert.assertEquals("Edge Test Resource", tbResource.getName()); + Assert.assertEquals(TEST_DATA, tbResource.getBase64Data()); } } diff --git a/application/src/test/java/org/thingsboard/server/service/transport/DefaultTransportApiServiceTest.java b/application/src/test/java/org/thingsboard/server/service/transport/DefaultTransportApiServiceTest.java index 33c11ea91d..43d8960f3b 100644 --- a/application/src/test/java/org/thingsboard/server/service/transport/DefaultTransportApiServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/transport/DefaultTransportApiServiceTest.java @@ -44,13 +44,13 @@ import org.thingsboard.server.dao.device.provision.ProvisionResponseStatus; import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.relation.RelationService; +import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.util.DataDecodingEncodingService; import org.thingsboard.server.service.apiusage.TbApiUsageStateService; import org.thingsboard.server.service.executors.DbCallbackExecutorService; import org.thingsboard.server.service.profile.TbDeviceProfileCache; -import org.thingsboard.server.service.resource.TbResourceService; import java.io.IOException; import java.nio.file.Files; @@ -96,7 +96,7 @@ public class DefaultTransportApiServiceTest { @MockBean protected DeviceProvisionService deviceProvisionService; @MockBean - protected TbResourceService resourceService; + protected ResourceService resourceService; @MockBean protected OtaPackageService otaPackageService; @MockBean diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java index af0a9e47a5..26829bb1af 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java @@ -64,13 +64,13 @@ public class BaseResourceService extends AbstractCachedEntityService { @Override protected void validateDataImpl(TenantId tenantId, TbResource resource) { validateString("Resource title", resource.getTitle()); + if (resource.getTenantId() == null) { + resource.setTenantId(TenantId.SYS_TENANT_ID); + } + if (!resource.getTenantId().isSysTenantId()) { + if (!tenantService.tenantExists(resource.getTenantId())) { + throw new DataValidationException("Resource is referencing to non-existent tenant!"); + } + } if (resource.getResourceType() == null) { throw new DataValidationException("Resource type should be specified!"); } - if (tenantId != null && !tenantId.isSysTenantId() && resource.getData() != null) { + if (!resource.getTenantId().isSysTenantId() && resource.getData() != null) { DefaultTenantProfileConfiguration profileConfiguration = tenantProfileCache.get(tenantId).getDefaultProfileConfiguration(); long maxResourceSize = profileConfiguration.getMaxResourceSize(); if (maxResourceSize > 0 && resource.getData().length > maxResourceSize) { @@ -104,14 +112,6 @@ public class ResourceDataValidator extends DataValidator { if (StringUtils.isEmpty(resource.getResourceKey())) { throw new DataValidationException("Resource key should be specified!"); } - if (resource.getTenantId() == null) { - resource.setTenantId(TenantId.SYS_TENANT_ID); - } - if (!resource.getTenantId().isSysTenantId()) { - if (!tenantService.tenantExists(resource.getTenantId())) { - throw new DataValidationException("Resource is referencing to non-existent tenant!"); - } - } } @Override From cdaedc8b51785c5a2d66b047045e6dbadffc6bed Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Thu, 2 Nov 2023 16:52:51 +0200 Subject: [PATCH 020/252] UI: Refactoring event filter panel --- .../event/event-filter-panel.component.html | 86 ++++++++++--------- .../event/event-filter-panel.component.scss | 19 ++-- .../components/event/event-table-config.ts | 30 ++++--- 3 files changed, 65 insertions(+), 70 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/event/event-filter-panel.component.html b/ui-ngx/src/app/modules/home/components/event/event-filter-panel.component.html index adfe411670..77e5dafca8 100644 --- a/ui-ngx/src/app/modules/home/components/event/event-filter-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/event/event-filter-panel.component.html @@ -15,50 +15,52 @@ limitations under the License. --> -
- - - - - {{ column.title | translate}} - - {{ 'event.all-events' | translate}} - - {{ value }} - - - - - - - {{ column.title | translate}} - - - {{ 'event.min-value' | translate }} - - - - - - {{ 'event.has-error' | translate }} - - - - - {{ column.title | translate}} - - - - - - {{ column.title | translate}} - - + +
+ + + + + {{ column.title | translate}} + + {{ 'event.all-events' | translate}} + + {{ value }} + + + + + + + {{ column.title | translate}} + + + {{ 'event.min-value' | translate }} + + + + + + {{ 'event.has-error' | translate }} + + + + + {{ column.title | translate}} + + + + + + {{ column.title | translate}} + + + - -
+
+
\n \n \n \n \n
\n \n
\n \n tb.rulenode.alarm-type\n \n \n {{ \'tb.rulenode.alarm-type-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n\n',dependencies:[{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ae.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","scriptLanguage","noValidate","required"]},{kind:"component",type:ie.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ie.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:se.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:me.TbScriptLangComponent,selector:"tb-script-lang",inputs:["disabled"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:rt,decorators:[{type:n,args:[{selector:"tb-action-node-clear-alarm-config",template:'
\n \n \n \n \n \n \n \n
\n \n
\n \n tb.rulenode.alarm-type\n \n \n {{ \'tb.rulenode.alarm-type-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder},{type:Z.NodeScriptTestService},{type:j.TranslateService}]},propDecorators:{jsFuncComponent:[{type:o,args:["jsFuncComponent",{static:!1}]}],tbelFuncComponent:[{type:o,args:["tbelFuncComponent",{static:!1}]}]}});class ot extends s{constructor(e,t,n,o){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=n,this.translate=o,this.alarmSeverities=Object.keys(c),this.alarmSeverityTranslationMap=f,this.separatorKeysCodes=[ue,pe,de],this.tbelEnabled=X(this.store).tbelEnabled,this.scriptLanguage=d,this.changeScript=new r,this.hasScript=!0,this.testScriptLabel="tb.rulenode.test-details-function"}configForm(){return this.createAlarmConfigForm}onConfigurationSet(e){this.createAlarmConfigForm=this.fb.group({scriptLang:[e?e.scriptLang:d.JS,[D.required]],alarmDetailsBuildJs:[e?e.alarmDetailsBuildJs:null,[]],alarmDetailsBuildTbel:[e?e.alarmDetailsBuildTbel:null,[]],useMessageAlarmData:[!!e&&e.useMessageAlarmData,[]],overwriteAlarmDetails:[!!e&&e.overwriteAlarmDetails,[]],alarmType:[e?e.alarmType:null,[]],severity:[e?e.severity:null,[]],propagate:[!!e&&e.propagate,[]],relationTypes:[e?e.relationTypes:null,[]],propagateToOwner:[!!e&&e.propagateToOwner,[]],propagateToTenant:[!!e&&e.propagateToTenant,[]],dynamicSeverity:!1}),this.createAlarmConfigForm.get("dynamicSeverity").valueChanges.subscribe((e=>{e?this.createAlarmConfigForm.get("severity").patchValue("",{emitEvent:!1}):this.createAlarmConfigForm.get("severity").patchValue(this.alarmSeverities[0],{emitEvent:!1})}))}validatorTriggers(){return["useMessageAlarmData","overwriteAlarmDetails","scriptLang"]}updateValidators(e){const t=this.createAlarmConfigForm.get("useMessageAlarmData").value,n=this.createAlarmConfigForm.get("overwriteAlarmDetails").value;t?(this.createAlarmConfigForm.get("alarmType").setValidators([]),this.createAlarmConfigForm.get("severity").setValidators([])):(this.createAlarmConfigForm.get("alarmType").setValidators([D.required]),this.createAlarmConfigForm.get("severity").setValidators([D.required])),this.createAlarmConfigForm.get("alarmType").updateValueAndValidity({emitEvent:e}),this.createAlarmConfigForm.get("severity").updateValueAndValidity({emitEvent:e});let r=this.createAlarmConfigForm.get("scriptLang").value;r!==d.TBEL||this.tbelEnabled||(r=d.JS,this.createAlarmConfigForm.get("scriptLang").patchValue(r,{emitEvent:!1}),setTimeout((()=>{this.createAlarmConfigForm.updateValueAndValidity({emitEvent:!0})})));const o=!1===t||!0===n;this.createAlarmConfigForm.get("alarmDetailsBuildJs").setValidators(o&&r===d.JS?[D.required]:[]),this.createAlarmConfigForm.get("alarmDetailsBuildTbel").setValidators(o&&r===d.TBEL?[D.required]:[]),this.createAlarmConfigForm.get("alarmDetailsBuildJs").updateValueAndValidity({emitEvent:e}),this.createAlarmConfigForm.get("alarmDetailsBuildTbel").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=d.JS)),e}testScript(e){const t=this.createAlarmConfigForm.get("scriptLang").value,n=t===d.JS?"alarmDetailsBuildJs":"alarmDetailsBuildTbel",r=t===d.JS?"rulenode/create_alarm_node_script_fn":"rulenode/tbel/create_alarm_node_script_fn",o=this.createAlarmConfigForm.get(n).value;this.nodeScriptTestService.testNodeScript(o,"json",this.translate.instant("tb.rulenode.details"),"Details",["msg","metadata","msgType"],this.ruleNodeId,r,t,e).subscribe((e=>{e&&(this.createAlarmConfigForm.get(n).setValue(e),this.changeScript.emit())}))}removeKey(e,t){const n=this.createAlarmConfigForm.get(t).value,r=n.indexOf(e);r>=0&&(n.splice(r,1),this.createAlarmConfigForm.get(t).setValue(n,{emitEvent:!0}))}addKey(e,t){const n=e.input;let r=e.value;if((r||"").trim()){r=r.trim();let e=this.createAlarmConfigForm.get(t).value;e&&-1!==e.indexOf(r)||(e||(e=[]),e.push(r),this.createAlarmConfigForm.get(t).setValue(e,{emitEvent:!0}))}n&&(n.value="")}onValidate(){const e=this.createAlarmConfigForm.get("useMessageAlarmData").value,t=this.createAlarmConfigForm.get("overwriteAlarmDetails").value;if(!e||t){this.createAlarmConfigForm.get("scriptLang").value===d.JS&&this.jsFuncComponent.validateOnSubmit()}}}e("CreateAlarmConfigComponent",ot),ot.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:ot,deps:[{token:G.Store},{token:E.UntypedFormBuilder},{token:Z.NodeScriptTestService},{token:j.TranslateService}],target:t.ɵɵFactoryTarget.Component}),ot.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:ot,selector:"tb-action-node-create-alarm-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0},{propertyName:"tbelFuncComponent",first:!0,predicate:["tbelFuncComponent"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.use-message-alarm-data\' | translate }}\n \n \n {{ \'tb.rulenode.overwrite-alarm-details\' | translate }}\n \n
\n \n \n \n \n \n \n \n
\n \n
\n
\n
\n \n tb.rulenode.alarm-type\n \n \n {{ \'tb.rulenode.alarm-type-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.use-alarm-severity-pattern\' | translate }}\n \n \n tb.rulenode.alarm-severity\n \n \n {{ alarmSeverityTranslationMap.get(severity) | translate }}\n \n \n \n {{ \'tb.rulenode.alarm-severity-required\' | translate }}\n \n \n \n tb.rulenode.alarm-severity-pattern\n \n \n {{ \'tb.rulenode.alarm-severity-required\' | translate }}\n \n \n \n \n {{ \'tb.rulenode.propagate\' | translate }}\n \n
\n \n tb.rulenode.relation-types-list\n \n \n {{key}}\n close\n \n \n \n tb.rulenode.relation-types-list-hint\n \n
\n \n {{ \'tb.rulenode.propagate-to-owner\' | translate }}\n \n \n {{ \'tb.rulenode.propagate-to-tenant\' | translate }}\n \n
\n
\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ae.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","scriptLanguage","noValidate","required"]},{kind:"component",type:ie.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ie.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:U.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:se.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"component",type:ce.MatChipGrid,selector:"mat-chip-grid",inputs:["tabIndex","disabled","placeholder","required","value","errorStateMatcher"],outputs:["change","valueChange"]},{kind:"directive",type:ce.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputFor","matChipInputAddOnBlur","matChipInputSeparatorKeyCodes","placeholder","id","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]},{kind:"directive",type:ce.MatChipRemove,selector:"[matChipRemove]"},{kind:"component",type:ce.MatChipRow,selector:"mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]",inputs:["color","disabled","disableRipple","tabIndex","editable"],outputs:["edited"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:me.TbScriptLangComponent,selector:"tb-script-lang",inputs:["disabled"]},{kind:"pipe",type:fe.SafePipe,name:"safe"},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:ot,decorators:[{type:n,args:[{selector:"tb-action-node-create-alarm-config",template:'
\n \n {{ \'tb.rulenode.use-message-alarm-data\' | translate }}\n \n \n {{ \'tb.rulenode.overwrite-alarm-details\' | translate }}\n \n
\n \n \n \n \n \n \n \n
\n \n
\n
\n
\n \n tb.rulenode.alarm-type\n \n \n {{ \'tb.rulenode.alarm-type-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.use-alarm-severity-pattern\' | translate }}\n \n \n tb.rulenode.alarm-severity\n \n \n {{ alarmSeverityTranslationMap.get(severity) | translate }}\n \n \n \n {{ \'tb.rulenode.alarm-severity-required\' | translate }}\n \n \n \n tb.rulenode.alarm-severity-pattern\n \n \n {{ \'tb.rulenode.alarm-severity-required\' | translate }}\n \n \n \n \n {{ \'tb.rulenode.propagate\' | translate }}\n \n
\n \n tb.rulenode.relation-types-list\n \n \n {{key}}\n close\n \n \n \n tb.rulenode.relation-types-list-hint\n \n
\n \n {{ \'tb.rulenode.propagate-to-owner\' | translate }}\n \n \n {{ \'tb.rulenode.propagate-to-tenant\' | translate }}\n \n
\n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder},{type:Z.NodeScriptTestService},{type:j.TranslateService}]},propDecorators:{jsFuncComponent:[{type:o,args:["jsFuncComponent",{static:!1}]}],tbelFuncComponent:[{type:o,args:["tbelFuncComponent",{static:!1}]}]}});class at extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.directionTypes=Object.keys(g),this.directionTypeTranslations=y,this.entityType=x}configForm(){return this.createRelationConfigForm}onConfigurationSet(e){this.createRelationConfigForm=this.fb.group({direction:[e?e.direction:null,[D.required]],entityType:[e?e.entityType:null,[D.required]],entityNamePattern:[e?e.entityNamePattern:null,[]],entityTypePattern:[e?e.entityTypePattern:null,[]],relationType:[e?e.relationType:null,[D.required]],createEntityIfNotExists:[!!e&&e.createEntityIfNotExists,[]],removeCurrentRelations:[!!e&&e.removeCurrentRelations,[]],changeOriginatorToRelatedEntity:[!!e&&e.changeOriginatorToRelatedEntity,[]],entityCacheExpiration:[e?e.entityCacheExpiration:null,[D.required,D.min(0)]]})}validatorTriggers(){return["entityType"]}updateValidators(e){const t=this.createRelationConfigForm.get("entityType").value;t?this.createRelationConfigForm.get("entityNamePattern").setValidators([D.required,D.pattern(/.*\S.*/)]):this.createRelationConfigForm.get("entityNamePattern").setValidators([]),!t||t!==x.DEVICE&&t!==x.ASSET?this.createRelationConfigForm.get("entityTypePattern").setValidators([]):this.createRelationConfigForm.get("entityTypePattern").setValidators([D.required,D.pattern(/.*\S.*/)]),this.createRelationConfigForm.get("entityNamePattern").updateValueAndValidity({emitEvent:e}),this.createRelationConfigForm.get("entityTypePattern").updateValueAndValidity({emitEvent:e})}prepareOutputConfig(e){return e.entityNamePattern=e.entityNamePattern?e.entityNamePattern.trim():null,e.entityTypePattern=e.entityTypePattern?e.entityTypePattern.trim():null,e}}e("CreateRelationConfigComponent",at),at.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:at,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),at.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:at,selector:"tb-action-node-create-relation-config",usesInheritance:!0,ngImport:t,template:'
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.entity-name-pattern\n \n \n {{ \'tb.rulenode.entity-name-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.entity-type-pattern\n \n \n {{ \'tb.rulenode.entity-type-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n \n tb.rulenode.relation-type-pattern\n \n \n {{ \'tb.rulenode.relation-type-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n \n {{ \'tb.rulenode.create-entity-if-not-exists\' | translate }}\n \n
tb.rulenode.create-entity-if-not-exists-hint
\n
\n \n {{ \'tb.rulenode.remove-current-relations\' | translate }}\n \n
tb.rulenode.remove-current-relations-hint
\n \n {{ \'tb.rulenode.change-originator-to-related-entity\' | translate }}\n \n
tb.rulenode.change-originator-to-related-entity-hint
\n \n tb.rulenode.entity-cache-expiration\n \n \n {{ \'tb.rulenode.entity-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.entity-cache-expiration-range\' | translate }}\n \n tb.rulenode.entity-cache-expiration-hint\n \n
\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ge.EntityTypeSelectComponent,selector:"tb-entity-type-select",inputs:["allowedEntityTypes","useAliasEntityTypes","filterAllowedEntityTypes","showLabel","required","disabled"]},{kind:"component",type:U.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:at,decorators:[{type:n,args:[{selector:"tb-action-node-create-relation-config",template:'
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.entity-name-pattern\n \n \n {{ \'tb.rulenode.entity-name-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.entity-type-pattern\n \n \n {{ \'tb.rulenode.entity-type-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n \n tb.rulenode.relation-type-pattern\n \n \n {{ \'tb.rulenode.relation-type-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n \n {{ \'tb.rulenode.create-entity-if-not-exists\' | translate }}\n \n
tb.rulenode.create-entity-if-not-exists-hint
\n
\n \n {{ \'tb.rulenode.remove-current-relations\' | translate }}\n \n
tb.rulenode.remove-current-relations-hint
\n \n {{ \'tb.rulenode.change-originator-to-related-entity\' | translate }}\n \n
tb.rulenode.change-originator-to-related-entity-hint
\n \n tb.rulenode.entity-cache-expiration\n \n \n {{ \'tb.rulenode.entity-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.entity-cache-expiration-range\' | translate }}\n \n tb.rulenode.entity-cache-expiration-hint\n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class it extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.directionTypes=Object.keys(g),this.directionTypeTranslations=y,this.entityType=x}configForm(){return this.deleteRelationConfigForm}onConfigurationSet(e){this.deleteRelationConfigForm=this.fb.group({deleteForSingleEntity:[!!e&&e.deleteForSingleEntity,[]],direction:[e?e.direction:null,[D.required]],entityType:[e?e.entityType:null,[]],entityNamePattern:[e?e.entityNamePattern:null,[]],relationType:[e?e.relationType:null,[D.required]],entityCacheExpiration:[e?e.entityCacheExpiration:null,[D.required,D.min(0)]]})}validatorTriggers(){return["deleteForSingleEntity","entityType"]}updateValidators(e){const t=this.deleteRelationConfigForm.get("deleteForSingleEntity").value,n=this.deleteRelationConfigForm.get("entityType").value;t?this.deleteRelationConfigForm.get("entityType").setValidators([D.required]):this.deleteRelationConfigForm.get("entityType").setValidators([]),t&&n?this.deleteRelationConfigForm.get("entityNamePattern").setValidators([D.required,D.pattern(/.*\S.*/)]):this.deleteRelationConfigForm.get("entityNamePattern").setValidators([]),this.deleteRelationConfigForm.get("entityType").updateValueAndValidity({emitEvent:!1}),this.deleteRelationConfigForm.get("entityNamePattern").updateValueAndValidity({emitEvent:e})}prepareOutputConfig(e){return e.entityNamePattern=e.entityNamePattern?e.entityNamePattern.trim():null,e}}e("DeleteRelationConfigComponent",it),it.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:it,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),it.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:it,selector:"tb-action-node-delete-relation-config",usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.delete-relation-to-specific-entity\' | translate }}\n \n
tb.rulenode.delete-relation-hint
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.entity-name-pattern\n \n \n {{ \'tb.rulenode.entity-name-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n \n tb.rulenode.relation-type-pattern\n \n \n {{ \'tb.rulenode.relation-type-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.entity-cache-expiration\n \n \n {{ \'tb.rulenode.entity-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.entity-cache-expiration-range\' | translate }}\n \n tb.rulenode.entity-cache-expiration-hint\n \n
\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ge.EntityTypeSelectComponent,selector:"tb-entity-type-select",inputs:["allowedEntityTypes","useAliasEntityTypes","filterAllowedEntityTypes","showLabel","required","disabled"]},{kind:"component",type:U.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:it,decorators:[{type:n,args:[{selector:"tb-action-node-delete-relation-config",template:'
\n \n {{ \'tb.rulenode.delete-relation-to-specific-entity\' | translate }}\n \n
tb.rulenode.delete-relation-hint
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.entity-name-pattern\n \n \n {{ \'tb.rulenode.entity-name-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n \n tb.rulenode.relation-type-pattern\n \n \n {{ \'tb.rulenode.relation-type-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.entity-cache-expiration\n \n \n {{ \'tb.rulenode.entity-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.entity-cache-expiration-range\' | translate }}\n \n tb.rulenode.entity-cache-expiration-hint\n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class lt extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.deviceProfile}onConfigurationSet(e){this.deviceProfile=this.fb.group({persistAlarmRulesState:[!!e&&e.persistAlarmRulesState,D.required],fetchAlarmRulesStateOnStart:[!!e&&e.fetchAlarmRulesStateOnStart,D.required]})}}e("DeviceProfileConfigComponent",lt),lt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:lt,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),lt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:lt,selector:"tb-device-profile-config",usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.persist-alarm-rules\' | translate }}\n \n \n {{ \'tb.rulenode.fetch-alarm-rules\' | translate }}\n \n
\n',dependencies:[{kind:"component",type:U.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:lt,decorators:[{type:n,args:[{selector:"tb-device-profile-config",template:'
\n \n {{ \'tb.rulenode.persist-alarm-rules\' | translate }}\n \n \n {{ \'tb.rulenode.fetch-alarm-rules\' | translate }}\n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class st extends s{constructor(e,t,n,o){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=n,this.translate=o,this.tbelEnabled=X(this.store).tbelEnabled,this.scriptLanguage=d,this.changeScript=new r,this.hasScript=!0,this.testScriptLabel="tb.rulenode.test-generator-function",this.serviceType=p.TB_RULE_ENGINE}configForm(){return this.generatorConfigForm}onConfigurationSet(e){this.generatorConfigForm=this.fb.group({msgCount:[e?e.msgCount:null,[D.required,D.min(0)]],periodInSeconds:[e?e.periodInSeconds:null,[D.required,D.min(1)]],originator:[e?e.originator:null,[]],scriptLang:[e?e.scriptLang:d.JS,[D.required]],jsScript:[e?e.jsScript:null,[]],tbelScript:[e?e.tbelScript:null,[]],queueName:[e?e.queueName:null,[]]})}validatorTriggers(){return["scriptLang"]}updateValidators(e){let t=this.generatorConfigForm.get("scriptLang").value;t!==d.TBEL||this.tbelEnabled||(t=d.JS,this.generatorConfigForm.get("scriptLang").patchValue(t,{emitEvent:!1}),setTimeout((()=>{this.generatorConfigForm.updateValueAndValidity({emitEvent:!0})}))),this.generatorConfigForm.get("jsScript").setValidators(t===d.JS?[D.required]:[]),this.generatorConfigForm.get("jsScript").updateValueAndValidity({emitEvent:e}),this.generatorConfigForm.get("tbelScript").setValidators(t===d.TBEL?[D.required]:[]),this.generatorConfigForm.get("tbelScript").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=d.JS),e.originatorId&&e.originatorType?e.originator={id:e.originatorId,entityType:e.originatorType}:e.originator=null,delete e.originatorId,delete e.originatorType),e}prepareOutputConfig(e){return e.originator?(e.originatorId=e.originator.id,e.originatorType=e.originator.entityType):(e.originatorId=null,e.originatorType=null),delete e.originator,e}testScript(e){const t=this.generatorConfigForm.get("scriptLang").value,n=t===d.JS?"jsScript":"tbelScript",r=t===d.JS?"rulenode/generator_node_script_fn":"rulenode/tbel/generator_node_script_fn",o=this.generatorConfigForm.get(n).value;this.nodeScriptTestService.testNodeScript(o,"generate",this.translate.instant("tb.rulenode.generator"),"Generate",["prevMsg","prevMetadata","prevMsgType"],this.ruleNodeId,r,t,e).subscribe((e=>{e&&(this.generatorConfigForm.get(n).setValue(e),this.changeScript.emit())}))}onValidate(){this.generatorConfigForm.get("scriptLang").value===d.JS&&this.jsFuncComponent.validateOnSubmit()}}var mt;e("GeneratorConfigComponent",st),st.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:st,deps:[{token:G.Store},{token:E.UntypedFormBuilder},{token:Z.NodeScriptTestService},{token:j.TranslateService}],target:t.ɵɵFactoryTarget.Component}),st.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:st,selector:"tb-action-node-generator-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0},{propertyName:"tbelFuncComponent",first:!0,predicate:["tbelFuncComponent"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.message-count\n \n \n {{ \'tb.rulenode.message-count-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-message-count-message\' | translate }}\n \n \n \n tb.rulenode.period-seconds\n \n \n {{ \'tb.rulenode.period-seconds-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-period-seconds-message\' | translate }}\n \n \n
\n \n \n \n
\n\n \n \n\n \n \n \n \n \n \n \n
\n \n
\n
\n',dependencies:[{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ye.EntitySelectComponent,selector:"tb-entity-select",inputs:["allowedEntityTypes","useAliasEntityTypes","required","disabled"]},{kind:"component",type:W.QueueAutocompleteComponent,selector:"tb-queue-autocomplete",inputs:["labelText","requiredText","autocompleteHint","subscriptSizing","required","queueType","disabled"]},{kind:"component",type:ae.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","scriptLanguage","noValidate","required"]},{kind:"component",type:ie.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ie.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:se.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:me.TbScriptLangComponent,selector:"tb-script-lang",inputs:["disabled"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:st,decorators:[{type:n,args:[{selector:"tb-action-node-generator-config",template:'
\n \n tb.rulenode.message-count\n \n \n {{ \'tb.rulenode.message-count-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-message-count-message\' | translate }}\n \n \n \n tb.rulenode.period-seconds\n \n \n {{ \'tb.rulenode.period-seconds-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-period-seconds-message\' | translate }}\n \n \n
\n \n \n \n
\n\n \n \n\n \n \n \n \n \n \n \n
\n \n
\n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder},{type:Z.NodeScriptTestService},{type:j.TranslateService}]},propDecorators:{jsFuncComponent:[{type:o,args:["jsFuncComponent",{static:!1}]}],tbelFuncComponent:[{type:o,args:["tbelFuncComponent",{static:!1}]}]}}),function(e){e.CUSTOMER="CUSTOMER",e.TENANT="TENANT",e.RELATED="RELATED",e.ALARM_ORIGINATOR="ALARM_ORIGINATOR",e.ENTITY="ENTITY"}(mt||(mt={}));const ut=new Map([[mt.CUSTOMER,"tb.rulenode.originator-customer"],[mt.TENANT,"tb.rulenode.originator-tenant"],[mt.RELATED,"tb.rulenode.originator-related"],[mt.ALARM_ORIGINATOR,"tb.rulenode.originator-alarm-originator"],[mt.ENTITY,"tb.rulenode.originator-entity"]]),pt=[b.createdTime,b.name,{value:"type",name:"tb.rulenode.profile-name",keyName:"originatorProfileName"},b.firstName,b.lastName,b.email,b.title,b.country,b.state,b.city,b.address,b.address2,b.zip,b.phone,b.label,{value:"id",name:"tb.rulenode.id",keyName:"id"},{value:"additionalInfo",name:"tb.rulenode.additional-info",keyName:"additionalInfo"}],dt=new Map([["type","profileName"],["createdTime","createdTime"],["name","name"],["firstName","firstName"],["lastName","lastName"],["email","email"],["title","title"],["country","country"],["state","state"],["city","city"],["address","address"],["address2","address2"],["zip","zip"],["phone","phone"],["label","label"],["id","id"],["additionalInfo","additionalInfo"]]);var ct;!function(e){e.CIRCLE="CIRCLE",e.POLYGON="POLYGON"}(ct||(ct={}));const ft=new Map([[ct.CIRCLE,"tb.rulenode.perimeter-circle"],[ct.POLYGON,"tb.rulenode.perimeter-polygon"]]);var gt;!function(e){e.MILLISECONDS="MILLISECONDS",e.SECONDS="SECONDS",e.MINUTES="MINUTES",e.HOURS="HOURS",e.DAYS="DAYS"}(gt||(gt={}));const yt=new Map([[gt.MILLISECONDS,"tb.rulenode.time-unit-milliseconds"],[gt.SECONDS,"tb.rulenode.time-unit-seconds"],[gt.MINUTES,"tb.rulenode.time-unit-minutes"],[gt.HOURS,"tb.rulenode.time-unit-hours"],[gt.DAYS,"tb.rulenode.time-unit-days"]]);var xt;!function(e){e.METER="METER",e.KILOMETER="KILOMETER",e.FOOT="FOOT",e.MILE="MILE",e.NAUTICAL_MILE="NAUTICAL_MILE"}(xt||(xt={}));const bt=new Map([[xt.METER,"tb.rulenode.range-unit-meter"],[xt.KILOMETER,"tb.rulenode.range-unit-kilometer"],[xt.FOOT,"tb.rulenode.range-unit-foot"],[xt.MILE,"tb.rulenode.range-unit-mile"],[xt.NAUTICAL_MILE,"tb.rulenode.range-unit-nautical-mile"]]);var ht;!function(e){e.ID="ID",e.TITLE="TITLE",e.COUNTRY="COUNTRY",e.STATE="STATE",e.CITY="CITY",e.ZIP="ZIP",e.ADDRESS="ADDRESS",e.ADDRESS2="ADDRESS2",e.PHONE="PHONE",e.EMAIL="EMAIL",e.ADDITIONAL_INFO="ADDITIONAL_INFO"}(ht||(ht={}));const vt=new Map([[ht.ID,"tb.rulenode.entity-details-id"],[ht.TITLE,"tb.rulenode.entity-details-title"],[ht.COUNTRY,"tb.rulenode.entity-details-country"],[ht.STATE,"tb.rulenode.entity-details-state"],[ht.CITY,"tb.rulenode.entity-details-city"],[ht.ZIP,"tb.rulenode.entity-details-zip"],[ht.ADDRESS,"tb.rulenode.entity-details-address"],[ht.ADDRESS2,"tb.rulenode.entity-details-address2"],[ht.PHONE,"tb.rulenode.entity-details-phone"],[ht.EMAIL,"tb.rulenode.entity-details-email"],[ht.ADDITIONAL_INFO,"tb.rulenode.entity-details-additional_info"]]);var Ct;!function(e){e.FIRST="FIRST",e.LAST="LAST",e.ALL="ALL"}(Ct||(Ct={}));const Ft=new Map([[Ct.FIRST,"tb.rulenode.first"],[Ct.LAST,"tb.rulenode.last"],[Ct.ALL,"tb.rulenode.all"]]),kt=new Map([[Ct.FIRST,"tb.rulenode.first-mode-hint"],[Ct.LAST,"tb.rulenode.last-mode-hint"],[Ct.ALL,"tb.rulenode.all-mode-hint"]]);var Lt,Tt;!function(e){e.ASC="ASC",e.DESC="DESC"}(Lt||(Lt={})),function(e){e.ATTRIBUTES="ATTRIBUTES",e.LATEST_TELEMETRY="LATEST_TELEMETRY",e.FIELDS="FIELDS"}(Tt||(Tt={}));const It=new Map([[Tt.ATTRIBUTES,"tb.rulenode.attributes"],[Tt.LATEST_TELEMETRY,"tb.rulenode.latest-telemetry"],[Tt.FIELDS,"tb.rulenode.fields"]]),Nt=new Map([[Tt.ATTRIBUTES,"tb.rulenode.add-mapped-attribute-to"],[Tt.LATEST_TELEMETRY,"tb.rulenode.add-mapped-latest-telemetry-to"],[Tt.FIELDS,"tb.rulenode.add-mapped-fields-to"]]),St=new Map([[Lt.ASC,"tb.rulenode.ascending"],[Lt.DESC,"tb.rulenode.descending"]]);var qt;!function(e){e.STANDARD="STANDARD",e.FIFO="FIFO"}(qt||(qt={}));const At=new Map([[qt.STANDARD,"tb.rulenode.sqs-queue-standard"],[qt.FIFO,"tb.rulenode.sqs-queue-fifo"]]),Mt=["anonymous","basic","cert.PEM"],Gt=new Map([["anonymous","tb.rulenode.credentials-anonymous"],["basic","tb.rulenode.credentials-basic"],["cert.PEM","tb.rulenode.credentials-pem"]]),Et=["sas","cert.PEM"],Dt=new Map([["sas","tb.rulenode.credentials-sas"],["cert.PEM","tb.rulenode.credentials-pem"]]);var Vt;!function(e){e.GET="GET",e.POST="POST",e.PUT="PUT",e.DELETE="DELETE"}(Vt||(Vt={}));const Pt=["US-ASCII","ISO-8859-1","UTF-8","UTF-16BE","UTF-16LE","UTF-16"],wt=new Map([["US-ASCII","tb.rulenode.charset-us-ascii"],["ISO-8859-1","tb.rulenode.charset-iso-8859-1"],["UTF-8","tb.rulenode.charset-utf-8"],["UTF-16BE","tb.rulenode.charset-utf-16be"],["UTF-16LE","tb.rulenode.charset-utf-16le"],["UTF-16","tb.rulenode.charset-utf-16"]]);var Rt;!function(e){e.CUSTOM="CUSTOM",e.ADD="ADD",e.SUB="SUB",e.MULT="MULT",e.DIV="DIV",e.SIN="SIN",e.SINH="SINH",e.COS="COS",e.COSH="COSH",e.TAN="TAN",e.TANH="TANH",e.ACOS="ACOS",e.ASIN="ASIN",e.ATAN="ATAN",e.ATAN2="ATAN2",e.EXP="EXP",e.EXPM1="EXPM1",e.SQRT="SQRT",e.CBRT="CBRT",e.GET_EXP="GET_EXP",e.HYPOT="HYPOT",e.LOG="LOG",e.LOG10="LOG10",e.LOG1P="LOG1P",e.CEIL="CEIL",e.FLOOR="FLOOR",e.FLOOR_DIV="FLOOR_DIV",e.FLOOR_MOD="FLOOR_MOD",e.ABS="ABS",e.MIN="MIN",e.MAX="MAX",e.POW="POW",e.SIGNUM="SIGNUM",e.RAD="RAD",e.DEG="DEG"}(Rt||(Rt={}));const Ot=new Map([[Rt.CUSTOM,{value:Rt.CUSTOM,name:"Custom Function",description:"Use this function to specify complex mathematical expression.",minArgs:1,maxArgs:16}],[Rt.ADD,{value:Rt.ADD,name:"Addition",description:"x + y",minArgs:2,maxArgs:2}],[Rt.SUB,{value:Rt.SUB,name:"Subtraction",description:"x - y",minArgs:2,maxArgs:2}],[Rt.MULT,{value:Rt.MULT,name:"Multiplication",description:"x * y",minArgs:2,maxArgs:2}],[Rt.DIV,{value:Rt.DIV,name:"Division",description:"x / y",minArgs:2,maxArgs:2}],[Rt.SIN,{value:Rt.SIN,name:"Sine",description:"Returns the trigonometric sine of an angle in radians.",minArgs:1,maxArgs:1}],[Rt.SINH,{value:Rt.SINH,name:"Hyperbolic sine",description:"Returns the hyperbolic sine of an argument.",minArgs:1,maxArgs:1}],[Rt.COS,{value:Rt.COS,name:"Cosine",description:"Returns the trigonometric cosine of an angle in radians.",minArgs:1,maxArgs:1}],[Rt.COSH,{value:Rt.COSH,name:"Hyperbolic cosine",description:"Returns the hyperbolic cosine of an argument.",minArgs:1,maxArgs:1}],[Rt.TAN,{value:Rt.TAN,name:"Tangent",description:"Returns the trigonometric tangent of an angle in radians",minArgs:1,maxArgs:1}],[Rt.TANH,{value:Rt.TANH,name:"Hyperbolic tangent",description:"Returns the hyperbolic tangent of an argument",minArgs:1,maxArgs:1}],[Rt.ACOS,{value:Rt.ACOS,name:"Arc cosine",description:"Returns the arc cosine of an argument",minArgs:1,maxArgs:1}],[Rt.ASIN,{value:Rt.ASIN,name:"Arc sine",description:"Returns the arc sine of an argument",minArgs:1,maxArgs:1}],[Rt.ATAN,{value:Rt.ATAN,name:"Arc tangent",description:"Returns the arc tangent of an argument",minArgs:1,maxArgs:1}],[Rt.ATAN2,{value:Rt.ATAN2,name:"2-argument arc tangent",description:"Returns the angle theta from the conversion of rectangular coordinates (x, y) to polar coordinates (r, theta)",minArgs:2,maxArgs:2}],[Rt.EXP,{value:Rt.EXP,name:"Exponential",description:"Returns Euler's number e raised to the power of an argument",minArgs:1,maxArgs:1}],[Rt.EXPM1,{value:Rt.EXPM1,name:"Exponential minus one",description:"Returns Euler's number e raised to the power of an argument minus one",minArgs:1,maxArgs:1}],[Rt.SQRT,{value:Rt.SQRT,name:"Square",description:"Returns the correctly rounded positive square root of an argument",minArgs:1,maxArgs:1}],[Rt.CBRT,{value:Rt.CBRT,name:"Cube root",description:"Returns the cube root of an argument",minArgs:1,maxArgs:1}],[Rt.GET_EXP,{value:Rt.GET_EXP,name:"Get exponent",description:"Returns the unbiased exponent used in the representation of an argument",minArgs:1,maxArgs:1}],[Rt.HYPOT,{value:Rt.HYPOT,name:"Square root",description:"Returns the square root of the squares of the arguments",minArgs:2,maxArgs:2}],[Rt.LOG,{value:Rt.LOG,name:"Logarithm",description:"Returns the natural logarithm of an argument",minArgs:1,maxArgs:1}],[Rt.LOG10,{value:Rt.LOG10,name:"Base 10 logarithm",description:"Returns the base 10 logarithm of an argument",minArgs:1,maxArgs:1}],[Rt.LOG1P,{value:Rt.LOG1P,name:"Logarithm of the sum",description:"Returns the natural logarithm of the sum of an argument",minArgs:1,maxArgs:1}],[Rt.CEIL,{value:Rt.CEIL,name:"Ceiling",description:"Returns the smallest (closest to negative infinity) of an argument",minArgs:1,maxArgs:1}],[Rt.FLOOR,{value:Rt.FLOOR,name:"Floor",description:"Returns the largest (closest to positive infinity) of an argument",minArgs:1,maxArgs:1}],[Rt.FLOOR_DIV,{value:Rt.FLOOR_DIV,name:"Floor division",description:"Returns the largest (closest to positive infinity) of the arguments",minArgs:2,maxArgs:2}],[Rt.FLOOR_MOD,{value:Rt.FLOOR_MOD,name:"Floor modulus",description:"Returns the floor modulus of the arguments",minArgs:2,maxArgs:2}],[Rt.ABS,{value:Rt.ABS,name:"Absolute",description:"Returns the absolute value of an argument",minArgs:1,maxArgs:1}],[Rt.MIN,{value:Rt.MIN,name:"Min",description:"Returns the smaller of the arguments",minArgs:2,maxArgs:2}],[Rt.MAX,{value:Rt.MAX,name:"Max",description:"Returns the greater of the arguments",minArgs:2,maxArgs:2}],[Rt.POW,{value:Rt.POW,name:"Raise to a power",description:"Returns the value of the first argument raised to the power of the second argument",minArgs:2,maxArgs:2}],[Rt.SIGNUM,{value:Rt.SIGNUM,name:"Sign of a real number",description:"Returns the signum function of the argument",minArgs:1,maxArgs:1}],[Rt.RAD,{value:Rt.RAD,name:"Radian",description:"Converts an angle measured in degrees to an approximately equivalent angle measured in radians",minArgs:1,maxArgs:1}],[Rt.DEG,{value:Rt.DEG,name:"Degrees",description:"Converts an angle measured in radians to an approximately equivalent angle measured in degrees.",minArgs:1,maxArgs:1}]]);var Bt,Kt,Ut;!function(e){e.MESSAGE_BODY="MESSAGE_BODY",e.MESSAGE_METADATA="MESSAGE_METADATA",e.ATTRIBUTE="ATTRIBUTE",e.TIME_SERIES="TIME_SERIES",e.CONSTANT="CONSTANT"}(Bt||(Bt={})),function(e){e.MESSAGE_BODY="MESSAGE_BODY",e.MESSAGE_METADATA="MESSAGE_METADATA",e.ATTRIBUTE="ATTRIBUTE",e.TIME_SERIES="TIME_SERIES"}(Kt||(Kt={})),function(e){e.DATA="DATA",e.METADATA="METADATA"}(Ut||(Ut={}));const _t=new Map([[Ut.DATA,"tb.rulenode.message"],[Ut.METADATA,"tb.rulenode.metadata"]]),zt=new Map([[Bt.MESSAGE_BODY,{name:"tb.rulenode.message-body-type",description:"Fetch argument value from incoming message"}],[Bt.MESSAGE_METADATA,{name:"tb.rulenode.message-metadata-type",description:"Fetch argument value from incoming message metadata"}],[Bt.ATTRIBUTE,{name:"tb.rulenode.attribute-type",description:"Fetch attribute value from database"}],[Bt.TIME_SERIES,{name:"tb.rulenode.time-series-type",description:"Fetch latest time-series value from database"}],[Bt.CONSTANT,{name:"tb.rulenode.constant-type",description:"Define constant value"}]]),Ht=new Map([[Kt.MESSAGE_BODY,{name:"tb.rulenode.message-body-type",description:"Add result to the outgoing message"}],[Kt.MESSAGE_METADATA,{name:"tb.rulenode.message-metadata-type",description:"Add result to the outgoing message metadata"}],[Kt.ATTRIBUTE,{name:"tb.rulenode.attribute-type",description:"Store result as an entity attribute in the database"}],[Kt.TIME_SERIES,{name:"tb.rulenode.time-series-type",description:"Store result as an entity time-series in the database"}]]),jt=["x","y","z","a","b","c","d","k","l","m","n","o","p","r","s","t"];var Qt,$t;!function(e){e.SHARED_SCOPE="SHARED_SCOPE",e.SERVER_SCOPE="SERVER_SCOPE",e.CLIENT_SCOPE="CLIENT_SCOPE"}(Qt||(Qt={})),function(e){e.SHARED_SCOPE="SHARED_SCOPE",e.SERVER_SCOPE="SERVER_SCOPE"}($t||($t={}));const Jt=new Map([[Qt.SHARED_SCOPE,"tb.rulenode.shared-scope"],[Qt.SERVER_SCOPE,"tb.rulenode.server-scope"],[Qt.CLIENT_SCOPE,"tb.rulenode.client-scope"]]);class Yt extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.perimeterType=ct,this.perimeterTypes=Object.keys(ct),this.perimeterTypeTranslationMap=ft,this.rangeUnits=Object.keys(xt),this.rangeUnitTranslationMap=bt,this.timeUnits=Object.keys(gt),this.timeUnitsTranslationMap=yt}configForm(){return this.geoActionConfigForm}onConfigurationSet(e){this.geoActionConfigForm=this.fb.group({latitudeKeyName:[e?e.latitudeKeyName:null,[D.required]],longitudeKeyName:[e?e.longitudeKeyName:null,[D.required]],perimeterType:[e?e.perimeterType:null,[D.required]],fetchPerimeterInfoFromMessageMetadata:[!!e&&e.fetchPerimeterInfoFromMessageMetadata,[]],perimeterKeyName:[e?e.perimeterKeyName:null,[]],centerLatitude:[e?e.centerLatitude:null,[]],centerLongitude:[e?e.centerLatitude:null,[]],range:[e?e.range:null,[]],rangeUnit:[e?e.rangeUnit:null,[]],polygonsDefinition:[e?e.polygonsDefinition:null,[]],minInsideDuration:[e?e.minInsideDuration:null,[D.required,D.min(1),D.max(2147483647)]],minInsideDurationTimeUnit:[e?e.minInsideDurationTimeUnit:null,[D.required]],minOutsideDuration:[e?e.minOutsideDuration:null,[D.required,D.min(1),D.max(2147483647)]],minOutsideDurationTimeUnit:[e?e.minOutsideDurationTimeUnit:null,[D.required]]})}validatorTriggers(){return["fetchPerimeterInfoFromMessageMetadata","perimeterType"]}updateValidators(e){const t=this.geoActionConfigForm.get("fetchPerimeterInfoFromMessageMetadata").value,n=this.geoActionConfigForm.get("perimeterType").value;t?this.geoActionConfigForm.get("perimeterKeyName").setValidators([D.required]):this.geoActionConfigForm.get("perimeterKeyName").setValidators([]),t||n!==ct.CIRCLE?(this.geoActionConfigForm.get("centerLatitude").setValidators([]),this.geoActionConfigForm.get("centerLongitude").setValidators([]),this.geoActionConfigForm.get("range").setValidators([]),this.geoActionConfigForm.get("rangeUnit").setValidators([])):(this.geoActionConfigForm.get("centerLatitude").setValidators([D.required,D.min(-90),D.max(90)]),this.geoActionConfigForm.get("centerLongitude").setValidators([D.required,D.min(-180),D.max(180)]),this.geoActionConfigForm.get("range").setValidators([D.required,D.min(0)]),this.geoActionConfigForm.get("rangeUnit").setValidators([D.required])),t||n!==ct.POLYGON?this.geoActionConfigForm.get("polygonsDefinition").setValidators([]):this.geoActionConfigForm.get("polygonsDefinition").setValidators([D.required]),this.geoActionConfigForm.get("perimeterKeyName").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("centerLatitude").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("centerLongitude").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("range").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("rangeUnit").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("polygonsDefinition").updateValueAndValidity({emitEvent:e})}}e("GpsGeoActionConfigComponent",Yt),Yt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Yt,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Yt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Yt,selector:"tb-action-node-gps-geofencing-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.latitude-key-name\n \n \n {{ \'tb.rulenode.latitude-key-name-required\' | translate }}\n \n \n \n tb.rulenode.longitude-key-name\n \n \n {{ \'tb.rulenode.longitude-key-name-required\' | translate }}\n \n \n \n tb.rulenode.perimeter-type\n \n \n {{ perimeterTypeTranslationMap.get(type) | translate }}\n \n \n \n \n {{ \'tb.rulenode.fetch-perimeter-info-from-metadata\' | translate }}\n \n \n tb.rulenode.perimeter-key-name\n \n \n {{ \'tb.rulenode.perimeter-key-name-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.circle-center-latitude\n \n \n {{ \'tb.rulenode.circle-center-latitude-required\' | translate }}\n \n \n \n tb.rulenode.circle-center-longitude\n \n \n {{ \'tb.rulenode.circle-center-longitude-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.range\n \n \n {{ \'tb.rulenode.range-required\' | translate }}\n \n \n \n tb.rulenode.range-units\n \n \n {{ rangeUnitTranslationMap.get(type) | translate }}\n \n \n \n
\n
\n
\n \n tb.rulenode.polygon-definition\n \n \n {{ \'tb.rulenode.polygon-definition-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.min-inside-duration\n \n \n {{ \'tb.rulenode.min-inside-duration-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n tb.rulenode.min-inside-duration-time-unit\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n \n tb.rulenode.min-outside-duration\n \n \n {{ \'tb.rulenode.min-outside-duration-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n tb.rulenode.min-outside-duration-time-unit\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:U.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Yt,decorators:[{type:n,args:[{selector:"tb-action-node-gps-geofencing-config",template:'
\n \n tb.rulenode.latitude-key-name\n \n \n {{ \'tb.rulenode.latitude-key-name-required\' | translate }}\n \n \n \n tb.rulenode.longitude-key-name\n \n \n {{ \'tb.rulenode.longitude-key-name-required\' | translate }}\n \n \n \n tb.rulenode.perimeter-type\n \n \n {{ perimeterTypeTranslationMap.get(type) | translate }}\n \n \n \n \n {{ \'tb.rulenode.fetch-perimeter-info-from-metadata\' | translate }}\n \n \n tb.rulenode.perimeter-key-name\n \n \n {{ \'tb.rulenode.perimeter-key-name-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.circle-center-latitude\n \n \n {{ \'tb.rulenode.circle-center-latitude-required\' | translate }}\n \n \n \n tb.rulenode.circle-center-longitude\n \n \n {{ \'tb.rulenode.circle-center-longitude-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.range\n \n \n {{ \'tb.rulenode.range-required\' | translate }}\n \n \n \n tb.rulenode.range-units\n \n \n {{ rangeUnitTranslationMap.get(type) | translate }}\n \n \n \n
\n
\n
\n \n tb.rulenode.polygon-definition\n \n \n {{ \'tb.rulenode.polygon-definition-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.min-inside-duration\n \n \n {{ \'tb.rulenode.min-inside-duration-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n tb.rulenode.min-inside-duration-time-unit\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n \n tb.rulenode.min-outside-duration\n \n \n {{ \'tb.rulenode.min-outside-duration-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n tb.rulenode.min-outside-duration-time-unit\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class Wt extends s{constructor(e,t,n,o){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=n,this.translate=o,this.tbelEnabled=X(this.store).tbelEnabled,this.scriptLanguage=d,this.changeScript=new r,this.hasScript=!0,this.testScriptLabel="tb.rulenode.test-to-string-function"}configForm(){return this.logConfigForm}onConfigurationSet(e){this.logConfigForm=this.fb.group({scriptLang:[e?e.scriptLang:d.JS,[D.required]],jsScript:[e?e.jsScript:null,[]],tbelScript:[e?e.tbelScript:null,[]]})}validatorTriggers(){return["scriptLang"]}updateValidators(e){let t=this.logConfigForm.get("scriptLang").value;t!==d.TBEL||this.tbelEnabled||(t=d.JS,this.logConfigForm.get("scriptLang").patchValue(t,{emitEvent:!1}),setTimeout((()=>{this.logConfigForm.updateValueAndValidity({emitEvent:!0})}))),this.logConfigForm.get("jsScript").setValidators(t===d.JS?[D.required]:[]),this.logConfigForm.get("jsScript").updateValueAndValidity({emitEvent:e}),this.logConfigForm.get("tbelScript").setValidators(t===d.TBEL?[D.required]:[]),this.logConfigForm.get("tbelScript").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=d.JS)),e}testScript(e){const t=this.logConfigForm.get("scriptLang").value,n=t===d.JS?"jsScript":"tbelScript",r=t===d.JS?"rulenode/log_node_script_fn":"rulenode/tbel/log_node_script_fn",o=this.logConfigForm.get(n).value;this.nodeScriptTestService.testNodeScript(o,"string",this.translate.instant("tb.rulenode.to-string"),"ToString",["msg","metadata","msgType"],this.ruleNodeId,r,t,e).subscribe((e=>{e&&(this.logConfigForm.get(n).setValue(e),this.changeScript.emit())}))}onValidate(){this.logConfigForm.get("scriptLang").value===d.JS&&this.jsFuncComponent.validateOnSubmit()}}e("LogConfigComponent",Wt),Wt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Wt,deps:[{token:G.Store},{token:E.UntypedFormBuilder},{token:Z.NodeScriptTestService},{token:j.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Wt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Wt,selector:"tb-action-node-log-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0},{propertyName:"tbelFuncComponent",first:!0,predicate:["tbelFuncComponent"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n \n \n \n
\n \n
\n
\n',dependencies:[{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ae.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","scriptLanguage","noValidate","required"]},{kind:"component",type:ie.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ie.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:se.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:me.TbScriptLangComponent,selector:"tb-script-lang",inputs:["disabled"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Wt,decorators:[{type:n,args:[{selector:"tb-action-node-log-config",template:'
\n \n \n \n \n \n \n \n
\n \n
\n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder},{type:Z.NodeScriptTestService},{type:j.TranslateService}]},propDecorators:{jsFuncComponent:[{type:o,args:["jsFuncComponent",{static:!1}]}],tbelFuncComponent:[{type:o,args:["tbelFuncComponent",{static:!1}]}]}});class Xt extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.msgCountConfigForm}onConfigurationSet(e){this.msgCountConfigForm=this.fb.group({interval:[e?e.interval:null,[D.required,D.min(1)]],telemetryPrefix:[e?e.telemetryPrefix:null,[D.required]]})}}e("MsgCountConfigComponent",Xt),Xt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Xt,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Xt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Xt,selector:"tb-action-node-msg-count-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.interval-seconds\n \n \n {{ \'tb.rulenode.interval-seconds-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-interval-seconds-message\' | translate }}\n \n \n \n tb.rulenode.output-timeseries-key-prefix\n \n \n {{ \'tb.rulenode.output-timeseries-key-prefix-required\' | translate }}\n \n \n
\n',dependencies:[{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Xt,decorators:[{type:n,args:[{selector:"tb-action-node-msg-count-config",template:'
\n \n tb.rulenode.interval-seconds\n \n \n {{ \'tb.rulenode.interval-seconds-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-interval-seconds-message\' | translate }}\n \n \n \n tb.rulenode.output-timeseries-key-prefix\n \n \n {{ \'tb.rulenode.output-timeseries-key-prefix-required\' | translate }}\n \n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class Zt extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.msgDelayConfigForm}onConfigurationSet(e){this.msgDelayConfigForm=this.fb.group({useMetadataPeriodInSecondsPatterns:[!!e&&e.useMetadataPeriodInSecondsPatterns,[]],periodInSeconds:[e?e.periodInSeconds:null,[]],periodInSecondsPattern:[e?e.periodInSecondsPattern:null,[]],maxPendingMsgs:[e?e.maxPendingMsgs:null,[D.required,D.min(1),D.max(1e5)]]})}validatorTriggers(){return["useMetadataPeriodInSecondsPatterns"]}updateValidators(e){this.msgDelayConfigForm.get("useMetadataPeriodInSecondsPatterns").value?(this.msgDelayConfigForm.get("periodInSecondsPattern").setValidators([D.required]),this.msgDelayConfigForm.get("periodInSeconds").setValidators([])):(this.msgDelayConfigForm.get("periodInSecondsPattern").setValidators([]),this.msgDelayConfigForm.get("periodInSeconds").setValidators([D.required,D.min(0)])),this.msgDelayConfigForm.get("periodInSecondsPattern").updateValueAndValidity({emitEvent:e}),this.msgDelayConfigForm.get("periodInSeconds").updateValueAndValidity({emitEvent:e})}}e("MsgDelayConfigComponent",Zt),Zt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Zt,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Zt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Zt,selector:"tb-action-node-msg-delay-config",usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.use-metadata-period-in-seconds-patterns\' | translate }}\n \n
tb.rulenode.use-metadata-period-in-seconds-patterns-hint
\n \n tb.rulenode.period-seconds\n \n \n {{ \'tb.rulenode.period-seconds-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-period-0-seconds-message\' | translate }}\n \n \n \n \n tb.rulenode.period-in-seconds-pattern\n \n \n {{ \'tb.rulenode.period-in-seconds-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n \n tb.rulenode.max-pending-messages\n \n \n {{ \'tb.rulenode.max-pending-messages-required\' | translate }}\n \n \n {{ \'tb.rulenode.max-pending-messages-range\' | translate }}\n \n \n {{ \'tb.rulenode.max-pending-messages-range\' | translate }}\n \n \n
\n',dependencies:[{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:U.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Zt,decorators:[{type:n,args:[{selector:"tb-action-node-msg-delay-config",template:'
\n \n {{ \'tb.rulenode.use-metadata-period-in-seconds-patterns\' | translate }}\n \n
tb.rulenode.use-metadata-period-in-seconds-patterns-hint
\n \n tb.rulenode.period-seconds\n \n \n {{ \'tb.rulenode.period-seconds-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-period-0-seconds-message\' | translate }}\n \n \n \n \n tb.rulenode.period-in-seconds-pattern\n \n \n {{ \'tb.rulenode.period-in-seconds-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n \n tb.rulenode.max-pending-messages\n \n \n {{ \'tb.rulenode.max-pending-messages-required\' | translate }}\n \n \n {{ \'tb.rulenode.max-pending-messages-range\' | translate }}\n \n \n {{ \'tb.rulenode.max-pending-messages-range\' | translate }}\n \n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class en extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.attributeScopes=Object.keys(m),this.telemetryTypeTranslationsMap=u}configForm(){return this.pushToCloudConfigForm}onConfigurationSet(e){this.pushToCloudConfigForm=this.fb.group({scope:[e?e.scope:null,[D.required]]})}}e("PushToCloudConfigComponent",en),en.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:en,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),en.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:en,selector:"tb-action-node-push-to-cloud-config",usesInheritance:!0,ngImport:t,template:'
\n \n attribute.attributes-scope\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n
\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:en,decorators:[{type:n,args:[{selector:"tb-action-node-push-to-cloud-config",template:'
\n \n attribute.attributes-scope\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class tn extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.attributeScopes=Object.keys(m),this.telemetryTypeTranslationsMap=u}configForm(){return this.pushToEdgeConfigForm}onConfigurationSet(e){this.pushToEdgeConfigForm=this.fb.group({scope:[e?e.scope:null,[D.required]]})}}e("PushToEdgeConfigComponent",tn),tn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:tn,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),tn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:tn,selector:"tb-action-node-push-to-edge-config",usesInheritance:!0,ngImport:t,template:'
\n \n attribute.attributes-scope\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n
\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:tn,decorators:[{type:n,args:[{selector:"tb-action-node-push-to-edge-config",template:'
\n \n attribute.attributes-scope\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class nn extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.rpcReplyConfigForm}onConfigurationSet(e){this.rpcReplyConfigForm=this.fb.group({requestIdMetaDataAttribute:[e?e.requestIdMetaDataAttribute:null,[]]})}}e("RpcReplyConfigComponent",nn),nn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:nn,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),nn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:nn,selector:"tb-action-node-rpc-reply-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.request-id-metadata-attribute\n \n \n
\n',dependencies:[{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:nn,decorators:[{type:n,args:[{selector:"tb-action-node-rpc-reply-config",template:'
\n \n tb.rulenode.request-id-metadata-attribute\n \n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class rn extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.rpcRequestConfigForm}onConfigurationSet(e){this.rpcRequestConfigForm=this.fb.group({timeoutInSeconds:[e?e.timeoutInSeconds:null,[D.required,D.min(0)]]})}}e("RpcRequestConfigComponent",rn),rn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:rn,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),rn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:rn,selector:"tb-action-node-rpc-request-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.timeout-sec\n \n \n {{ \'tb.rulenode.timeout-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-timeout-message\' | translate }}\n \n \n
\n',dependencies:[{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:rn,decorators:[{type:n,args:[{selector:"tb-action-node-rpc-request-config",template:'
\n \n tb.rulenode.timeout-sec\n \n \n {{ \'tb.rulenode.timeout-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-timeout-message\' | translate }}\n \n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class on extends h{get required(){return this.requiredValue}set required(e){this.requiredValue=xe(e)}constructor(e,t,n,r){super(e),this.store=e,this.translate=t,this.injector=n,this.fb=r,this.propagateChange=null,this.valueChangeSubscription=null}ngOnInit(){this.ngControl=this.injector.get(V),null!=this.ngControl&&(this.ngControl.valueAccessor=this),this.kvListFormGroup=this.fb.group({}),this.kvListFormGroup.addControl("keyVals",this.fb.array([]))}keyValsFormArray(){return this.kvListFormGroup.get("keyVals")}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.kvListFormGroup.disable({emitEvent:!1}):this.kvListFormGroup.enable({emitEvent:!1})}writeValue(e){this.valueChangeSubscription&&this.valueChangeSubscription.unsubscribe();const t=[];if(e)for(const n of Object.keys(e))Object.prototype.hasOwnProperty.call(e,n)&&t.push(this.fb.group({key:[n,[D.required]],value:[e[n],[D.required]]}));this.kvListFormGroup.setControl("keyVals",this.fb.array(t)),this.valueChangeSubscription=this.kvListFormGroup.valueChanges.subscribe((()=>{this.updateModel()}))}removeKeyVal(e){this.kvListFormGroup.get("keyVals").removeAt(e)}addKeyVal(){this.kvListFormGroup.get("keyVals").push(this.fb.group({key:["",[D.required]],value:["",[D.required]]}))}validate(e){const t=this.kvListFormGroup.get("keyVals").value;if(!t.length&&this.required)return{kvMapRequired:!0};if(!this.kvListFormGroup.valid)return{kvFieldsRequired:!0};if(this.uniqueKeyValuePairValidator)for(const e of t)if(e.key===e.value)return{uniqueKeyValuePair:!0};return null}updateModel(){const e=this.kvListFormGroup.get("keyVals").value;if(this.required&&!e.length||!this.kvListFormGroup.valid)this.propagateChange(null);else{const t={};e.forEach((e=>{t[e.key]=e.value})),this.propagateChange(t)}}}e("KvMapConfigOldComponent",on),on.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:on,deps:[{token:G.Store},{token:j.TranslateService},{token:t.Injector},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),on.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:on,selector:"tb-kv-map-config-old",inputs:{disabled:"disabled",uniqueKeyValuePairValidator:"uniqueKeyValuePairValidator",requiredText:"requiredText",keyText:"keyText",keyRequiredText:"keyRequiredText",valText:"valText",valRequiredText:"valRequiredText",hintText:"hintText",required:"required"},providers:[{provide:P,useExisting:a((()=>on)),multi:!0},{provide:w,useExisting:a((()=>on)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n
\n {{ keyText | translate }}\n {{ valText | translate }}\n \n
\n
\n
\n \n \n \n {{ keyRequiredText | translate }}\n \n \n \n \n \n {{ valRequiredText | translate }}\n \n \n \n
\n
\n
\n \n
\n \n
\n
\n',styles:[":host .tb-kv-map-config{margin-bottom:16px}:host .tb-kv-map-config .header{padding-left:5px;padding-right:5px;padding-bottom:5px}:host .tb-kv-map-config .header .cell{padding-left:5px;padding-right:5px;color:#757575;font-size:12px;font-weight:700;white-space:nowrap}:host .tb-kv-map-config .body{padding-left:5px;padding-right:5px;padding-bottom:0;max-height:300px;overflow:auto}:host .tb-kv-map-config .body .cell{padding-left:5px;padding-right:5px}:host .tb-kv-map-config tb-error{display:block;margin-top:-12px}\n"],dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:be.TbErrorComponent,selector:"tb-error",inputs:["noMargin","error"]},{kind:"component",type:ie.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ie.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:se.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:H.DefaultLayoutAlignDirective,selector:" [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg]",inputs:["fxLayoutAlign","fxLayoutAlign.xs","fxLayoutAlign.sm","fxLayoutAlign.md","fxLayoutAlign.lg","fxLayoutAlign.xl","fxLayoutAlign.lt-sm","fxLayoutAlign.lt-md","fxLayoutAlign.lt-lg","fxLayoutAlign.lt-xl","fxLayoutAlign.gt-xs","fxLayoutAlign.gt-sm","fxLayoutAlign.gt-md","fxLayoutAlign.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:he.DefaultShowHideDirective,selector:" [fxShow], [fxShow.print], [fxShow.xs], [fxShow.sm], [fxShow.md], [fxShow.lg], [fxShow.xl], [fxShow.lt-sm], [fxShow.lt-md], [fxShow.lt-lg], [fxShow.lt-xl], [fxShow.gt-xs], [fxShow.gt-sm], [fxShow.gt-md], [fxShow.gt-lg], [fxHide], [fxHide.print], [fxHide.xs], [fxHide.sm], [fxHide.md], [fxHide.lg], [fxHide.xl], [fxHide.lt-sm], [fxHide.lt-md], [fxHide.lt-lg], [fxHide.lt-xl], [fxHide.gt-xs], [fxHide.gt-sm], [fxHide.gt-md], [fxHide.gt-lg]",inputs:["fxShow","fxShow.print","fxShow.xs","fxShow.sm","fxShow.md","fxShow.lg","fxShow.xl","fxShow.lt-sm","fxShow.lt-md","fxShow.lt-lg","fxShow.lt-xl","fxShow.gt-xs","fxShow.gt-sm","fxShow.gt-md","fxShow.gt-lg","fxHide","fxHide.print","fxHide.xs","fxHide.sm","fxHide.md","fxHide.lg","fxHide.xl","fxHide.lt-sm","fxHide.lt-md","fxHide.lt-lg","fxHide.lt-xl","fxHide.gt-xs","fxHide.gt-sm","fxHide.gt-md","fxHide.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormControlDirective,selector:"[formControl]",inputs:["formControl","disabled","ngModel"],outputs:["ngModelChange"],exportAs:["ngForm"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormArrayName,selector:"[formArrayName]",inputs:["formArrayName"]},{kind:"pipe",type:B.AsyncPipe,name:"async"},{kind:"pipe",type:fe.SafePipe,name:"safe"},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:on,decorators:[{type:n,args:[{selector:"tb-kv-map-config-old",providers:[{provide:P,useExisting:a((()=>on)),multi:!0},{provide:w,useExisting:a((()=>on)),multi:!0}],template:'
\n
\n {{ keyText | translate }}\n {{ valText | translate }}\n \n
\n
\n
\n \n \n \n {{ keyRequiredText | translate }}\n \n \n \n \n \n {{ valRequiredText | translate }}\n \n \n \n
\n
\n
\n \n
\n \n
\n
\n',styles:[":host .tb-kv-map-config{margin-bottom:16px}:host .tb-kv-map-config .header{padding-left:5px;padding-right:5px;padding-bottom:5px}:host .tb-kv-map-config .header .cell{padding-left:5px;padding-right:5px;color:#757575;font-size:12px;font-weight:700;white-space:nowrap}:host .tb-kv-map-config .body{padding-left:5px;padding-right:5px;padding-bottom:0;max-height:300px;overflow:auto}:host .tb-kv-map-config .body .cell{padding-left:5px;padding-right:5px}:host .tb-kv-map-config tb-error{display:block;margin-top:-12px}\n"]}]}],ctorParameters:function(){return[{type:G.Store},{type:j.TranslateService},{type:t.Injector},{type:E.FormBuilder}]},propDecorators:{disabled:[{type:i}],uniqueKeyValuePairValidator:[{type:i}],requiredText:[{type:i}],keyText:[{type:i}],keyRequiredText:[{type:i}],valText:[{type:i}],valRequiredText:[{type:i}],hintText:[{type:i}],required:[{type:i}]}});class an extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.saveToCustomTableConfigForm}onConfigurationSet(e){this.saveToCustomTableConfigForm=this.fb.group({tableName:[e?e.tableName:null,[D.required,D.pattern(/.*\S.*/)]],fieldsMapping:[e?e.fieldsMapping:null,[D.required]]})}prepareOutputConfig(e){return e.tableName=e.tableName.trim(),e}}e("SaveToCustomTableConfigComponent",an),an.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:an,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),an.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:an,selector:"tb-action-node-custom-table-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.custom-table-name\n \n \n {{ \'tb.rulenode.custom-table-name-required\' | translate }}\n \n tb.rulenode.custom-table-hint\n \n \n \n \n
\n',dependencies:[{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:on,selector:"tb-kv-map-config-old",inputs:["disabled","uniqueKeyValuePairValidator","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:an,decorators:[{type:n,args:[{selector:"tb-action-node-custom-table-config",template:'
\n \n tb.rulenode.custom-table-name\n \n \n {{ \'tb.rulenode.custom-table-name-required\' | translate }}\n \n tb.rulenode.custom-table-hint\n \n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class ln extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.timeseriesConfigForm}onConfigurationSet(e){this.timeseriesConfigForm=this.fb.group({defaultTTL:[e?e.defaultTTL:null,[D.required,D.min(0)]],skipLatestPersistence:[!!e&&e.skipLatestPersistence,[]],useServerTs:[!!e&&e.useServerTs,[]]})}}e("TimeseriesConfigComponent",ln),ln.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:ln,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),ln.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:ln,selector:"tb-action-node-timeseries-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.default-ttl\n \n \n {{ \'tb.rulenode.default-ttl-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-default-ttl-message\' | translate }}\n \n \n \n {{ \'tb.rulenode.skip-latest-persistence\' | translate }}\n \n \n {{ \'tb.rulenode.use-server-ts\' | translate }}\n \n
tb.rulenode.use-server-ts-hint
\n
\n',dependencies:[{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:U.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:ln,decorators:[{type:n,args:[{selector:"tb-action-node-timeseries-config",template:'
\n \n tb.rulenode.default-ttl\n \n \n {{ \'tb.rulenode.default-ttl-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-default-ttl-message\' | translate }}\n \n \n \n {{ \'tb.rulenode.skip-latest-persistence\' | translate }}\n \n \n {{ \'tb.rulenode.use-server-ts\' | translate }}\n \n
tb.rulenode.use-server-ts-hint
\n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class sn extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.unassignCustomerConfigForm}onConfigurationSet(e){this.unassignCustomerConfigForm=this.fb.group({customerNamePattern:[e?e.customerNamePattern:null,[D.required,D.pattern(/.*\S.*/)]],customerCacheExpiration:[e?e.customerCacheExpiration:null,[D.required,D.min(0)]]})}prepareOutputConfig(e){return e.customerNamePattern=e.customerNamePattern.trim(),e}}e("UnassignCustomerConfigComponent",sn),sn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:sn,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),sn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:sn,selector:"tb-action-node-un-assign-to-customer-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.customer-name-pattern\n \n \n {{ \'tb.rulenode.customer-name-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.customer-cache-expiration\n \n \n {{ \'tb.rulenode.customer-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.customer-cache-expiration-range\' | translate }}\n \n tb.rulenode.customer-cache-expiration-hint\n \n
\n',dependencies:[{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:sn,decorators:[{type:n,args:[{selector:"tb-action-node-un-assign-to-customer-config",template:'
\n \n tb.rulenode.customer-name-pattern\n \n \n {{ \'tb.rulenode.customer-name-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.customer-cache-expiration\n \n \n {{ \'tb.rulenode.customer-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.customer-cache-expiration-range\' | translate }}\n \n tb.rulenode.customer-cache-expiration-hint\n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class mn extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.attributeScopeMap=m,this.attributeScopes=Object.keys(m),this.telemetryTypeTranslationsMap=u,this.separatorKeysCodes=[ue,pe,de]}configForm(){return this.deleteAttributesConfigForm}onConfigurationSet(e){this.deleteAttributesConfigForm=this.fb.group({scope:[e?e.scope:null,[D.required]],keys:[e?e.keys:null,[D.required]],sendAttributesDeletedNotification:[!!e&&e.sendAttributesDeletedNotification,[]],notifyDevice:[!!e&&e.notifyDevice,[]]}),this.deleteAttributesConfigForm.get("scope").valueChanges.subscribe((e=>{e!==m.SHARED_SCOPE&&this.deleteAttributesConfigForm.get("notifyDevice").patchValue(!1,{emitEvent:!1})}))}removeKey(e){const t=this.deleteAttributesConfigForm.get("keys").value,n=t.indexOf(e);n>=0&&(t.splice(n,1),this.deleteAttributesConfigForm.get("keys").patchValue(t,{emitEvent:!0}))}addKey(e){const t=e.input;let n=e.value;if((n||"").trim()){n=n.trim();let e=this.deleteAttributesConfigForm.get("keys").value;e&&-1!==e.indexOf(n)||(e||(e=[]),e.push(n),this.deleteAttributesConfigForm.get("keys").patchValue(e,{emitEvent:!0}))}t&&(t.value="")}}e("DeleteAttributesConfigComponent",mn),mn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:mn,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),mn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:mn,selector:"tb-action-node-delete-attributes-config",viewQueries:[{propertyName:"attributeChipList",first:!0,predicate:["attributeChipList"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'attribute.attributes-scope\' | translate }}\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n \n \n \n {{key}}\n close\n \n \n \n {{ \'tb.rulenode.attributes-keys-required\' | translate }}\n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.send-attributes-deleted-notification\' | translate }}\n \n
tb.rulenode.send-attributes-deleted-notification-hint
\n
\n \n {{ \'tb.rulenode.notify-device\' | translate }}\n \n
tb.rulenode.notify-device-delete-hint
\n
\n
\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:U.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:ce.MatChipGrid,selector:"mat-chip-grid",inputs:["tabIndex","disabled","placeholder","required","value","errorStateMatcher"],outputs:["change","valueChange"]},{kind:"directive",type:ce.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputFor","matChipInputAddOnBlur","matChipInputSeparatorKeyCodes","placeholder","id","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]},{kind:"directive",type:ce.MatChipRemove,selector:"[matChipRemove]"},{kind:"component",type:ce.MatChipRow,selector:"mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]",inputs:["color","disabled","disableRipple","tabIndex","editable"],outputs:["edited"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:mn,decorators:[{type:n,args:[{selector:"tb-action-node-delete-attributes-config",template:'
\n \n {{ \'attribute.attributes-scope\' | translate }}\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n \n \n \n {{key}}\n close\n \n \n \n {{ \'tb.rulenode.attributes-keys-required\' | translate }}\n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.send-attributes-deleted-notification\' | translate }}\n \n
tb.rulenode.send-attributes-deleted-notification-hint
\n
\n \n {{ \'tb.rulenode.notify-device\' | translate }}\n \n
tb.rulenode.notify-device-delete-hint
\n
\n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]},propDecorators:{attributeChipList:[{type:o,args:["attributeChipList"]}]}});class un extends h{get function(){return this.functionValue}set function(e){e&&this.functionValue!==e&&(this.functionValue=e,this.setupArgumentsFormGroup(!0))}constructor(e,t,n,r){super(e),this.store=e,this.translate=t,this.injector=n,this.fb=r,this.maxArgs=16,this.minArgs=1,this.displayArgumentName=!1,this.mathFunctionMap=Ot,this.ArgumentType=Bt,this.attributeScopeMap=Jt,this.argumentTypeMap=zt,this.arguments=Object.values(Bt),this.attributeScope=Object.values(Qt),this.propagateChange=null,this.valueChangeSubscription=[]}ngOnInit(){this.ngControl=this.injector.get(V),null!=this.ngControl&&(this.ngControl.valueAccessor=this),this.argumentsFormGroup=this.fb.group({arguments:this.fb.array([])}),this.valueChangeSubscription.push(this.argumentsFormGroup.valueChanges.subscribe((()=>{this.updateModel()}))),this.setupArgumentsFormGroup()}onDrop(e){const t=this.argumentsFormArray(),n=t.at(e.previousIndex);t.removeAt(e.previousIndex),t.insert(e.currentIndex,n),this.updateArgumentNames()}argumentsFormArray(){return this.argumentsFormGroup.get("arguments")}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.argumentsFormGroup.disable({emitEvent:!1}):(this.argumentsFormGroup.enable({emitEvent:!1}),this.argumentsFormGroup.get("arguments").controls.forEach((e=>this.updateArgumentControlValidators(e))))}ngOnDestroy(){this.valueChangeSubscription.length&&this.valueChangeSubscription.forEach((e=>e.unsubscribe()))}writeValue(e){const t=[];e&&e.forEach(((e,n)=>{t.push(this.createArgumentControl(e,n))})),this.argumentsFormGroup.setControl("arguments",this.fb.array(t),{emitEvent:!1}),this.setupArgumentsFormGroup()}removeArgument(e){this.argumentsFormGroup.get("arguments").removeAt(e),this.updateArgumentNames()}addArgument(e=!0){const t=this.argumentsFormGroup.get("arguments"),n=this.createArgumentControl(null,t.length);t.push(n,{emitEvent:e})}validate(e){return this.argumentsFormGroup.valid?null:{argumentsRequired:!0}}setupArgumentsFormGroup(e=!1){if(this.function&&(this.maxArgs=this.mathFunctionMap.get(this.function).maxArgs,this.minArgs=this.mathFunctionMap.get(this.function).minArgs,this.displayArgumentName=this.function===Rt.CUSTOM),this.argumentsFormGroup){for(this.argumentsFormGroup.get("arguments").setValidators([D.minLength(this.minArgs),D.maxLength(this.maxArgs)]),this.argumentsFormGroup.get("arguments").value.length>this.maxArgs&&(this.argumentsFormGroup.get("arguments").controls.length=this.maxArgs);this.argumentsFormGroup.get("arguments").value.length{this.updateArgumentControlValidators(n),n.get("attributeScope").updateValueAndValidity({emitEvent:!1}),n.get("defaultValue").updateValueAndValidity({emitEvent:!1})}))),n}updateArgumentControlValidators(e){const t=e.get("type").value;t===Bt.ATTRIBUTE?e.get("attributeScope").enable({emitEvent:!1}):e.get("attributeScope").disable({emitEvent:!1}),t&&t!==Bt.CONSTANT?e.get("defaultValue").enable({emitEvent:!1}):e.get("defaultValue").disable({emitEvent:!1})}updateArgumentNames(){this.argumentsFormGroup.get("arguments").controls.forEach(((e,t)=>{e.get("name").setValue(jt[t])}))}updateModel(){const e=this.argumentsFormGroup.get("arguments").value;e.length&&this.argumentsFormGroup.valid?this.propagateChange(e):this.propagateChange(null)}}e("ArgumentsMapConfigComponent",un),un.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:un,deps:[{token:G.Store},{token:j.TranslateService},{token:t.Injector},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),un.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:un,selector:"tb-arguments-map-config",inputs:{disabled:"disabled",function:"function"},providers:[{provide:P,useExisting:a((()=>un)),multi:!0},{provide:w,useExisting:a((()=>un)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n\n
\n \n \n
\n \n
\n {{argumentControl.get(\'name\').value}}.\n
\n \n tb.rulenode.argument-source-field-input\n \n \n {{ argumentTypeMap.get(argumentControl.get(\'type\').value)?.name | translate }}\n \n \n {{ argumentTypeMap.get(argument).name | translate }}\n \n {{ argumentTypeMap.get(argument).description }}\n \n \n \n \n tb.rulenode.argument-source-field-input-required\n \n \n
\n \n tb.rulenode.argument-key-field-input\n \n \n help\n \n \n tb.rulenode.argument-key-field-input-required\n \n \n \n tb.rulenode.constant-value-field-input\n \n \n tb.rulenode.constant-value-field-input-required\n \n \n \n tb.rulenode.default-value-field-input\n \n \n
\n \n tb.rulenode.attribute-scope-field-input\n \n \n {{ attributeScopeMap.get(scope) | translate }}\n \n \n \n tb.rulenode.attribute-scope-field-input-required\n \n \n
\n \n
\n
\n
\n
\n
\n
\n tb.rulenode.no-arguments-prompt\n
\n \n
\n',styles:[":host .mat-mdc-list-item.tb-argument{border:solid rgba(0,0,0,.25) 1px;border-radius:4px;padding:10px 0;margin-bottom:16px}:host .arguments-list{padding:0}\n"],dependencies:[{kind:"directive",type:B.NgClass,selector:"[ngClass]",inputs:["class","ngClass"]},{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ie.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ie.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:z.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"directive",type:Q.MatSelectTrigger,selector:"mat-select-trigger"},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:se.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"component",type:ve.MatList,selector:"mat-list",exportAs:["matList"]},{kind:"component",type:ve.MatListItem,selector:"mat-list-item, a[mat-list-item], button[mat-list-item]",inputs:["activated"],exportAs:["matListItem"]},{kind:"directive",type:Ce.CdkDropList,selector:"[cdkDropList], cdk-drop-list",inputs:["cdkDropListConnectedTo","cdkDropListData","cdkDropListOrientation","id","cdkDropListLockAxis","cdkDropListDisabled","cdkDropListSortingDisabled","cdkDropListEnterPredicate","cdkDropListSortPredicate","cdkDropListAutoScrollDisabled","cdkDropListAutoScrollStep"],outputs:["cdkDropListDropped","cdkDropListEntered","cdkDropListExited","cdkDropListSorted"],exportAs:["cdkDropList"]},{kind:"directive",type:Ce.CdkDrag,selector:"[cdkDrag]",inputs:["cdkDragData","cdkDragLockAxis","cdkDragRootElement","cdkDragBoundary","cdkDragStartDelay","cdkDragFreeDragPosition","cdkDragDisabled","cdkDragConstrainPosition","cdkDragPreviewClass","cdkDragPreviewContainer"],outputs:["cdkDragStarted","cdkDragReleased","cdkDragEnded","cdkDragEntered","cdkDragExited","cdkDragDropped","cdkDragMoved"],exportAs:["cdkDrag"]},{kind:"directive",type:Ce.CdkDragHandle,selector:"[cdkDragHandle]",inputs:["cdkDragHandleDisabled"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:H.DefaultLayoutAlignDirective,selector:" [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg]",inputs:["fxLayoutAlign","fxLayoutAlign.xs","fxLayoutAlign.sm","fxLayoutAlign.md","fxLayoutAlign.lg","fxLayoutAlign.xl","fxLayoutAlign.lt-sm","fxLayoutAlign.lt-md","fxLayoutAlign.lt-lg","fxLayoutAlign.lt-xl","fxLayoutAlign.gt-xs","fxLayoutAlign.gt-sm","fxLayoutAlign.gt-md","fxLayoutAlign.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:he.DefaultClassDirective,selector:" [ngClass], [ngClass.xs], [ngClass.sm], [ngClass.md], [ngClass.lg], [ngClass.xl], [ngClass.lt-sm], [ngClass.lt-md], [ngClass.lt-lg], [ngClass.lt-xl], [ngClass.gt-xs], [ngClass.gt-sm], [ngClass.gt-md], [ngClass.gt-lg]",inputs:["ngClass","ngClass.xs","ngClass.sm","ngClass.md","ngClass.lg","ngClass.xl","ngClass.lt-sm","ngClass.lt-md","ngClass.lt-lg","ngClass.lt-xl","ngClass.gt-xs","ngClass.gt-sm","ngClass.gt-md","ngClass.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.FormControlDirective,selector:"[formControl]",inputs:["formControl","disabled","ngModel"],outputs:["ngModelChange"],exportAs:["ngForm"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormArrayName,selector:"[formArrayName]",inputs:["formArrayName"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:un,decorators:[{type:n,args:[{selector:"tb-arguments-map-config",providers:[{provide:P,useExisting:a((()=>un)),multi:!0},{provide:w,useExisting:a((()=>un)),multi:!0}],template:'
\n\n
\n \n \n
\n \n
\n {{argumentControl.get(\'name\').value}}.\n
\n \n tb.rulenode.argument-source-field-input\n \n \n {{ argumentTypeMap.get(argumentControl.get(\'type\').value)?.name | translate }}\n \n \n {{ argumentTypeMap.get(argument).name | translate }}\n \n {{ argumentTypeMap.get(argument).description }}\n \n \n \n \n tb.rulenode.argument-source-field-input-required\n \n \n
\n \n tb.rulenode.argument-key-field-input\n \n \n help\n \n \n tb.rulenode.argument-key-field-input-required\n \n \n \n tb.rulenode.constant-value-field-input\n \n \n tb.rulenode.constant-value-field-input-required\n \n \n \n tb.rulenode.default-value-field-input\n \n \n
\n \n tb.rulenode.attribute-scope-field-input\n \n \n {{ attributeScopeMap.get(scope) | translate }}\n \n \n \n tb.rulenode.attribute-scope-field-input-required\n \n \n
\n \n
\n
\n
\n
\n
\n
\n tb.rulenode.no-arguments-prompt\n
\n \n
\n',styles:[":host .mat-mdc-list-item.tb-argument{border:solid rgba(0,0,0,.25) 1px;border-radius:4px;padding:10px 0;margin-bottom:16px}:host .arguments-list{padding:0}\n"]}]}],ctorParameters:function(){return[{type:G.Store},{type:j.TranslateService},{type:t.Injector},{type:E.FormBuilder}]},propDecorators:{disabled:[{type:i}],function:[{type:i}]}});class pn extends h{get required(){return this.requiredValue}set required(e){this.requiredValue=xe(e)}constructor(e,t,n,r){super(e),this.store=e,this.translate=t,this.injector=n,this.fb=r,this.searchText="",this.dirty=!1,this.mathOperation=[...Ot.values()],this.propagateChange=null}ngOnInit(){this.mathFunctionForm=this.fb.group({operation:[""]}),this.filteredOptions=this.mathFunctionForm.get("operation").valueChanges.pipe(Fe((e=>{let t;t="string"==typeof e&&Rt[e]?Rt[e]:null,this.updateView(t)})),ke((e=>(this.searchText=e||"",e?this._filter(e):this.mathOperation.slice()))))}_filter(e){const t=e.toLowerCase();return this.mathOperation.filter((e=>e.name.toLowerCase().includes(t)||e.value.toLowerCase().includes(t)))}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.mathFunctionForm.disable({emitEvent:!1}):this.mathFunctionForm.enable({emitEvent:!1})}mathFunctionDisplayFn(e){if(e){const t=Ot.get(e);return t.value+" | "+t.name}return""}writeValue(e){this.modelValue=e,this.mathFunctionForm.get("operation").setValue(e,{emitEvent:!1}),this.dirty=!0}updateView(e){this.modelValue!==e&&(this.modelValue=e,this.propagateChange(this.modelValue))}onFocus(){this.dirty&&(this.mathFunctionForm.get("operation").updateValueAndValidity({onlySelf:!0}),this.dirty=!1)}clear(){this.mathFunctionForm.get("operation").patchValue(""),setTimeout((()=>{this.operationInput.nativeElement.blur(),this.operationInput.nativeElement.focus()}),0)}}e("MathFunctionAutocompleteComponent",pn),pn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:pn,deps:[{token:G.Store},{token:j.TranslateService},{token:t.Injector},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),pn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:pn,selector:"tb-math-function-autocomplete",inputs:{required:"required",disabled:"disabled"},providers:[{provide:P,useExisting:a((()=>pn)),multi:!0}],viewQueries:[{propertyName:"operationInput",first:!0,predicate:["operationInput"],descendants:!0,static:!0}],usesInheritance:!0,ngImport:t,template:'\n tb.rulenode.functions-field-input\n \n \n \n \n \n \n {{ option.description }}\n \n \n \n tb.rulenode.no-option-found\n \n \n\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ie.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:Se.MatAutocomplete,selector:"mat-autocomplete",inputs:["disableRipple","hideSingleSelectionIndicator"],exportAs:["matAutocomplete"]},{kind:"directive",type:Se.MatAutocompleteTrigger,selector:"input[matAutocomplete], textarea[matAutocomplete]",exportAs:["matAutocompleteTrigger"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:B.AsyncPipe,name:"async"},{kind:"pipe",type:qe.HighlightPipe,name:"highlight"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:pn,decorators:[{type:n,args:[{selector:"tb-math-function-autocomplete",providers:[{provide:P,useExisting:a((()=>pn)),multi:!0}],template:'\n tb.rulenode.functions-field-input\n \n \n \n \n \n \n {{ option.description }}\n \n \n \n tb.rulenode.no-option-found\n \n \n\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:j.TranslateService},{type:t.Injector},{type:E.UntypedFormBuilder}]},propDecorators:{required:[{type:i}],disabled:[{type:i}],operationInput:[{type:o,args:["operationInput",{static:!0}]}]}});class dn extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.MathFunction=Rt,this.ArgumentTypeResult=Kt,this.argumentTypeResultMap=Ht,this.attributeScopeMap=Jt,this.argumentsResult=Object.values(Kt),this.attributeScopeResult=Object.values($t)}configForm(){return this.mathFunctionConfigForm}onConfigurationSet(e){this.mathFunctionConfigForm=this.fb.group({operation:[e?e.operation:null,[D.required]],arguments:[e?e.arguments:null,[D.required]],customFunction:[e?e.customFunction:"",[D.required]],result:this.fb.group({type:[e?e.result.type:null,[D.required]],attributeScope:[e?e.result.attributeScope:null,[D.required]],key:[e?e.result.key:"",[D.required]],resultValuePrecision:[e?e.result.resultValuePrecision:0],addToBody:[!!e&&e.result.addToBody],addToMetadata:[!!e&&e.result.addToMetadata]})})}updateValidators(e){const t=this.mathFunctionConfigForm.get("operation").value,n=this.mathFunctionConfigForm.get("result.type").value;t===Rt.CUSTOM?this.mathFunctionConfigForm.get("customFunction").enable({emitEvent:!1}):this.mathFunctionConfigForm.get("customFunction").disable({emitEvent:!1}),n===Kt.ATTRIBUTE?this.mathFunctionConfigForm.get("result.attributeScope").enable({emitEvent:!1}):this.mathFunctionConfigForm.get("result.attributeScope").disable({emitEvent:!1}),this.mathFunctionConfigForm.get("customFunction").updateValueAndValidity({emitEvent:e}),this.mathFunctionConfigForm.get("result.attributeScope").updateValueAndValidity({emitEvent:e})}validatorTriggers(){return["operation","result.type"]}}e("MathFunctionConfigComponent",dn),dn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:dn,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),dn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:dn,selector:"tb-action-node-math-function-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n
\n tb.rulenode.argument-tile\n \n \n
\n
\n {{\'tb.rulenode.custom-expression-field-input\' | translate }} *\n \n \n \n tb.rulenode.custom-expression-field-input-required\n \n tb.rulenode.custom-expression-field-input-hint\n \n
\n
\n tb.rulenode.result-title\n
\n \n tb.rulenode.type-field-input\n \n \n {{ argumentTypeResultMap.get(mathFunctionConfigForm.get(\'result.type\').value)?.name | translate }}\n \n \n {{ argumentTypeResultMap.get(argument).name | translate }}\n \n {{ argumentTypeResultMap.get(argument).description }}\n \n \n \n \n tb.rulenode.type-field-input-required\n \n \n
\n \n tb.rulenode.attribute-scope-field-input\n \n \n {{ attributeScopeMap.get(scope) | translate }}\n \n \n \n \n tb.rulenode.key-field-input\n \n help\n \n tb.rulenode.key-field-input-required\n \n \n
\n
\n \n tb.rulenode.number-floating-point-field-input\n \n \n \n
\n
\n \n {{\'tb.rulenode.add-to-message-field-input\' | translate }}\n \n \n {{\'tb.rulenode.add-to-metadata-field-input\' | translate}}\n \n
\n
\n
\n
\n',styles:[":host ::ng-deep .fields-group{padding:0 16px 8px;margin:10px 0;border:1px groove rgba(0,0,0,.25);border-radius:4px}:host ::ng-deep .fields-group .mat-mdc-form-field .mat-mdc-form-field-infix{width:100%}:host ::ng-deep .fields-group legend{color:#000000b3;width:-moz-fit-content;width:fit-content}:host ::ng-deep .fields-group legend+*{display:block}:host ::ng-deep .fields-group legend+*.no-margin-top{margin-top:0}\n"],dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:U.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:z.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"directive",type:Q.MatSelectTrigger,selector:"mat-select-trigger"},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:se.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:H.DefaultLayoutAlignDirective,selector:" [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg]",inputs:["fxLayoutAlign","fxLayoutAlign.xs","fxLayoutAlign.sm","fxLayoutAlign.md","fxLayoutAlign.lg","fxLayoutAlign.xl","fxLayoutAlign.lt-sm","fxLayoutAlign.lt-md","fxLayoutAlign.lt-lg","fxLayoutAlign.lt-xl","fxLayoutAlign.gt-xs","fxLayoutAlign.gt-sm","fxLayoutAlign.gt-md","fxLayoutAlign.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:E.FormGroupName,selector:"[formGroupName]",inputs:["formGroupName"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:un,selector:"tb-arguments-map-config",inputs:["disabled","function"]},{kind:"component",type:pn,selector:"tb-math-function-autocomplete",inputs:["required","disabled"]},{kind:"pipe",type:fe.SafePipe,name:"safe"},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:dn,decorators:[{type:n,args:[{selector:"tb-action-node-math-function-config",template:'
\n \n \n
\n tb.rulenode.argument-tile\n \n \n
\n
\n {{\'tb.rulenode.custom-expression-field-input\' | translate }} *\n \n \n \n tb.rulenode.custom-expression-field-input-required\n \n tb.rulenode.custom-expression-field-input-hint\n \n
\n
\n tb.rulenode.result-title\n
\n \n tb.rulenode.type-field-input\n \n \n {{ argumentTypeResultMap.get(mathFunctionConfigForm.get(\'result.type\').value)?.name | translate }}\n \n \n {{ argumentTypeResultMap.get(argument).name | translate }}\n \n {{ argumentTypeResultMap.get(argument).description }}\n \n \n \n \n tb.rulenode.type-field-input-required\n \n \n
\n \n tb.rulenode.attribute-scope-field-input\n \n \n {{ attributeScopeMap.get(scope) | translate }}\n \n \n \n \n tb.rulenode.key-field-input\n \n help\n \n tb.rulenode.key-field-input-required\n \n \n
\n
\n \n tb.rulenode.number-floating-point-field-input\n \n \n \n
\n
\n \n {{\'tb.rulenode.add-to-message-field-input\' | translate }}\n \n \n {{\'tb.rulenode.add-to-metadata-field-input\' | translate}}\n \n
\n
\n
\n
\n',styles:[":host ::ng-deep .fields-group{padding:0 16px 8px;margin:10px 0;border:1px groove rgba(0,0,0,.25);border-radius:4px}:host ::ng-deep .fields-group .mat-mdc-form-field .mat-mdc-form-field-infix{width:100%}:host ::ng-deep .fields-group legend{color:#000000b3;width:-moz-fit-content;width:fit-content}:host ::ng-deep .fields-group legend+*{display:block}:host ::ng-deep .fields-group legend+*.no-margin-top{margin-top:0}\n"]}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class cn extends h{set required(e){this.requiredValue!==e&&(this.requiredValue=e,this.updateValidators())}get required(){return this.requiredValue}constructor(e,t){super(e),this.store=e,this.fb=t,this.subscriptSizing="fixed",this.searchText="",this.dirty=!1,this.messageTypes=["POST_ATTRIBUTES_REQUEST","POST_TELEMETRY_REQUEST"],this.propagateChange=e=>{},this.messageTypeFormGroup=this.fb.group({messageType:[null,[D.maxLength(255)]]})}registerOnTouched(e){}registerOnChange(e){this.propagateChange=e}ngOnInit(){this.outputMessageTypes=this.messageTypeFormGroup.get("messageType").valueChanges.pipe(Fe((e=>{this.updateView(e)})),ke((e=>e||"")),Le((e=>this.fetchMessageTypes(e))))}writeValue(e){this.searchText="",this.modelValue=e,this.messageTypeFormGroup.get("messageType").patchValue(e,{emitEvent:!1}),this.dirty=!0}onFocus(){this.dirty&&(this.messageTypeFormGroup.get("messageType").updateValueAndValidity({onlySelf:!0,emitEvent:!0}),this.dirty=!1)}updateView(e){this.modelValue!==e&&(this.modelValue=e,this.propagateChange(this.modelValue))}displayMessageTypeFn(e){return e||void 0}fetchMessageTypes(e,t=!1){return this.searchText=e,Ge(this.messageTypes).pipe(ke((n=>n.filter((n=>t?!!e&&n===e:!e||n.toUpperCase().startsWith(e.toUpperCase()))))))}clear(){this.messageTypeFormGroup.get("messageType").patchValue(null,{emitEvent:!0}),setTimeout((()=>{this.messageTypeInput.nativeElement.blur(),this.messageTypeInput.nativeElement.focus()}),0)}setDisabledState(e){e?this.messageTypeFormGroup.disable({emitEvent:!1}):this.messageTypeFormGroup.enable({emitEvent:!1})}updateValidators(){this.messageTypeFormGroup.get("messageType").setValidators(this.required?[D.required,D.maxLength(255)]:[D.maxLength(255)]),this.messageTypeFormGroup.get("messageType").updateValueAndValidity({emitEvent:!1})}}e("OutputMessageTypeAutocompleteComponent",cn),cn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:cn,deps:[{token:G.Store},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),cn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:cn,selector:"tb-output-message-type-autocomplete",inputs:{autocompleteHint:"autocompleteHint",subscriptSizing:"subscriptSizing",required:"required"},providers:[{provide:P,useExisting:a((()=>cn)),multi:!0}],viewQueries:[{propertyName:"messageTypeInput",first:!0,predicate:["messageTypeInput"],descendants:!0,static:!0}],usesInheritance:!0,ngImport:t,template:'\n {{\'tb.rulenode.output-message-type\' | translate}}\n \n \n \n \n \n \n \n {{autocompleteHint | translate}}\n \n {{ \'tb.rulenode.output-message-type-required\' | translate }}\n \n \n {{ \'tb.rulenode.output-message-type-max-length\' | translate }}\n \n\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ie.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:z.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:Se.MatAutocomplete,selector:"mat-autocomplete",inputs:["disableRipple","hideSingleSelectionIndicator"],exportAs:["matAutocomplete"]},{kind:"directive",type:Se.MatAutocompleteTrigger,selector:"input[matAutocomplete], textarea[matAutocomplete]",exportAs:["matAutocompleteTrigger"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"pipe",type:B.AsyncPipe,name:"async"},{kind:"pipe",type:qe.HighlightPipe,name:"highlight"},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),Me([v()],cn.prototype,"required",null),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:cn,decorators:[{type:n,args:[{selector:"tb-output-message-type-autocomplete",providers:[{provide:P,useExisting:a((()=>cn)),multi:!0}],template:'\n {{\'tb.rulenode.output-message-type\' | translate}}\n \n \n \n \n \n \n \n {{autocompleteHint | translate}}\n \n {{ \'tb.rulenode.output-message-type-required\' | translate }}\n \n \n {{ \'tb.rulenode.output-message-type-max-length\' | translate }}\n \n\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.FormBuilder}]},propDecorators:{messageTypeInput:[{type:o,args:["messageTypeInput",{static:!0}]}],autocompleteHint:[{type:i}],subscriptSizing:[{type:i}],required:[{type:i}]}});class fn extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.serviceType=p.TB_RULE_ENGINE,this.deduplicationStrategie=Ct,this.deduplicationStrategies=Object.keys(this.deduplicationStrategie),this.deduplicationStrategiesTranslations=Ft}configForm(){return this.deduplicationConfigForm}onConfigurationSet(e){this.deduplicationConfigForm=this.fb.group({interval:[ee(e?.interval)?e.interval:null,[D.required,D.min(1)]],strategy:[ee(e?.strategy)?e.strategy:null,[D.required]],outMsgType:[ee(e?.outMsgType)?e.outMsgType:null,[D.required]],queueName:[ee(e?.queueName)?e.queueName:null,[D.required]],maxPendingMsgs:[ee(e?.maxPendingMsgs)?e.maxPendingMsgs:null,[D.required,D.min(1),D.max(1e3)]],maxRetries:[ee(e?.maxRetries)?e.maxRetries:null,[D.required,D.min(0),D.max(100)]]})}updateValidators(e){this.deduplicationConfigForm.get("strategy").value===this.deduplicationStrategie.ALL?(this.deduplicationConfigForm.get("outMsgType").enable({emitEvent:!1}),this.deduplicationConfigForm.get("queueName").enable({emitEvent:!1})):(this.deduplicationConfigForm.get("outMsgType").disable({emitEvent:!1}),this.deduplicationConfigForm.get("queueName").disable({emitEvent:!1})),this.deduplicationConfigForm.get("outMsgType").updateValueAndValidity({emitEvent:e}),this.deduplicationConfigForm.get("queueName").updateValueAndValidity({emitEvent:e})}validatorTriggers(){return["strategy"]}}e("DeduplicationConfigComponent",fn),fn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:fn,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),fn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:fn,selector:"tb-action-node-msg-deduplication-config",usesInheritance:!0,ngImport:t,template:"
\n \n {{'tb.rulenode.interval' | translate}}\n \n {{'tb.rulenode.interval-hint' | translate}}\n \n {{'tb.rulenode.interval-required' | translate}}\n \n \n {{'tb.rulenode.interval-min-error' | translate}}\n \n \n \n {{'tb.rulenode.strategy' | translate}}\n \n \n {{ deduplicationStrategiesTranslations.get(strategy) | translate }}\n \n \n \n {{'tb.rulenode.strategy-first-hint' | translate}}\n {{'tb.rulenode.strategy-last-hint' | translate}}\n \n {{'tb.rulenode.strategy-required' | translate}}\n \n \n
\n \n \n \n \n
\n \n \n \n
\n
Advanced settings
\n
\n
\n
\n \n \n {{'tb.rulenode.max-pending-msgs' | translate}}\n \n {{'tb.rulenode.max-pending-msgs-hint' | translate}}\n \n {{'tb.rulenode.max-pending-msgs-required' | translate}}\n \n \n {{'tb.rulenode.max-pending-msgs-max-error' | translate}}\n \n \n {{'tb.rulenode.max-pending-msgs-min-error' | translate}}\n \n \n \n {{'tb.rulenode.max-retries' | translate}}\n \n {{'tb.rulenode.max-retries-hint' | translate}}\n \n {{'tb.rulenode.max-retries-required' | translate}}\n \n \n {{'tb.rulenode.max-retries-max-error' | translate}}\n \n \n {{'tb.rulenode.max-retries-min-error' | translate}}\n \n \n \n
\n
\n",styles:[":host ::ng-deep .mat-expansion-panel.advanced-settings{border:none;box-shadow:none;padding:0}:host ::ng-deep .mat-expansion-panel.advanced-settings .mat-expansion-panel-body{padding:0}:host ::ng-deep .mat-expansion-panel:not(.mat-expanded) .mat-expansion-panel-header:not([aria-disabled=true]):hover{background:white}\n"],dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:W.QueueAutocompleteComponent,selector:"tb-queue-autocomplete",inputs:["labelText","requiredText","autocompleteHint","subscriptSizing","required","queueType","disabled"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:Ae.MatExpansionPanel,selector:"mat-expansion-panel",inputs:["disabled","expanded","hideToggle","togglePosition"],outputs:["opened","closed","expandedChange","afterExpand","afterCollapse"],exportAs:["matExpansionPanel"]},{kind:"component",type:Ae.MatExpansionPanelHeader,selector:"mat-expansion-panel-header",inputs:["tabIndex","expandedHeight","collapsedHeight"]},{kind:"directive",type:Ae.MatExpansionPanelTitle,selector:"mat-panel-title"},{kind:"directive",type:Ae.MatExpansionPanelContent,selector:"ng-template[matExpansionPanelContent]"},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutAlignDirective,selector:" [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg]",inputs:["fxLayoutAlign","fxLayoutAlign.xs","fxLayoutAlign.sm","fxLayoutAlign.md","fxLayoutAlign.lg","fxLayoutAlign.xl","fxLayoutAlign.lt-sm","fxLayoutAlign.lt-md","fxLayoutAlign.lt-lg","fxLayoutAlign.lt-xl","fxLayoutAlign.gt-xs","fxLayoutAlign.gt-sm","fxLayoutAlign.gt-md","fxLayoutAlign.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:cn,selector:"tb-output-message-type-autocomplete",inputs:["autocompleteHint","subscriptSizing","required"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:fn,decorators:[{type:n,args:[{selector:"tb-action-node-msg-deduplication-config",template:"
\n \n {{'tb.rulenode.interval' | translate}}\n \n {{'tb.rulenode.interval-hint' | translate}}\n \n {{'tb.rulenode.interval-required' | translate}}\n \n \n {{'tb.rulenode.interval-min-error' | translate}}\n \n \n \n {{'tb.rulenode.strategy' | translate}}\n \n \n {{ deduplicationStrategiesTranslations.get(strategy) | translate }}\n \n \n \n {{'tb.rulenode.strategy-first-hint' | translate}}\n {{'tb.rulenode.strategy-last-hint' | translate}}\n \n {{'tb.rulenode.strategy-required' | translate}}\n \n \n
\n \n \n \n \n
\n \n \n \n
\n
Advanced settings
\n
\n
\n
\n \n \n {{'tb.rulenode.max-pending-msgs' | translate}}\n \n {{'tb.rulenode.max-pending-msgs-hint' | translate}}\n \n {{'tb.rulenode.max-pending-msgs-required' | translate}}\n \n \n {{'tb.rulenode.max-pending-msgs-max-error' | translate}}\n \n \n {{'tb.rulenode.max-pending-msgs-min-error' | translate}}\n \n \n \n {{'tb.rulenode.max-retries' | translate}}\n \n {{'tb.rulenode.max-retries-hint' | translate}}\n \n {{'tb.rulenode.max-retries-required' | translate}}\n \n \n {{'tb.rulenode.max-retries-max-error' | translate}}\n \n \n {{'tb.rulenode.max-retries-min-error' | translate}}\n \n \n \n
\n
\n",styles:[":host ::ng-deep .mat-expansion-panel.advanced-settings{border:none;box-shadow:none;padding:0}:host ::ng-deep .mat-expansion-panel.advanced-settings .mat-expansion-panel-body{padding:0}:host ::ng-deep .mat-expansion-panel:not(.mat-expanded) .mat-expansion-panel-header:not([aria-disabled=true]):hover{background:white}\n"]}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class gn{}e("ExampleHintComponent",gn),gn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:gn,deps:[],target:t.ɵɵFactoryTarget.Component}),gn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:gn,selector:"tb-example-hint",inputs:{hintText:"hintText",popupHelpLink:"popupHelpLink"},ngImport:t,template:'
\n
{{ hintText }}
\n
\n
\n
\n',styles:[":host .space-between{display:flex;justify-content:space-between;gap:20px}:host .space-between .see-example{display:flex;flex-shrink:0}\n"],dependencies:[{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:Pe.HelpPopupComponent,selector:"[tb-help-popup], [tb-help-popup-content]",inputs:["tb-help-popup","tb-help-popup-content","trigger-text","trigger-style","tb-help-popup-placement","tb-help-popup-style","hintMode"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:gn,decorators:[{type:n,args:[{selector:"tb-example-hint",template:'
\n
{{ hintText }}
\n
\n
\n
\n',styles:[":host .space-between{display:flex;justify-content:space-between;gap:20px}:host .space-between .see-example{display:flex;flex-shrink:0}\n"]}]}],propDecorators:{hintText:[{type:i}],popupHelpLink:[{type:i}]}});class yn extends h{constructor(e,t,n,r){super(e),this.store=e,this.translate=t,this.injector=n,this.fb=r,this.propagateChange=null,this.destroy$=new Ee,this.disabled=!1,this.uniqueKeyValuePairValidator=!1,this.required=!1,this.duplicateValuesValidator=e=>e.controls.key.value===e.controls.value.value?{uniqueKeyValuePair:!0}:null,this.oneMapRequiredValidator=e=>e.get("keyVals").value.length,this.propagateNestedErrors=e=>{if(this.kvListFormGroup&&this.kvListFormGroup.get("keyVals")&&"VALID"===this.kvListFormGroup.get("keyVals")?.status)return null;const t={};if(this.kvListFormGroup&&this.kvListFormGroup.setErrors(null),e instanceof R||e instanceof O){if(e.errors)for(const n of Object.keys(e.errors))t[n]=!0;for(const n of Object.keys(e.controls)){const r=this.propagateNestedErrors(e.controls[n]);if(r&&Object.keys(r).length)for(const e of Object.keys(r))t[e]=!0}return t}if(e.errors)for(const n of Object.keys(e.errors))t[n]=!0;return te(t,{})?null:t}}ngOnInit(){this.ngControl=this.injector.get(V),null!=this.ngControl&&(this.ngControl.valueAccessor=this),this.kvListFormGroup=this.fb.group({keyVals:this.fb.array([])},{validators:[this.propagateNestedErrors,this.oneMapRequiredValidator]}),this.kvListFormGroup.valueChanges.pipe(De(this.destroy$)).subscribe((()=>{this.updateModel()}))}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}keyValsFormArray(){return this.kvListFormGroup.get("keyVals")}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.kvListFormGroup.disable({emitEvent:!1}):this.kvListFormGroup.enable({emitEvent:!1})}writeValue(e){const t=Object.keys(e).map((t=>({key:t,value:e[t]})));if(this.keyValsFormArray().length===t.length)this.keyValsFormArray().patchValue(t,{emitEvent:!1});else{const e=[];t.forEach((t=>{e.push(this.fb.group({key:[t.key,[D.required,D.pattern(/(?:.|\s)*\S(&:.|\s)*/)]],value:[t.value,[D.required,D.pattern(/(?:.|\s)*\S(&:.|\s)*/)]]},{validators:this.uniqueKeyValuePairValidator?[this.duplicateValuesValidator]:[]}))})),this.kvListFormGroup.setControl("keyVals",this.fb.array(e,this.propagateNestedErrors),{emitEvent:!1})}}removeKeyVal(e){this.keyValsFormArray().removeAt(e)}addKeyVal(){this.keyValsFormArray().push(this.fb.group({key:["",[D.required,D.pattern(/(?:.|\s)*\S(&:.|\s)*/)]],value:["",[D.required,D.pattern(/(?:.|\s)*\S(&:.|\s)*/)]]},{validators:this.uniqueKeyValuePairValidator?[this.duplicateValuesValidator]:[]}))}validate(e){const t=this.kvListFormGroup.get("keyVals").value;if(!t.length&&this.required)return{kvMapRequired:!0};if(!this.kvListFormGroup.valid)return{kvFieldsRequired:!0};if(this.uniqueKeyValuePairValidator)for(const e of t)if(e.key===e.value)return{uniqueKeyValuePair:!0};return null}updateModel(){const e=this.kvListFormGroup.get("keyVals").value;if(this.required&&!e.length||!this.kvListFormGroup.valid)this.propagateChange(null);else{const t={};e.forEach((e=>{t[e.key]=e.value})),this.propagateChange(t)}}}e("KvMapConfigComponent",yn),yn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:yn,deps:[{token:G.Store},{token:j.TranslateService},{token:t.Injector},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),yn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:yn,selector:"tb-kv-map-config",inputs:{disabled:"disabled",uniqueKeyValuePairValidator:"uniqueKeyValuePairValidator",labelText:"labelText",requiredText:"requiredText",keyText:"keyText",keyRequiredText:"keyRequiredText",valText:"valText",valRequiredText:"valRequiredText",hintText:"hintText",popupHelpLink:"popupHelpLink",required:"required"},providers:[{provide:P,useExisting:a((()=>yn)),multi:!0},{provide:w,useExisting:a((()=>yn)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n
\n
{{ labelText }}
\n
\n {{ requiredText }}\n
\n
\n tb.rulenode.map-fields-required\n
\n
\n {{ \'tb.key-val.unique-key-value-pair-error\' | translate:\n {\n valText: valText,\n keyText: keyText\n } }}\n
\n
\n
\n
\n
\n
{{ keyText }}
\n
{{ valText }}
\n
\n
\n
\n
\n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n
\n
\n \n
\n \n
\n',styles:[":host .field-space{flex:1 1 50%}:host .actions-header{width:40px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n'],dependencies:[{kind:"directive",type:B.NgClass,selector:"[ngClass]",inputs:["class","ngClass"]},{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ie.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ie.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:se.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:he.DefaultClassDirective,selector:" [ngClass], [ngClass.xs], [ngClass.sm], [ngClass.md], [ngClass.lg], [ngClass.xl], [ngClass.lt-sm], [ngClass.lt-md], [ngClass.lt-lg], [ngClass.lt-xl], [ngClass.gt-xs], [ngClass.gt-sm], [ngClass.gt-md], [ngClass.gt-lg]",inputs:["ngClass","ngClass.xs","ngClass.sm","ngClass.md","ngClass.lg","ngClass.xl","ngClass.lt-sm","ngClass.lt-md","ngClass.lt-lg","ngClass.lt-xl","ngClass.gt-xs","ngClass.gt-sm","ngClass.gt-md","ngClass.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.FormControlDirective,selector:"[formControl]",inputs:["formControl","disabled","ngModel"],outputs:["ngModelChange"],exportAs:["ngForm"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:gn,selector:"tb-example-hint",inputs:["hintText","popupHelpLink"]},{kind:"pipe",type:B.AsyncPipe,name:"async"},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),Me([v()],yn.prototype,"disabled",void 0),Me([v()],yn.prototype,"uniqueKeyValuePairValidator",void 0),Me([v()],yn.prototype,"required",void 0),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:yn,decorators:[{type:n,args:[{selector:"tb-kv-map-config",providers:[{provide:P,useExisting:a((()=>yn)),multi:!0},{provide:w,useExisting:a((()=>yn)),multi:!0}],template:'
\n
\n
{{ labelText }}
\n
\n {{ requiredText }}\n
\n
\n tb.rulenode.map-fields-required\n
\n
\n {{ \'tb.key-val.unique-key-value-pair-error\' | translate:\n {\n valText: valText,\n keyText: keyText\n } }}\n
\n
\n
\n
\n
\n
{{ keyText }}
\n
{{ valText }}
\n
\n
\n
\n
\n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n
\n
\n \n
\n \n
\n',styles:[":host .field-space{flex:1 1 50%}:host .actions-header{width:40px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n']}]}],ctorParameters:function(){return[{type:G.Store},{type:j.TranslateService},{type:t.Injector},{type:E.FormBuilder}]},propDecorators:{disabled:[{type:i}],uniqueKeyValuePairValidator:[{type:i}],labelText:[{type:i}],requiredText:[{type:i}],keyText:[{type:i}],keyRequiredText:[{type:i}],valText:[{type:i}],valRequiredText:[{type:i}],hintText:[{type:i}],popupHelpLink:[{type:i}],required:[{type:i}]}});class xn extends h{get required(){return this.requiredValue}set required(e){this.requiredValue=xe(e)}constructor(e,t){super(e),this.store=e,this.fb=t,this.directionTypes=Object.values(g),this.directionTypeTranslations=y,this.entityType=x,this.propagateChange=null}ngOnInit(){this.deviceRelationsQueryFormGroup=this.fb.group({fetchLastLevelOnly:[!1,[]],direction:[null,[D.required]],maxLevel:[null,[D.min(1)]],relationType:[null],deviceTypes:[null,[D.required]]}),this.deviceRelationsQueryFormGroup.valueChanges.subscribe((e=>{this.deviceRelationsQueryFormGroup.valid?this.propagateChange(e):this.propagateChange(null)}))}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.deviceRelationsQueryFormGroup.disable({emitEvent:!1}):this.deviceRelationsQueryFormGroup.enable({emitEvent:!1})}writeValue(e){this.deviceRelationsQueryFormGroup.reset(e,{emitEvent:!1})}}e("DeviceRelationsQueryConfigComponent",xn),xn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:xn,deps:[{token:G.Store},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),xn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:xn,selector:"tb-device-relations-query-config",inputs:{disabled:"disabled",required:"required"},providers:[{provide:P,useExisting:a((()=>xn)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }} tb.rulenode.relations-query-config-direction-suffix\n \n \n \n \n tb.rulenode.max-relation-level\n \n \n {{ \'tb.rulenode.max-relation-level-error\' | translate }}\n \n \n
\n
\n \n {{ \'alias.last-level-relation\' | translate }}\n \n
\n \n \n \n help\n \n
\n',styles:[":host .last-level-slide-toggle{margin:8px 0 24px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n'],dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:we.EntitySubTypeListComponent,selector:"tb-entity-subtype-list",inputs:["required","floatLabel","label","disabled","entityType","emptyInputPlaceholder","filledInputPlaceholder"]},{kind:"component",type:Re.RelationTypeAutocompleteComponent,selector:"tb-relation-type-autocomplete",inputs:["showLabel","additionalClasses","appearance","required","disabled","subscriptSizing"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:z.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:se.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"component",type:J.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Y.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:xn,decorators:[{type:n,args:[{selector:"tb-device-relations-query-config",providers:[{provide:P,useExisting:a((()=>xn)),multi:!0}],template:'
\n
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }} tb.rulenode.relations-query-config-direction-suffix\n \n \n \n \n tb.rulenode.max-relation-level\n \n \n {{ \'tb.rulenode.max-relation-level-error\' | translate }}\n \n \n
\n
\n \n {{ \'alias.last-level-relation\' | translate }}\n \n
\n \n \n \n help\n \n
\n',styles:[":host .last-level-slide-toggle{margin:8px 0 24px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n']}]}],ctorParameters:function(){return[{type:G.Store},{type:E.FormBuilder}]},propDecorators:{disabled:[{type:i}],required:[{type:i}]}});class bn extends h{get required(){return this.requiredValue}set required(e){this.requiredValue=xe(e)}constructor(e,t){super(e),this.store=e,this.fb=t,this.directionTypes=Object.values(g),this.directionTypeTranslations=y,this.propagateChange=null}ngOnInit(){this.relationsQueryFormGroup=this.fb.group({fetchLastLevelOnly:[!1,[]],direction:[null,[D.required]],maxLevel:[null,[D.min(1)]],filters:[null]}),this.relationsQueryFormGroup.valueChanges.subscribe((e=>{this.relationsQueryFormGroup.valid?this.propagateChange(e):this.propagateChange(null)}))}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.relationsQueryFormGroup.disable({emitEvent:!1}):this.relationsQueryFormGroup.enable({emitEvent:!1})}writeValue(e){this.relationsQueryFormGroup.reset(e||{},{emitEvent:!1})}}e("RelationsQueryConfigComponent",bn),bn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:bn,deps:[{token:G.Store},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),bn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:bn,selector:"tb-relations-query-config",inputs:{disabled:"disabled",required:"required"},providers:[{provide:P,useExisting:a((()=>bn)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n
tb.rulenode.relations-query
\n
\n
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }} tb.rulenode.relations-query-config-direction-suffix\n \n \n \n \n tb.rulenode.max-relation-level\n \n \n {{ \'tb.rulenode.max-relation-level-error\' | translate }}\n \n \n
\n
\n \n {{ \'alias.last-level-relation\' | translate }}\n \n
\n
\n
\n
relation.relation-filters
\n \n \n
\n
\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:J.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Y.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"component",type:Oe.RelationFiltersComponent,selector:"tb-relation-filters",inputs:["disabled","allowedEntityTypes"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:bn,decorators:[{type:n,args:[{selector:"tb-relations-query-config",providers:[{provide:P,useExisting:a((()=>bn)),multi:!0}],template:'
\n
tb.rulenode.relations-query
\n
\n
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }} tb.rulenode.relations-query-config-direction-suffix\n \n \n \n \n tb.rulenode.max-relation-level\n \n \n {{ \'tb.rulenode.max-relation-level-error\' | translate }}\n \n \n
\n
\n \n {{ \'alias.last-level-relation\' | translate }}\n \n
\n
\n
\n
relation.relation-filters
\n \n \n
\n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.FormBuilder}]},propDecorators:{disabled:[{type:i}],required:[{type:i}]}});class hn extends h{get required(){return this.requiredValue}set required(e){this.requiredValue=xe(e)}constructor(e,t,n,r){super(e),this.store=e,this.translate=t,this.truncate=n,this.fb=r,this.placeholder="tb.rulenode.add-message-type",this.separatorKeysCodes=[ue,pe,de],this.messageTypes=[],this.messageTypesList=[],this.searchText="",this.propagateChange=e=>{},this.messageTypeConfigForm=this.fb.group({messageType:[null]});for(const e of Object.keys(C))this.messageTypesList.push({name:F.get(C[e]),value:e})}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}ngOnInit(){this.filteredMessageTypes=this.messageTypeConfigForm.get("messageType").valueChanges.pipe(Te(""),ke((e=>e||"")),Le((e=>this.fetchMessageTypes(e))),Ie())}setDisabledState(e){this.disabled=e,this.disabled?this.messageTypeConfigForm.disable({emitEvent:!1}):this.messageTypeConfigForm.enable({emitEvent:!1})}writeValue(e){this.searchText="",this.messageTypes.length=0,e&&e.forEach((e=>{const t=this.messageTypesList.find((t=>t.value===e));t?this.messageTypes.push({name:t.name,value:t.value}):this.messageTypes.push({name:e,value:e})}))}displayMessageTypeFn(e){return e?e.name:void 0}textIsNotEmpty(e){return e&&e.length>0}createMessageType(e,t){e.preventDefault(),this.transformMessageType(t)}add(e){this.transformMessageType(e.value)}fetchMessageTypes(e){if(this.searchText=e,this.searchText&&this.searchText.length){const e=this.searchText.toUpperCase();return Ge(this.messageTypesList.filter((t=>t.name.toUpperCase().includes(e))))}return Ge(this.messageTypesList)}transformMessageType(e){if((e||"").trim()){let t;const n=e.trim(),r=this.messageTypesList.find((e=>e.name===n));t=r?{name:r.name,value:r.value}:{name:n,value:n},t&&this.addMessageType(t)}this.clear("")}remove(e){const t=this.messageTypes.indexOf(e);t>=0&&(this.messageTypes.splice(t,1),this.updateModel())}selected(e){this.addMessageType(e.option.value),this.clear("")}addMessageType(e){-1===this.messageTypes.findIndex((t=>t.value===e.value))&&(this.messageTypes.push(e),this.updateModel())}onFocus(){this.messageTypeConfigForm.get("messageType").updateValueAndValidity({onlySelf:!0,emitEvent:!0})}clear(e=""){this.messageTypeInput.nativeElement.value=e,this.messageTypeConfigForm.get("messageType").patchValue(null,{emitEvent:!0}),setTimeout((()=>{this.messageTypeInput.nativeElement.blur(),this.messageTypeInput.nativeElement.focus()}),0)}updateModel(){const e=this.messageTypes.map((e=>e.value));this.required?(this.chipList.errorState=!e.length,this.propagateChange(e.length>0?e:null)):(this.chipList.errorState=!1,this.propagateChange(e))}}e("MessageTypesConfigComponent",hn),hn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:hn,deps:[{token:G.Store},{token:j.TranslateService},{token:k.TruncatePipe},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),hn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:hn,selector:"tb-message-types-config",inputs:{required:"required",label:"label",placeholder:"placeholder",disabled:"disabled"},providers:[{provide:P,useExisting:a((()=>hn)),multi:!0}],viewQueries:[{propertyName:"chipList",first:!0,predicate:["chipList"],descendants:!0},{propertyName:"matAutocomplete",first:!0,predicate:["messageTypeAutocomplete"],descendants:!0},{propertyName:"messageTypeInput",first:!0,predicate:["messageTypeInput"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'\n {{ label }}\n \n \n {{messageType.name}}\n close\n \n \n \n \n \n \n \n \n
\n \n \n help\n \n {{ \'tb.rulenode.select-message-types-required\' | translate }}\n \n\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:z.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:se.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"component",type:Se.MatAutocomplete,selector:"mat-autocomplete",inputs:["disableRipple","hideSingleSelectionIndicator"],exportAs:["matAutocomplete"]},{kind:"directive",type:Se.MatAutocompleteTrigger,selector:"input[matAutocomplete], textarea[matAutocomplete]",exportAs:["matAutocompleteTrigger"]},{kind:"directive",type:Se.MatAutocompleteOrigin,selector:"[matAutocompleteOrigin]",exportAs:["matAutocompleteOrigin"]},{kind:"component",type:ce.MatChipGrid,selector:"mat-chip-grid",inputs:["tabIndex","disabled","placeholder","required","value","errorStateMatcher"],outputs:["change","valueChange"]},{kind:"directive",type:ce.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputFor","matChipInputAddOnBlur","matChipInputSeparatorKeyCodes","placeholder","id","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]},{kind:"directive",type:ce.MatChipRemove,selector:"[matChipRemove]"},{kind:"component",type:ce.MatChipRow,selector:"mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]",inputs:["color","disabled","disableRipple","tabIndex","editable"],outputs:["edited"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:B.AsyncPipe,name:"async"},{kind:"pipe",type:qe.HighlightPipe,name:"highlight"},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:hn,decorators:[{type:n,args:[{selector:"tb-message-types-config",providers:[{provide:P,useExisting:a((()=>hn)),multi:!0}],template:'\n {{ label }}\n \n \n {{messageType.name}}\n close\n \n \n \n \n \n \n \n \n
\n
\n tb.rulenode.no-message-types-found\n
\n \n \n {{ \'tb.rulenode.no-message-type-matching\' | translate :\n {messageType: truncate.transform(searchText, true, 6, '...')}\n }}\n \n \n \n tb.rulenode.create-new-message-type\n \n
\n
\n
\n help\n \n {{ \'tb.rulenode.select-message-types-required\' | translate }}\n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:j.TranslateService},{type:k.TruncatePipe},{type:E.FormBuilder}]},propDecorators:{required:[{type:i}],label:[{type:i}],placeholder:[{type:i}],disabled:[{type:i}],chipList:[{type:o,args:["chipList",{static:!1}]}],matAutocomplete:[{type:o,args:["messageTypeAutocomplete",{static:!1}]}],messageTypeInput:[{type:o,args:["messageTypeInput",{static:!1}]}]}});class vn extends h{get required(){return this.requiredValue}set required(e){this.requiredValue=xe(e)}constructor(e,t){super(e),this.store=e,this.fb=t,this.subscriptions=[],this.disableCertPemCredentials=!1,this.passwordFieldRequired=!0,this.allCredentialsTypes=Mt,this.credentialsTypeTranslationsMap=Gt,this.propagateChange=e=>{}}ngOnInit(){this.credentialsConfigFormGroup=this.fb.group({type:[null,[D.required]],username:[null,[]],password:[null,[]],caCert:[null,[]],caCertFileName:[null,[]],privateKey:[null,[]],privateKeyFileName:[null,[]],cert:[null,[]],certFileName:[null,[]]}),this.subscriptions.push(this.credentialsConfigFormGroup.valueChanges.subscribe((()=>{this.updateView()}))),this.subscriptions.push(this.credentialsConfigFormGroup.get("type").valueChanges.subscribe((()=>{this.credentialsTypeChanged()})))}ngOnChanges(e){for(const t of Object.keys(e)){const n=e[t];if(!n.firstChange&&n.currentValue!==n.previousValue&&n.currentValue&&"disableCertPemCredentials"===t){"cert.PEM"===this.credentialsConfigFormGroup.get("type").value&&setTimeout((()=>{this.credentialsConfigFormGroup.get("type").patchValue("anonymous",{emitEvent:!0})}))}}}ngOnDestroy(){this.subscriptions.forEach((e=>e.unsubscribe()))}writeValue(e){ee(e)&&(this.credentialsConfigFormGroup.reset(e,{emitEvent:!1}),this.updateValidators())}setDisabledState(e){e?this.credentialsConfigFormGroup.disable({emitEvent:!1}):(this.credentialsConfigFormGroup.enable({emitEvent:!1}),this.updateValidators())}updateView(){let e=this.credentialsConfigFormGroup.value;const t=e.type;switch(t){case"anonymous":e={type:t};break;case"basic":e={type:t,username:e.username,password:e.password};break;case"cert.PEM":delete e.username}this.propagateChange(e)}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}validate(e){return this.credentialsConfigFormGroup.valid?null:{credentialsConfig:{valid:!1}}}credentialsTypeChanged(){this.credentialsConfigFormGroup.patchValue({username:null,password:null,caCert:null,caCertFileName:null,privateKey:null,privateKeyFileName:null,cert:null,certFileName:null}),this.updateValidators()}updateValidators(e=!1){const t=this.credentialsConfigFormGroup.get("type").value;switch(e&&this.credentialsConfigFormGroup.reset({type:t},{emitEvent:!1}),this.credentialsConfigFormGroup.setValidators([]),this.credentialsConfigFormGroup.get("username").setValidators([]),this.credentialsConfigFormGroup.get("password").setValidators([]),t){case"anonymous":break;case"basic":this.credentialsConfigFormGroup.get("username").setValidators([D.required]),this.credentialsConfigFormGroup.get("password").setValidators(this.passwordFieldRequired?[D.required]:[]);break;case"cert.PEM":this.credentialsConfigFormGroup.setValidators([this.requiredFilesSelected(D.required,[["caCert","caCertFileName"],["privateKey","privateKeyFileName","cert","certFileName"]])])}this.credentialsConfigFormGroup.get("username").updateValueAndValidity({emitEvent:e}),this.credentialsConfigFormGroup.get("password").updateValueAndValidity({emitEvent:e}),this.credentialsConfigFormGroup.updateValueAndValidity({emitEvent:e})}requiredFilesSelected(e,t=null){return n=>{t||(t=[Object.keys(n.controls)]);return n?.controls&&t.some((t=>t.every((t=>!e(n.controls[t])))))?null:{notAllRequiredFilesSelected:!0}}}}e("CredentialsConfigComponent",vn),vn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:vn,deps:[{token:G.Store},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),vn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:vn,selector:"tb-credentials-config",inputs:{required:"required",disableCertPemCredentials:"disableCertPemCredentials",passwordFieldRequired:"passwordFieldRequired"},providers:[{provide:P,useExisting:a((()=>vn)),multi:!0},{provide:w,useExisting:a((()=>vn)),multi:!0}],usesInheritance:!0,usesOnChanges:!0,ngImport:t,template:'
\n \n \n tb.rulenode.credentials\n \n {{ credentialsTypeTranslationsMap.get(credentialsConfigFormGroup.get(\'type\').value) | translate }}\n \n \n \n \n tb.rulenode.credentials-type\n \n \n {{ credentialsTypeTranslationsMap.get(credentialsType) | translate }}\n \n \n \n {{ \'tb.rulenode.credentials-type-required\' | translate }}\n \n \n
\n \n \n \n \n tb.rulenode.username\n \n \n {{ \'tb.rulenode.username-required\' | translate }}\n \n \n \n tb.rulenode.password\n \n \n \n {{ \'tb.rulenode.password-required\' | translate }}\n \n \n \n \n
{{ \'tb.rulenode.credentials-pem-hint\' | translate }}
\n \n \n \n \n \n \n \n tb.rulenode.private-key-password\n \n \n \n
\n
\n
\n
\n
\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:B.NgSwitch,selector:"[ngSwitch]",inputs:["ngSwitch"]},{kind:"directive",type:B.NgSwitchCase,selector:"[ngSwitchCase]",inputs:["ngSwitchCase"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:z.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:Ae.MatExpansionPanel,selector:"mat-expansion-panel",inputs:["disabled","expanded","hideToggle","togglePosition"],outputs:["opened","closed","expandedChange","afterExpand","afterCollapse"],exportAs:["matExpansionPanel"]},{kind:"component",type:Ae.MatExpansionPanelHeader,selector:"mat-expansion-panel-header",inputs:["tabIndex","expandedHeight","collapsedHeight"]},{kind:"directive",type:Ae.MatExpansionPanelTitle,selector:"mat-panel-title"},{kind:"directive",type:Ae.MatExpansionPanelDescription,selector:"mat-panel-description"},{kind:"directive",type:Ae.MatExpansionPanelContent,selector:"ng-template[matExpansionPanelContent]"},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:Be.FileInputComponent,selector:"tb-file-input",inputs:["label","accept","noFileText","inputId","allowedExtensions","dropLabel","contentConvertFunction","required","requiredAsError","disabled","existingFileName","readAsBinary","workFromFileObj","multipleFile"],outputs:["fileNameChanged"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Ke.TogglePasswordComponent,selector:"tb-toggle-password"},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:vn,decorators:[{type:n,args:[{selector:"tb-credentials-config",providers:[{provide:P,useExisting:a((()=>vn)),multi:!0},{provide:w,useExisting:a((()=>vn)),multi:!0}],template:'
\n \n \n tb.rulenode.credentials\n \n {{ credentialsTypeTranslationsMap.get(credentialsConfigFormGroup.get(\'type\').value) | translate }}\n \n \n \n \n tb.rulenode.credentials-type\n \n \n {{ credentialsTypeTranslationsMap.get(credentialsType) | translate }}\n \n \n \n {{ \'tb.rulenode.credentials-type-required\' | translate }}\n \n \n
\n \n \n \n \n tb.rulenode.username\n \n \n {{ \'tb.rulenode.username-required\' | translate }}\n \n \n \n tb.rulenode.password\n \n \n \n {{ \'tb.rulenode.password-required\' | translate }}\n \n \n \n \n
{{ \'tb.rulenode.credentials-pem-hint\' | translate }}
\n \n \n \n \n \n \n \n tb.rulenode.private-key-password\n \n \n \n
\n
\n
\n
\n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.FormBuilder}]},propDecorators:{required:[{type:i}],disableCertPemCredentials:[{type:i}],passwordFieldRequired:[{type:i}]}});class Cn{constructor(e,t){this.fb=e,this.translate=t,this.propagateChange=e=>{},this.destroy$=new Ee,this.selectOptions=[];for(const e of _t.keys())this.selectOptions.push({value:e,name:this.translate.instant(_t.get(e))})}ngOnInit(){this.chipControlGroup=this.fb.group({chipControl:[null,[]]}),this.chipControlGroup.get("chipControl").valueChanges.pipe(Ne(this.destroy$)).subscribe((e=>{e&&this.propagateChange(e)}))}writeValue(e){this.chipControlGroup.get("chipControl").patchValue(e,{emitEvent:!1})}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){e?this.chipControlGroup.disable({emitEvent:!1}):this.chipControlGroup.enable({emitEvent:!1})}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}}e("MsgMetadataChipComponent",Cn),Cn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Cn,deps:[{token:E.FormBuilder},{token:j.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Cn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Cn,selector:"tb-msg-metadata-chip",inputs:{labelText:"labelText"},providers:[{provide:P,useExisting:a((()=>Cn)),multi:!0}],ngImport:t,template:'
\n
{{ labelText }}
\n \n {{ option.name }}\n \n
\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"component",type:ce.MatChipListbox,selector:"mat-chip-listbox",inputs:["tabIndex","multiple","aria-orientation","selectable","compareWith","required","hideSingleSelectionIndicator","value"],outputs:["change"]},{kind:"component",type:ce.MatChipOption,selector:"mat-basic-chip-option, [mat-basic-chip-option], mat-chip-option, [mat-chip-option]",inputs:["color","disabled","disableRipple","tabIndex","selectable","selected"],outputs:["selectionChange"]},{kind:"directive",type:he.DefaultClassDirective,selector:" [ngClass], [ngClass.xs], [ngClass.sm], [ngClass.md], [ngClass.lg], [ngClass.xl], [ngClass.lt-sm], [ngClass.lt-md], [ngClass.lt-lg], [ngClass.lt-xl], [ngClass.gt-xs], [ngClass.gt-sm], [ngClass.gt-md], [ngClass.gt-lg]",inputs:["ngClass","ngClass.xs","ngClass.sm","ngClass.md","ngClass.lg","ngClass.xl","ngClass.lt-sm","ngClass.lt-md","ngClass.lt-lg","ngClass.lt-xl","ngClass.gt-xs","ngClass.gt-sm","ngClass.gt-md","ngClass.gt-lg"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Cn,decorators:[{type:n,args:[{selector:"tb-msg-metadata-chip",providers:[{provide:P,useExisting:a((()=>Cn)),multi:!0}],template:'
\n
{{ labelText }}
\n \n {{ option.name }}\n \n
\n'}]}],ctorParameters:function(){return[{type:E.FormBuilder},{type:j.TranslateService}]},propDecorators:{labelText:[{type:i}]}});class Fn extends h{constructor(e,t,n,r){super(e),this.store=e,this.translate=t,this.injector=n,this.fb=r,this.destroy$=new Ee,this.sourceFieldSubcritption=[],this.propagateChange=null,this.disabled=!1,this.required=!1,this.oneMapRequiredValidator=e=>e.get("keyVals").value.length,this.propagateNestedErrors=e=>{if(this.svListFormGroup&&this.svListFormGroup.get("keyVals")&&"VALID"===this.svListFormGroup.get("keyVals")?.status)return null;const t={};if(this.svListFormGroup&&this.svListFormGroup.setErrors(null),e instanceof R||e instanceof O){if(e.errors)for(const n of Object.keys(e.errors))t[n]=!0;for(const n of Object.keys(e.controls)){const r=this.propagateNestedErrors(e.controls[n]);if(r&&Object.keys(r).length)for(const e of Object.keys(r))t[e]=!0}return t}if(e.errors)for(const n of Object.keys(e.errors))t[n]=!0;return te(t,{})?null:t}}ngOnInit(){this.ngControl=this.injector.get(V),null!=this.ngControl&&(this.ngControl.valueAccessor=this),this.svListFormGroup=this.fb.group({keyVals:this.fb.array([])},{validators:[this.propagateNestedErrors,this.oneMapRequiredValidator]}),this.svListFormGroup.valueChanges.pipe(Ne(this.destroy$)).subscribe((()=>{this.updateModel()}))}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}keyValsFormArray(){return this.svListFormGroup.get("keyVals")}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.svListFormGroup.disable({emitEvent:!1}):this.svListFormGroup.enable({emitEvent:!1})}writeValue(e){const t=Object.keys(e).map((t=>({key:t,value:e[t]})));if(this.keyValsFormArray().length===t.length)this.keyValsFormArray().patchValue(t,{emitEvent:!1});else{const e=[];t.forEach((t=>{e.push(this.fb.group({key:[t.key,[D.required]],value:[t.value,[D.required,D.pattern(/(?:.|\s)*\S(&:.|\s)*/)]]}))})),this.svListFormGroup.setControl("keyVals",this.fb.array(e,this.propagateNestedErrors),{emitEvent:!1});for(const e of this.keyValsFormArray().controls)this.keyChangeSubscribe(e)}}filterSelectOptions(e){const t=[];for(const e of this.svListFormGroup.get("keyVals").value){const n=this.selectOptions.find((t=>t.value===e.key));n&&t.push(n)}const n=[];for(const r of this.selectOptions)ee(t.find((e=>e.value===r.value)))&&r.value!==e?.get("key").value||n.push(r);return n}removeKeyVal(e){this.keyValsFormArray().removeAt(e),this.sourceFieldSubcritption[e].unsubscribe(),this.sourceFieldSubcritption.splice(e,1)}addKeyVal(){this.keyValsFormArray().push(this.fb.group({key:["",[D.required]],value:["",[D.required,D.pattern(/(?:.|\s)*\S(&:.|\s)*/)]]})),this.keyChangeSubscribe(this.keyValsFormArray().at(this.keyValsFormArray().length-1))}keyChangeSubscribe(e){this.sourceFieldSubcritption.push(e.get("key").valueChanges.pipe(Ne(this.destroy$)).subscribe((t=>{const n=dt.get(t);e.get("value").patchValue(this.targetKeyPrefix+n[0].toUpperCase()+n.slice(1))})))}validate(e){return!this.svListFormGroup.get("keyVals").value.length&&this.required?{svMapRequired:!0}:this.svListFormGroup.valid?null:{svFieldsRequired:!0}}updateModel(){const e=this.svListFormGroup.get("keyVals").value;if(this.required&&!e.length||!this.svListFormGroup.valid)this.propagateChange(null);else{const t={};e.forEach((e=>{t[e.key]=e.value})),this.propagateChange(t)}}}e("SvMapConfigComponent",Fn),Fn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Fn,deps:[{token:G.Store},{token:j.TranslateService},{token:t.Injector},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Fn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Fn,selector:"tb-sv-map-config",inputs:{selectOptions:"selectOptions",disabled:"disabled",labelText:"labelText",requiredText:"requiredText",targetKeyPrefix:"targetKeyPrefix",selectText:"selectText",selectRequiredText:"selectRequiredText",valText:"valText",valRequiredText:"valRequiredText",hintText:"hintText",popupHelpLink:"popupHelpLink",required:"required"},providers:[{provide:P,useExisting:a((()=>Fn)),multi:!0},{provide:w,useExisting:a((()=>Fn)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n
\n
{{ labelText }}
\n
\n tb.rulenode.map-fields-required\n
\n
\n {{ requiredText }}\n
\n
\n
\n
\n
\n
{{ selectText }}
\n
{{ valText }}
\n
\n
\n
\n
\n \n \n \n {{option.name}}\n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n
\n
\n \n
\n \n
\n',styles:[":host .field-space{flex:1 1 50%}:host .actions-header{width:40px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n'],dependencies:[{kind:"directive",type:B.NgClass,selector:"[ngClass]",inputs:["class","ngClass"]},{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ie.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ie.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:se.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:he.DefaultClassDirective,selector:" [ngClass], [ngClass.xs], [ngClass.sm], [ngClass.md], [ngClass.lg], [ngClass.xl], [ngClass.lt-sm], [ngClass.lt-md], [ngClass.lt-lg], [ngClass.lt-xl], [ngClass.gt-xs], [ngClass.gt-sm], [ngClass.gt-md], [ngClass.gt-lg]",inputs:["ngClass","ngClass.xs","ngClass.sm","ngClass.md","ngClass.lg","ngClass.xl","ngClass.lt-sm","ngClass.lt-md","ngClass.lt-lg","ngClass.lt-xl","ngClass.gt-xs","ngClass.gt-sm","ngClass.gt-md","ngClass.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormControlDirective,selector:"[formControl]",inputs:["formControl","disabled","ngModel"],outputs:["ngModelChange"],exportAs:["ngForm"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:gn,selector:"tb-example-hint",inputs:["hintText","popupHelpLink"]},{kind:"pipe",type:B.AsyncPipe,name:"async"},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),Me([v()],Fn.prototype,"disabled",void 0),Me([v()],Fn.prototype,"required",void 0),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Fn,decorators:[{type:n,args:[{selector:"tb-sv-map-config",providers:[{provide:P,useExisting:a((()=>Fn)),multi:!0},{provide:w,useExisting:a((()=>Fn)),multi:!0}],template:'
\n
\n
{{ labelText }}
\n
\n tb.rulenode.map-fields-required\n
\n
\n {{ requiredText }}\n
\n
\n
\n
\n
\n
{{ selectText }}
\n
{{ valText }}
\n
\n
\n
\n
\n \n \n \n {{option.name}}\n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n
\n
\n \n
\n \n
\n',styles:[":host .field-space{flex:1 1 50%}:host .actions-header{width:40px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n']}]}],ctorParameters:function(){return[{type:G.Store},{type:j.TranslateService},{type:t.Injector},{type:E.FormBuilder}]},propDecorators:{selectOptions:[{type:i}],disabled:[{type:i}],labelText:[{type:i}],requiredText:[{type:i}],targetKeyPrefix:[{type:i}],selectText:[{type:i}],selectRequiredText:[{type:i}],valText:[{type:i}],valRequiredText:[{type:i}],hintText:[{type:i}],popupHelpLink:[{type:i}],required:[{type:i}]}});class kn extends h{get required(){return this.requiredValue}set required(e){this.requiredValue=xe(e)}constructor(e,t){super(e),this.store=e,this.fb=t,this.directionTypes=Object.keys(g),this.directionTypeTranslations=y,this.propagateChange=null}ngOnInit(){this.relationsQueryFormGroup=this.fb.group({fetchLastLevelOnly:[!1,[]],direction:[null,[D.required]],maxLevel:[null,[]],filters:[null]}),this.relationsQueryFormGroup.valueChanges.subscribe((e=>{this.relationsQueryFormGroup.valid?this.propagateChange(e):this.propagateChange(null)}))}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.relationsQueryFormGroup.disable({emitEvent:!1}):this.relationsQueryFormGroup.enable({emitEvent:!1})}writeValue(e){this.relationsQueryFormGroup.reset(e||{},{emitEvent:!1})}}e("RelationsQueryConfigOldComponent",kn),kn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:kn,deps:[{token:G.Store},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),kn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:kn,selector:"tb-relations-query-config-old",inputs:{disabled:"disabled",required:"required"},providers:[{provide:P,useExisting:a((()=>kn)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'alias.last-level-relation\' | translate }}\n \n
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n \n tb.rulenode.max-relation-level\n \n \n
\n
relation.relation-filters
\n \n
\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"component",type:U.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Oe.RelationFiltersComponent,selector:"tb-relation-filters",inputs:["disabled","allowedEntityTypes"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:kn,decorators:[{type:n,args:[{selector:"tb-relations-query-config-old",providers:[{provide:P,useExisting:a((()=>kn)),multi:!0}],template:'
\n \n {{ \'alias.last-level-relation\' | translate }}\n \n
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n \n tb.rulenode.max-relation-level\n \n \n
\n
relation.relation-filters
\n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.FormBuilder}]},propDecorators:{disabled:[{type:i}],required:[{type:i}]}});class Ln{constructor(e,t){this.translate=e,this.fb=t,this.propagateChange=e=>{},this.destroy$=new Ee,this.separatorKeysCodes=[ue,pe,de],this.onTouched=()=>{}}ngOnInit(){this.attributeControlGroup=this.fb.group({clientAttributeNames:[null,[]],sharedAttributeNames:[null,[]],serverAttributeNames:[null,[]],latestTsKeyNames:[null,[]],getLatestValueWithTs:[!1,[]]},{validators:this.atLeastOne(D.required,["clientAttributeNames","sharedAttributeNames","serverAttributeNames","latestTsKeyNames"])}),this.attributeControlGroup.valueChanges.pipe(Ne(this.destroy$)).subscribe((e=>{this.propagateChange(e)}))}validate(){return this.attributeControlGroup.valid?null:{atLeastOneRequired:!0}}atLeastOne(e,t=null){return n=>{t||(t=Object.keys(n.controls));return n?.controls&&t.some((t=>!e(n.controls[t])))?null:{atLeastOne:!0}}}writeValue(e){this.attributeControlGroup.setValue(e,{emitEvent:!1})}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){this.onTouched=e}setDisabledState(e){e?this.attributeControlGroup.disable({emitEvent:!1}):this.attributeControlGroup.enable({emitEvent:!1})}ngOnDestroy(){this.destroy$.next(null),this.destroy$.complete()}}e("SelectAttributesComponent",Ln),Ln.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Ln,deps:[{token:j.TranslateService},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Ln.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Ln,selector:"tb-select-attributes",inputs:{popupHelpLink:"popupHelpLink"},providers:[{provide:P,useExisting:a((()=>Ln)),multi:!0},{provide:w,useExisting:Ln,multi:!0}],ngImport:t,template:'
\n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n \n {{ \'tb.rulenode.fetch-latest-telemetry-with-timestamp\' | translate }}\n \n
\n
\n\n\n help\n\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n'],dependencies:[{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:B.NgTemplateOutlet,selector:"[ngTemplateOutlet]",inputs:["ngTemplateOutletContext","ngTemplateOutlet","ngTemplateOutletInjector"]},{kind:"component",type:Ue.StringItemsListComponent,selector:"tb-string-items-list",inputs:["required","disabled","label","placeholder","hint","requiredText","floatLabel","appearance","editable","subscriptSizing","predefinedValues"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:z.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"directive",type:se.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"component",type:J.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:Y.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"component",type:gn,selector:"tb-example-hint",inputs:["hintText","popupHelpLink"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Ln,decorators:[{type:n,args:[{selector:"tb-select-attributes",providers:[{provide:P,useExisting:a((()=>Ln)),multi:!0},{provide:w,useExisting:Ln,multi:!0}],template:'
\n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n \n {{ \'tb.rulenode.fetch-latest-telemetry-with-timestamp\' | translate }}\n \n
\n
\n\n\n help\n\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n']}]}],ctorParameters:function(){return[{type:j.TranslateService},{type:E.FormBuilder}]},propDecorators:{popupHelpLink:[{type:i}]}});class Tn extends h{constructor(e,t){super(e),this.store=e,this.fb=t,this.propagateChange=null,this.destroy$=new Ee,this.alarmStatus=L,this.alarmStatusTranslations=T}ngOnInit(){this.alarmStatusGroup=this.fb.group({alarmStatus:[null,[]]}),this.alarmStatusGroup.get("alarmStatus").valueChanges.pipe(Ne(this.destroy$)).subscribe((e=>{this.propagateChange(e)}))}setDisabledState(e){e?this.alarmStatusGroup.disable({emitEvent:!1}):this.alarmStatusGroup.enable({emitEvent:!1})}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}writeValue(e){this.alarmStatusGroup.get("alarmStatus").patchValue(e,{emitEvent:!1})}}e("AlarmStatusSelectComponent",Tn),Tn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Tn,deps:[{token:G.Store},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Tn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Tn,selector:"tb-alarm-status-select",providers:[{provide:P,useExisting:a((()=>Tn)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n
\n \n {{ alarmStatusTranslations.get(alarmStatus.ACTIVE_UNACK) | translate }}\n \n \n {{ alarmStatusTranslations.get(alarmStatus.ACTIVE_ACK) | translate }}\n \n
\n
\n \n {{ alarmStatusTranslations.get(alarmStatus.CLEARED_UNACK) | translate }}\n \n \n {{ alarmStatusTranslations.get(alarmStatus.ACTIVE_UNACK) | translate }}\n \n
\n
\n
\n',styles:[":host .chip-listbox{max-width:460px;width:100%}:host .chip-listbox .toggle-column{display:flex;flex:1 1 100%;gap:8px}:host .chip-listbox .option{margin:0}@media screen and (max-width: 959px){:host .chip-listbox{max-width:360px}:host .chip-listbox .toggle-column{flex-direction:column}}:host ::ng-deep .chip-listbox .mdc-evolution-chip-set__chips{gap:8px}:host ::ng-deep .chip-listbox .option button{flex-basis:100%;justify-content:start}:host ::ng-deep .chip-listbox .option .mdc-evolution-chip__graphic{flex-grow:0}\n"],dependencies:[{kind:"component",type:ce.MatChipListbox,selector:"mat-chip-listbox",inputs:["tabIndex","multiple","aria-orientation","selectable","compareWith","required","hideSingleSelectionIndicator","value"],outputs:["change"]},{kind:"component",type:ce.MatChipOption,selector:"mat-basic-chip-option, [mat-basic-chip-option], mat-chip-option, [mat-chip-option]",inputs:["color","disabled","disableRipple","tabIndex","selectable","selected"],outputs:["selectionChange"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutAlignDirective,selector:" [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg]",inputs:["fxLayoutAlign","fxLayoutAlign.xs","fxLayoutAlign.sm","fxLayoutAlign.md","fxLayoutAlign.lg","fxLayoutAlign.xl","fxLayoutAlign.lt-sm","fxLayoutAlign.lt-md","fxLayoutAlign.lt-lg","fxLayoutAlign.lt-xl","fxLayoutAlign.gt-xs","fxLayoutAlign.gt-sm","fxLayoutAlign.gt-md","fxLayoutAlign.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Tn,decorators:[{type:n,args:[{selector:"tb-alarm-status-select",providers:[{provide:P,useExisting:a((()=>Tn)),multi:!0}],template:'
\n \n
\n \n {{ alarmStatusTranslations.get(alarmStatus.ACTIVE_UNACK) | translate }}\n \n \n {{ alarmStatusTranslations.get(alarmStatus.ACTIVE_ACK) | translate }}\n \n
\n
\n \n {{ alarmStatusTranslations.get(alarmStatus.CLEARED_UNACK) | translate }}\n \n \n {{ alarmStatusTranslations.get(alarmStatus.ACTIVE_UNACK) | translate }}\n \n
\n
\n
\n',styles:[":host .chip-listbox{max-width:460px;width:100%}:host .chip-listbox .toggle-column{display:flex;flex:1 1 100%;gap:8px}:host .chip-listbox .option{margin:0}@media screen and (max-width: 959px){:host .chip-listbox{max-width:360px}:host .chip-listbox .toggle-column{flex-direction:column}}:host ::ng-deep .chip-listbox .mdc-evolution-chip-set__chips{gap:8px}:host ::ng-deep .chip-listbox .option button{flex-basis:100%;justify-content:start}:host ::ng-deep .chip-listbox .option .mdc-evolution-chip__graphic{flex-grow:0}\n"]}]}],ctorParameters:function(){return[{type:G.Store},{type:E.FormBuilder}]}});class In{}e("RulenodeCoreConfigCommonModule",In),In.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:In,deps:[],target:t.ɵɵFactoryTarget.NgModule}),In.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.9",ngImport:t,type:In,declarations:[yn,xn,bn,hn,vn,un,pn,cn,on,Cn,Fn,kn,Ln,Tn,gn],imports:[K,I,Ve],exports:[yn,xn,bn,hn,vn,un,pn,cn,on,Cn,Fn,kn,Ln,Tn,gn]}),In.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:In,imports:[K,I,Ve]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:In,decorators:[{type:l,args:[{declarations:[yn,xn,bn,hn,vn,un,pn,cn,on,Cn,Fn,kn,Ln,Tn,gn],imports:[K,I,Ve],exports:[yn,xn,bn,hn,vn,un,pn,cn,on,Cn,Fn,kn,Ln,Tn,gn]}]}]});class Nn{}e("RuleNodeCoreConfigActionModule",Nn),Nn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Nn,deps:[],target:t.ɵɵFactoryTarget.NgModule}),Nn.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.9",ngImport:t,type:Nn,declarations:[mn,tt,ln,rn,Wt,et,rt,ot,at,Zt,it,st,Yt,Xt,nn,an,sn,nt,lt,tn,en,dn,fn],imports:[K,I,Ve,In],exports:[mn,tt,ln,rn,Wt,et,rt,ot,at,Zt,it,st,Yt,Xt,nn,an,sn,nt,lt,tn,en,dn,fn]}),Nn.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Nn,imports:[K,I,Ve,In]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Nn,decorators:[{type:l,args:[{declarations:[mn,tt,ln,rn,Wt,et,rt,ot,at,Zt,it,st,Yt,Xt,nn,an,sn,nt,lt,tn,en,dn,fn],imports:[K,I,Ve,In],exports:[mn,tt,ln,rn,Wt,et,rt,ot,at,Zt,it,st,Yt,Xt,nn,an,sn,nt,lt,tn,en,dn,fn]}]}]});class Sn extends s{constructor(e,t,n){super(e),this.store=e,this.translate=t,this.fb=n,this.separatorKeysCodes=[ue,pe,de]}configForm(){return this.calculateDeltaConfigForm}onConfigurationSet(e){this.calculateDeltaConfigForm=this.fb.group({inputValueKey:[e.inputValueKey,[D.required,D.pattern(/(?:.|\s)*\S(&:.|\s)*/)]],outputValueKey:[e.outputValueKey,[D.required,D.pattern(/(?:.|\s)*\S(&:.|\s)*/)]],useCache:[e.useCache,[]],addPeriodBetweenMsgs:[e.addPeriodBetweenMsgs,[]],periodValueKey:[e.periodValueKey,[]],round:[e.round,[D.min(0),D.max(15)]],tellFailureIfDeltaIsNegative:[e.tellFailureIfDeltaIsNegative,[]]})}prepareInputConfig(e){return{inputValueKey:ee(e?.inputValueKey)?e.inputValueKey:null,outputValueKey:ee(e?.outputValueKey)?e.outputValueKey:null,useCache:!ee(e?.useCache)||e.useCache,addPeriodBetweenMsgs:!!ee(e?.addPeriodBetweenMsgs)&&e.addPeriodBetweenMsgs,periodValueKey:ee(e?.periodValueKey)?e.periodValueKey:null,round:ee(e?.round)?e.round:null,tellFailureIfDeltaIsNegative:!ee(e?.tellFailureIfDeltaIsNegative)||e.tellFailureIfDeltaIsNegative}}prepareOutputConfig(e){return ne(e)}updateValidators(e){this.calculateDeltaConfigForm.get("addPeriodBetweenMsgs").value?this.calculateDeltaConfigForm.get("periodValueKey").setValidators([D.required]):this.calculateDeltaConfigForm.get("periodValueKey").setValidators([]),this.calculateDeltaConfigForm.get("periodValueKey").updateValueAndValidity({emitEvent:e})}validatorTriggers(){return["addPeriodBetweenMsgs"]}}e("CalculateDeltaConfigComponent",Sn),Sn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Sn,deps:[{token:G.Store},{token:j.TranslateService},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Sn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Sn,selector:"tb-enrichment-node-calculate-delta-config",usesInheritance:!0,ngImport:t,template:"
\n
\n \n {{ 'tb.rulenode.input-value-key' | translate }}\n \n \n {{ 'tb.rulenode.input-value-key-required' | translate }}\n \n \n \n {{ 'tb.rulenode.output-value-key' | translate }}\n \n \n {{ 'tb.rulenode.output-value-key-required' | translate }}\n \n \n
\n \n {{ 'tb.rulenode.number-of-digits-after-floating-point' | translate }}\n \n \n {{ 'tb.rulenode.number-of-digits-after-floating-point-range' | translate }}\n \n \n {{ 'tb.rulenode.number-of-digits-after-floating-point-range' | translate }}\n \n \n
\n
\n \n {{ 'tb.rulenode.failure-if-delta-negative' | translate }}\n \n
\n
\n \n {{ 'tb.rulenode.use-caching' | translate }}\n \n
\n
\n
\n \n {{ 'tb.rulenode.add-time-difference-between-readings' | translate:\n { inputValueKey: calculateDeltaConfigForm.get('inputValueKey').valid ?\n calculateDeltaConfigForm.get('inputValueKey').value : 'tb.rulenode.input-value-key' | translate } }}\n \n
\n \n {{ 'tb.rulenode.period-value-key' | translate }}\n \n \n {{ 'tb.rulenode.period-value-key-required' | translate }}\n \n \n
\n
\n
\n",dependencies:[{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:J.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:Y.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Sn,decorators:[{type:n,args:[{selector:"tb-enrichment-node-calculate-delta-config",template:"
\n
\n \n {{ 'tb.rulenode.input-value-key' | translate }}\n \n \n {{ 'tb.rulenode.input-value-key-required' | translate }}\n \n \n \n {{ 'tb.rulenode.output-value-key' | translate }}\n \n \n {{ 'tb.rulenode.output-value-key-required' | translate }}\n \n \n
\n \n {{ 'tb.rulenode.number-of-digits-after-floating-point' | translate }}\n \n \n {{ 'tb.rulenode.number-of-digits-after-floating-point-range' | translate }}\n \n \n {{ 'tb.rulenode.number-of-digits-after-floating-point-range' | translate }}\n \n \n
\n
\n \n {{ 'tb.rulenode.failure-if-delta-negative' | translate }}\n \n
\n
\n \n {{ 'tb.rulenode.use-caching' | translate }}\n \n
\n
\n
\n \n {{ 'tb.rulenode.add-time-difference-between-readings' | translate:\n { inputValueKey: calculateDeltaConfigForm.get('inputValueKey').valid ?\n calculateDeltaConfigForm.get('inputValueKey').value : 'tb.rulenode.input-value-key' | translate } }}\n \n
\n \n {{ 'tb.rulenode.period-value-key' | translate }}\n \n \n {{ 'tb.rulenode.period-value-key-required' | translate }}\n \n \n
\n
\n
\n"}]}],ctorParameters:function(){return[{type:G.Store},{type:j.TranslateService},{type:E.FormBuilder}]}});class qn extends s{constructor(e,t,n){super(e),this.store=e,this.fb=t,this.translate=n,this.fetchToData=[],this.DataToFetch=Tt;for(const e of It.keys())e!==Tt.FIELDS&&this.fetchToData.push({value:e,name:this.translate.instant(It.get(e))})}configForm(){return this.customerAttributesConfigForm}prepareOutputConfig(e){const t={};for(const n of Object.keys(e.dataMapping))t[n.trim()]=e.dataMapping[n];return e.dataMapping=t,ne(e)}prepareInputConfig(e){let t,n;return t=ee(e?.telemetry)?e.telemetry?Tt.LATEST_TELEMETRY:Tt.ATTRIBUTES:ee(e?.dataToFetch)?e.dataToFetch:Tt.ATTRIBUTES,n=ee(e?.attrMapping)?e.attrMapping:ee(e?.dataMapping)?e.dataMapping:null,{dataToFetch:t,dataMapping:n,fetchTo:ee(e?.fetchTo)?e.fetchTo:Ut.METADATA}}selectTranslation(e,t){return this.customerAttributesConfigForm.get("dataToFetch").value===Tt.LATEST_TELEMETRY?e:t}onConfigurationSet(e){this.customerAttributesConfigForm=this.fb.group({dataToFetch:[e.dataToFetch,[]],dataMapping:[e.dataMapping,[D.required]],fetchTo:[e.fetchTo]})}}e("CustomerAttributesConfigComponent",qn),qn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:qn,deps:[{token:G.Store},{token:E.FormBuilder},{token:j.TranslateService}],target:t.ɵɵFactoryTarget.Component}),qn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:qn,selector:"tb-enrichment-node-customer-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n
tb.rulenode.mapping-of-customers
\n
\n
\n \n \n {{ data.name }}\n \n \n
\n
\n \n \n \n \n
\n',styles:[":host .fetch-to-data-toggle{max-width:420px;width:100%}\n"],dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.DefaultLayoutAlignDirective,selector:" [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg]",inputs:["fxLayoutAlign","fxLayoutAlign.xs","fxLayoutAlign.sm","fxLayoutAlign.md","fxLayoutAlign.lg","fxLayoutAlign.xl","fxLayoutAlign.lt-sm","fxLayoutAlign.lt-md","fxLayoutAlign.lt-lg","fxLayoutAlign.lt-xl","fxLayoutAlign.gt-xs","fxLayoutAlign.gt-sm","fxLayoutAlign.gt-md","fxLayoutAlign.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"directive",type:_e.ToggleOption,selector:"tb-toggle-option",inputs:["value"]},{kind:"component",type:ze.ToggleSelectComponent,selector:"tb-toggle-select",inputs:["disabled","selectMediaBreakpoint","appearance","disablePagination"]},{kind:"component",type:yn,selector:"tb-kv-map-config",inputs:["disabled","uniqueKeyValuePairValidator","labelText","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","popupHelpLink","required"]},{kind:"component",type:Cn,selector:"tb-msg-metadata-chip",inputs:["labelText"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:qn,decorators:[{type:n,args:[{selector:"tb-enrichment-node-customer-attributes-config",template:'
\n
tb.rulenode.mapping-of-customers
\n
\n
\n \n \n {{ data.name }}\n \n \n
\n
\n \n \n \n \n
\n',styles:[":host .fetch-to-data-toggle{max-width:420px;width:100%}\n"]}]}],ctorParameters:function(){return[{type:G.Store},{type:E.FormBuilder},{type:j.TranslateService}]}});class An extends s{constructor(e,t,n){super(e),this.store=e,this.translate=t,this.fb=n}configForm(){return this.deviceAttributesConfigForm}onConfigurationSet(e){this.deviceAttributesConfigForm=this.fb.group({deviceRelationsQuery:[e.deviceRelationsQuery,[D.required]],tellFailureIfAbsent:[e.tellFailureIfAbsent,[]],fetchTo:[e.fetchTo,[]],attributesControl:[e.attributesControl,[]]})}prepareInputConfig(e){return re(e)&&(e.attributesControl={clientAttributeNames:ee(e?.clientAttributeNames)?e.clientAttributeNames:null,latestTsKeyNames:ee(e?.latestTsKeyNames)?e.latestTsKeyNames:null,serverAttributeNames:ee(e?.serverAttributeNames)?e.serverAttributeNames:null,sharedAttributeNames:ee(e?.sharedAttributeNames)?e.sharedAttributeNames:null,getLatestValueWithTs:!!ee(e?.getLatestValueWithTs)&&e.getLatestValueWithTs}),{deviceRelationsQuery:ee(e?.deviceRelationsQuery)?e.deviceRelationsQuery:null,tellFailureIfAbsent:!ee(e?.tellFailureIfAbsent)||e.tellFailureIfAbsent,fetchTo:ee(e?.fetchTo)?e.fetchTo:Ut.METADATA,attributesControl:e?e.attributesControl:null}}prepareOutputConfig(e){for(const t of Object.keys(e.attributesControl))e[t]=e.attributesControl[t];return delete e.attributesControl,e}}e("DeviceAttributesConfigComponent",An),An.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:An,deps:[{token:G.Store},{token:j.TranslateService},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),An.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:An,selector:"tb-enrichment-node-device-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n
\n
tb.rulenode.device-relations-query
\n \n \n
\n
\n
\n
tb.rulenode.related-device-attributes
\n
\n tb.rulenode.at-least-one-field-required\n
\n
\n \n \n
\n
\n \n {{ \'tb.rulenode.tell-failure\' | translate }}\n \n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n'],dependencies:[{kind:"component",type:J.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Y.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"component",type:xn,selector:"tb-device-relations-query-config",inputs:["disabled","required"]},{kind:"component",type:Cn,selector:"tb-msg-metadata-chip",inputs:["labelText"]},{kind:"component",type:Ln,selector:"tb-select-attributes",inputs:["popupHelpLink"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:An,decorators:[{type:n,args:[{selector:"tb-enrichment-node-device-attributes-config",template:'
\n
\n
tb.rulenode.device-relations-query
\n \n \n
\n
\n
\n
tb.rulenode.related-device-attributes
\n
\n tb.rulenode.at-least-one-field-required\n
\n
\n \n \n
\n
\n \n {{ \'tb.rulenode.tell-failure\' | translate }}\n \n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n']}]}],ctorParameters:function(){return[{type:G.Store},{type:j.TranslateService},{type:E.FormBuilder}]}});class Mn extends s{constructor(e,t,n){super(e),this.store=e,this.translate=t,this.fb=n,this.predefinedValues=[];for(const e of Object.keys(ht))this.predefinedValues.push({value:ht[e],name:this.translate.instant(vt.get(ht[e]))})}ngOnInit(){super.ngOnInit()}configForm(){return this.entityDetailsConfigForm}prepareInputConfig(e){let t;return t=ee(e?.addToMetadata)?e.addToMetadata?Ut.METADATA:Ut.DATA:e?.fetchTo?e.fetchTo:Ut.DATA,{detailsList:ee(e?.detailsList)?e.detailsList:null,fetchTo:t}}onConfigurationSet(e){this.entityDetailsConfigForm=this.fb.group({detailsList:[e.detailsList,[D.required]],fetchTo:[e.fetchTo,[]]})}}e("EntityDetailsConfigComponent",Mn),Mn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Mn,deps:[{token:G.Store},{token:j.TranslateService},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Mn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Mn,selector:"tb-enrichment-node-entity-details-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n help\n \n \n \n \n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n'],dependencies:[{kind:"component",type:Ue.StringItemsListComponent,selector:"tb-string-items-list",inputs:["required","disabled","label","placeholder","hint","requiredText","floatLabel","appearance","editable","subscriptSizing","predefinedValues"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:z.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"directive",type:se.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:Cn,selector:"tb-msg-metadata-chip",inputs:["labelText"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Mn,decorators:[{type:n,args:[{selector:"tb-enrichment-node-entity-details-config",template:'
\n \n \n help\n \n \n \n \n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n']}]}],ctorParameters:function(){return[{type:G.Store},{type:j.TranslateService},{type:E.FormBuilder}]}});class Gn extends s{constructor(e,t,n){super(e),this.store=e,this.translate=t,this.fb=n,this.separatorKeysCodes=[ue,pe,de],this.aggregationTypes=N,this.aggregations=Object.values(N),this.aggregationTypesTranslations=S,this.fetchMode=Ct,this.samplingOrders=Object.values(Lt),this.samplingOrdersTranslate=St,this.timeUnits=Object.values(gt),this.timeUnitsTranslationMap=yt,this.deduplicationStrategiesHintTranslations=kt,this.headerOptions=[],this.timeUnitMap={[gt.MILLISECONDS]:1,[gt.SECONDS]:1e3,[gt.MINUTES]:6e4,[gt.HOURS]:36e5,[gt.DAYS]:864e5},this.intervalValidator=()=>e=>e.get("startInterval").value*this.timeUnitMap[e.get("startIntervalTimeUnit").value]<=e.get("endInterval").value*this.timeUnitMap[e.get("endIntervalTimeUnit").value]?{intervalError:!0}:null;for(const e of Ft.keys())this.headerOptions.push({value:e,name:this.translate.instant(Ft.get(e))})}configForm(){return this.getTelemetryFromDatabaseConfigForm}onConfigurationSet(e){this.getTelemetryFromDatabaseConfigForm=this.fb.group({latestTsKeyNames:[e.latestTsKeyNames,[D.required]],aggregation:[e.aggregation,[D.required]],fetchMode:[e.fetchMode,[D.required]],orderBy:[e.orderBy,[]],limit:[e.limit,[]],useMetadataIntervalPatterns:[e.useMetadataIntervalPatterns,[]],interval:this.fb.group({startInterval:[e.interval.startInterval,[]],startIntervalTimeUnit:[e.interval.startIntervalTimeUnit,[]],endInterval:[e.interval.endInterval,[]],endIntervalTimeUnit:[e.interval.endIntervalTimeUnit,[]]}),startIntervalPattern:[e.startIntervalPattern,[]],endIntervalPattern:[e.endIntervalPattern,[]]})}validatorTriggers(){return["fetchMode","useMetadataIntervalPatterns"]}toggleChange(e){this.getTelemetryFromDatabaseConfigForm.get("fetchMode").patchValue(e,{emitEvent:!0})}prepareOutputConfig(e){return e.startInterval=e.interval.startInterval,e.startIntervalTimeUnit=e.interval.startIntervalTimeUnit,e.endInterval=e.interval.endInterval,e.endIntervalTimeUnit=e.interval.endIntervalTimeUnit,delete e.interval,ne(e)}prepareInputConfig(e){return re(e)&&(e.interval={startInterval:e.startInterval,startIntervalTimeUnit:e.startIntervalTimeUnit,endInterval:e.endInterval,endIntervalTimeUnit:e.endIntervalTimeUnit}),{latestTsKeyNames:ee(e?.latestTsKeyNames)?e.latestTsKeyNames:null,aggregation:ee(e?.aggregation)?e.aggregation:N.NONE,fetchMode:ee(e?.fetchMode)?e.fetchMode:Ct.FIRST,orderBy:ee(e?.orderBy)?e.orderBy:Lt.ASC,limit:ee(e?.limit)?e.limit:1e3,useMetadataIntervalPatterns:!!ee(e?.useMetadataIntervalPatterns)&&e.useMetadataIntervalPatterns,interval:{startInterval:ee(e?.interval?.startInterval)?e.interval.startInterval:2,startIntervalTimeUnit:ee(e?.interval?.startIntervalTimeUnit)?e.interval.startIntervalTimeUnit:gt.MINUTES,endInterval:ee(e?.interval?.endInterval)?e.interval.endInterval:1,endIntervalTimeUnit:ee(e?.interval?.endIntervalTimeUnit)?e.interval.endIntervalTimeUnit:gt.MINUTES},startIntervalPattern:ee(e?.startIntervalPattern)?e.startIntervalPattern:null,endIntervalPattern:ee(e?.endIntervalPattern)?e.endIntervalPattern:null}}updateValidators(e){const t=this.getTelemetryFromDatabaseConfigForm.get("fetchMode").value,n=this.getTelemetryFromDatabaseConfigForm.get("useMetadataIntervalPatterns").value;t&&t===Ct.ALL?(this.getTelemetryFromDatabaseConfigForm.get("aggregation").setValidators([D.required]),this.getTelemetryFromDatabaseConfigForm.get("orderBy").setValidators([D.required]),this.getTelemetryFromDatabaseConfigForm.get("limit").setValidators([D.required,D.min(2),D.max(1e3)])):(this.getTelemetryFromDatabaseConfigForm.get("aggregation").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("orderBy").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("limit").setValidators([])),n?(this.getTelemetryFromDatabaseConfigForm.get("interval.startInterval").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("interval.startIntervalTimeUnit").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("interval.endInterval").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("interval.endIntervalTimeUnit").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("interval").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("startIntervalPattern").setValidators([D.required,D.pattern(/(?:.|\s)*\S(&:.|\s)*/)]),this.getTelemetryFromDatabaseConfigForm.get("endIntervalPattern").setValidators([D.required,D.pattern(/(?:.|\s)*\S(&:.|\s)*/)])):(this.getTelemetryFromDatabaseConfigForm.get("interval.startInterval").setValidators([D.required,D.min(1),D.max(2147483647)]),this.getTelemetryFromDatabaseConfigForm.get("interval.startIntervalTimeUnit").setValidators([D.required]),this.getTelemetryFromDatabaseConfigForm.get("interval.endInterval").setValidators([D.required,D.min(1),D.max(2147483647)]),this.getTelemetryFromDatabaseConfigForm.get("interval.endIntervalTimeUnit").setValidators([D.required]),this.getTelemetryFromDatabaseConfigForm.get("interval").setValidators([this.intervalValidator()]),this.getTelemetryFromDatabaseConfigForm.get("startIntervalPattern").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("endIntervalPattern").setValidators([])),this.getTelemetryFromDatabaseConfigForm.get("aggregation").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("orderBy").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("limit").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("interval.startInterval").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("interval.startIntervalTimeUnit").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("interval.endInterval").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("interval.endIntervalTimeUnit").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("interval").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("startIntervalPattern").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("endIntervalPattern").updateValueAndValidity({emitEvent:e})}removeKey(e,t){const n=this.getTelemetryFromDatabaseConfigForm.get(t).value,r=n.indexOf(e);r>=0&&(n.splice(r,1),this.getTelemetryFromDatabaseConfigForm.get(t).setValue(n,{emitEvent:!0}))}clearChipGrid(){this.getTelemetryFromDatabaseConfigForm.get("latestTsKeyNames").patchValue([],{emitEvent:!0})}addKey(e,t){const n=e.input;let r=e.value;if((r||"").trim()){r=r.trim();let e=this.getTelemetryFromDatabaseConfigForm.get(t).value;e&&-1!==e.indexOf(r)||(e||(e=[]),e.push(r),this.getTelemetryFromDatabaseConfigForm.get(t).setValue(e,{emitEvent:!0}))}n&&(n.value="")}defaultPaddingEnable(){return this.getTelemetryFromDatabaseConfigForm.get("fetchMode").value===Ct.ALL&&this.getTelemetryFromDatabaseConfigForm.get("aggregation").value===N.NONE}}e("GetTelemetryFromDatabaseConfigComponent",Gn),Gn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Gn,deps:[{token:G.Store},{token:j.TranslateService},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Gn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Gn,selector:"tb-enrichment-node-get-telemetry-from-database",usesInheritance:!0,ngImport:t,template:'
\n \n
\n help\n \n
\n
\n
tb.rulenode.fetch-interval
\n
\n \n {{ \'tb.rulenode.use-metadata-dynamic-interval\' | translate }}\n \n
\n
\n
\n \n {{ \'tb.rulenode.interval-start\' | translate }}\n \n \n {{ \'tb.rulenode.start-interval-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n {{ \'tb.rulenode.time-unit\' | translate }}\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n \n {{ \'tb.rulenode.interval-end\' | translate }}\n \n \n {{ \'tb.rulenode.end-interval-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n {{ \'tb.rulenode.time-unit\' | translate }}\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n error_outline\n
\n \n {{ \'tb.rulenode.fetch-timeseries-from-to\' | translate:\n {\n startInterval: getTelemetryFromDatabaseConfigForm.get(\'interval.startInterval\').value,\n endInterval: getTelemetryFromDatabaseConfigForm.get(\'interval.endInterval\').value,\n startIntervalTimeUnit: getTelemetryFromDatabaseConfigForm.get(\'interval.startIntervalTimeUnit\').value.toLowerCase(),\n endIntervalTimeUnit: getTelemetryFromDatabaseConfigForm.get(\'interval.endIntervalTimeUnit\').value.toLowerCase()\n } }}\n \n \n {{ "tb.rulenode.fetch-timeseries-from-to-invalid" | translate }}\n \n
\n
\n
\n \n
\n \n {{ \'tb.rulenode.start-interval\' | translate }}\n \n \n {{ \'tb.rulenode.start-interval-required\' | translate }}\n \n \n \n {{ \'tb.rulenode.end-interval\' | translate }}\n \n \n {{ \'tb.rulenode.end-interval-required\' | translate }}\n \n \n \n \n
\n
\n
\n
\n
tb.rulenode.fetch-strategy
\n
\n
\n \n \n {{ data.name }}\n \n \n
\n
\n {{ deduplicationStrategiesHintTranslations.get(getTelemetryFromDatabaseConfigForm.get(\'fetchMode\').value) | translate }}\n
\n
\n
\n \n {{ \'aggregation.function\' | translate }}\n \n \n {{ aggregationTypesTranslations.get(aggregationTypes[aggregation]) | translate }}\n \n \n \n
\n \n {{ "tb.rulenode.order-by-timestamp" | translate }} \n \n \n {{ samplingOrdersTranslate.get(order) | translate }}\n \n \n \n \n {{ "tb.rulenode.limit" | translate }}\n \n {{ "tb.rulenode.limit-hint" | translate }}\n \n {{ \'tb.rulenode.limit-required\' | translate }}\n \n \n {{ \'tb.rulenode.limit-range\' | translate }}\n \n \n {{ \'tb.rulenode.limit-range\' | translate }}\n \n \n
\n
\n
\n
\n',styles:[":host .see-example{display:inline-block}:host .description-block{display:flex;align-items:center;border-radius:6px;border:1px solid #EAEAEA}:host .description-block .description-icon{font-size:24px;height:24px;min-height:24px;width:24px;min-width:24px;line-height:24px;color:#d9d9d9;margin:4px}:host .description-block .description-text{font-size:12px;line-height:16px;letter-spacing:.25px;margin:6px}:host .description-block.error{color:var(--mdc-theme-error, #f44336)}:host .description-block.error .description-icon{color:var(--mdc-theme-error, #f44336)}:host .item-center{align-items:center}:host .item-center .fetch-mod-toggle{width:100%}:host .hint-container{width:100%}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n'],dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:Pe.HelpPopupComponent,selector:"[tb-help-popup], [tb-help-popup-content]",inputs:["tb-help-popup","tb-help-popup-content","trigger-text","trigger-style","tb-help-popup-placement","tb-help-popup-style","hintMode"]},{kind:"component",type:Ue.StringItemsListComponent,selector:"tb-string-items-list",inputs:["required","disabled","label","placeholder","hint","requiredText","floatLabel","appearance","editable","subscriptSizing","predefinedValues"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:z.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:se.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"component",type:J.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:E.FormGroupName,selector:"[formGroupName]",inputs:["formGroupName"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"directive",type:_e.ToggleOption,selector:"tb-toggle-option",inputs:["value"]},{kind:"component",type:ze.ToggleSelectComponent,selector:"tb-toggle-select",inputs:["disabled","selectMediaBreakpoint","appearance","disablePagination"]},{kind:"component",type:Y.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"component",type:gn,selector:"tb-example-hint",inputs:["hintText","popupHelpLink"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Gn,decorators:[{type:n,args:[{selector:"tb-enrichment-node-get-telemetry-from-database",template:'
\n \n
\n help\n \n
\n
\n
tb.rulenode.fetch-interval
\n
\n \n {{ \'tb.rulenode.use-metadata-dynamic-interval\' | translate }}\n \n
\n
\n
\n \n {{ \'tb.rulenode.interval-start\' | translate }}\n \n \n {{ \'tb.rulenode.start-interval-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n {{ \'tb.rulenode.time-unit\' | translate }}\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n \n {{ \'tb.rulenode.interval-end\' | translate }}\n \n \n {{ \'tb.rulenode.end-interval-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n {{ \'tb.rulenode.time-unit\' | translate }}\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n error_outline\n
\n \n {{ \'tb.rulenode.fetch-timeseries-from-to\' | translate:\n {\n startInterval: getTelemetryFromDatabaseConfigForm.get(\'interval.startInterval\').value,\n endInterval: getTelemetryFromDatabaseConfigForm.get(\'interval.endInterval\').value,\n startIntervalTimeUnit: getTelemetryFromDatabaseConfigForm.get(\'interval.startIntervalTimeUnit\').value.toLowerCase(),\n endIntervalTimeUnit: getTelemetryFromDatabaseConfigForm.get(\'interval.endIntervalTimeUnit\').value.toLowerCase()\n } }}\n \n \n {{ "tb.rulenode.fetch-timeseries-from-to-invalid" | translate }}\n \n
\n
\n
\n \n
\n \n {{ \'tb.rulenode.start-interval\' | translate }}\n \n \n {{ \'tb.rulenode.start-interval-required\' | translate }}\n \n \n \n {{ \'tb.rulenode.end-interval\' | translate }}\n \n \n {{ \'tb.rulenode.end-interval-required\' | translate }}\n \n \n \n \n
\n
\n
\n
\n
tb.rulenode.fetch-strategy
\n
\n
\n \n \n {{ data.name }}\n \n \n
\n
\n {{ deduplicationStrategiesHintTranslations.get(getTelemetryFromDatabaseConfigForm.get(\'fetchMode\').value) | translate }}\n
\n
\n
\n \n {{ \'aggregation.function\' | translate }}\n \n \n {{ aggregationTypesTranslations.get(aggregationTypes[aggregation]) | translate }}\n \n \n \n
\n \n {{ "tb.rulenode.order-by-timestamp" | translate }} \n \n \n {{ samplingOrdersTranslate.get(order) | translate }}\n \n \n \n \n {{ "tb.rulenode.limit" | translate }}\n \n {{ "tb.rulenode.limit-hint" | translate }}\n \n {{ \'tb.rulenode.limit-required\' | translate }}\n \n \n {{ \'tb.rulenode.limit-range\' | translate }}\n \n \n {{ \'tb.rulenode.limit-range\' | translate }}\n \n \n
\n
\n
\n
\n',styles:[":host .see-example{display:inline-block}:host .description-block{display:flex;align-items:center;border-radius:6px;border:1px solid #EAEAEA}:host .description-block .description-icon{font-size:24px;height:24px;min-height:24px;width:24px;min-width:24px;line-height:24px;color:#d9d9d9;margin:4px}:host .description-block .description-text{font-size:12px;line-height:16px;letter-spacing:.25px;margin:6px}:host .description-block.error{color:var(--mdc-theme-error, #f44336)}:host .description-block.error .description-icon{color:var(--mdc-theme-error, #f44336)}:host .item-center{align-items:center}:host .item-center .fetch-mod-toggle{width:100%}:host .hint-container{width:100%}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n']}]}],ctorParameters:function(){return[{type:G.Store},{type:j.TranslateService},{type:E.FormBuilder}]}});class En extends s{constructor(e,t,n){super(e),this.store=e,this.translate=t,this.fb=n}configForm(){return this.originatorAttributesConfigForm}onConfigurationSet(e){this.originatorAttributesConfigForm=this.fb.group({tellFailureIfAbsent:[e.tellFailureIfAbsent,[]],fetchTo:[e.fetchTo,[]],attributesControl:[e.attributesControl,[]]})}prepareInputConfig(e){return re(e)&&(e.attributesControl={clientAttributeNames:ee(e?.clientAttributeNames)?e.clientAttributeNames:null,latestTsKeyNames:ee(e?.latestTsKeyNames)?e.latestTsKeyNames:null,serverAttributeNames:ee(e?.serverAttributeNames)?e.serverAttributeNames:null,sharedAttributeNames:ee(e?.sharedAttributeNames)?e.sharedAttributeNames:null,getLatestValueWithTs:!!ee(e?.getLatestValueWithTs)&&e.getLatestValueWithTs}),{fetchTo:ee(e?.fetchTo)?e.fetchTo:Ut.METADATA,tellFailureIfAbsent:!!ee(e?.tellFailureIfAbsent)&&e.tellFailureIfAbsent,attributesControl:ee(e?.attributesControl)?e.attributesControl:null}}prepareOutputConfig(e){for(const t of Object.keys(e.attributesControl))e[t]=e.attributesControl[t];return delete e.attributesControl,e}}e("OriginatorAttributesConfigComponent",En),En.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:En,deps:[{token:G.Store},{token:j.TranslateService},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),En.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:En,selector:"tb-enrichment-node-originator-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n
\n
\n
tb.rulenode.originator-attributes
\n
\n tb.rulenode.at-least-one-field-required\n
\n
\n \n \n \n \n
\n
\n \n {{ \'tb.rulenode.tell-failure\' | translate }}\n \n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n'],dependencies:[{kind:"component",type:J.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Y.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"component",type:Cn,selector:"tb-msg-metadata-chip",inputs:["labelText"]},{kind:"component",type:Ln,selector:"tb-select-attributes",inputs:["popupHelpLink"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:En,decorators:[{type:n,args:[{selector:"tb-enrichment-node-originator-attributes-config",template:'
\n
\n
\n
tb.rulenode.originator-attributes
\n
\n tb.rulenode.at-least-one-field-required\n
\n
\n \n \n \n \n
\n
\n \n {{ \'tb.rulenode.tell-failure\' | translate }}\n \n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n']}]}],ctorParameters:function(){return[{type:G.Store},{type:j.TranslateService},{type:E.FormBuilder}]}});class Dn extends s{constructor(e,t,n){super(e),this.store=e,this.fb=t,this.translate=n,this.originatorFields=[];for(const e of pt)this.originatorFields.push({value:e.value,name:this.translate.instant(e.name)})}configForm(){return this.originatorFieldsConfigForm}prepareOutputConfig(e){return ne(e)}prepareInputConfig(e){return{dataMapping:ee(e?.dataMapping)?e.dataMapping:null,ignoreNullStrings:ee(e?.ignoreNullStrings)?e.ignoreNullStrings:null,fetchTo:ee(e?.fetchTo)?e.fetchTo:Ut.METADATA}}onConfigurationSet(e){this.originatorFieldsConfigForm=this.fb.group({dataMapping:[e.dataMapping,[D.required]],ignoreNullStrings:[e.ignoreNullStrings,[]],fetchTo:[e.fetchTo,[]]})}}e("OriginatorFieldsConfigComponent",Dn),Dn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Dn,deps:[{token:G.Store},{token:E.FormBuilder},{token:j.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Dn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Dn,selector:"tb-enrichment-node-originator-fields-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n
\n \n {{ \'tb.rulenode.skip-empty-fields\' | translate }}\n \n
\n
\n',dependencies:[{kind:"component",type:J.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:Y.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"component",type:Cn,selector:"tb-msg-metadata-chip",inputs:["labelText"]},{kind:"component",type:Fn,selector:"tb-sv-map-config",inputs:["selectOptions","disabled","labelText","requiredText","targetKeyPrefix","selectText","selectRequiredText","valText","valRequiredText","hintText","popupHelpLink","required"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Dn,decorators:[{type:n,args:[{selector:"tb-enrichment-node-originator-fields-config",template:'
\n \n \n \n \n
\n \n {{ \'tb.rulenode.skip-empty-fields\' | translate }}\n \n
\n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.FormBuilder},{type:j.TranslateService}]}});class Vn extends s{constructor(e,t,n){super(e),this.store=e,this.fb=t,this.translate=n,this.DataToFetch=Tt,this.msgMetadataLabelTranslations=Nt,this.originatorFields=[],this.fetchToData=[],this.destroy$=new Ee;for(const e of Object.keys(pt))this.originatorFields.push({value:pt[e].value,name:this.translate.instant(pt[e].name)});for(const e of It.keys())this.fetchToData.push({value:e,name:this.translate.instant(It.get(e))})}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}configForm(){return this.relatedAttributesConfigForm}prepareOutputConfig(e){e.dataToFetch===Tt.FIELDS?(e.dataMapping=e.svMap,delete e.svMap):(e.dataMapping=e.kvMap,delete e.kvMap);const t={};if(e&&e.dataMapping)for(const n of Object.keys(e.dataMapping))t[n.trim()]=e.dataMapping[n];return e.dataMapping=t,delete e.svMap,delete e.kvMap,ne(e)}prepareInputConfig(e){let t,n,r={[b.name.value]:`relatedEntity${this.translate.instant(b.name.name)}`},o={serialNumber:"sn"};return t=ee(e?.telemetry)?e.telemetry?Tt.LATEST_TELEMETRY:Tt.ATTRIBUTES:ee(e?.dataToFetch)?e.dataToFetch:Tt.ATTRIBUTES,n=ee(e?.attrMapping)?e.attrMapping:ee(e?.dataMapping)?e.dataMapping:null,t===Tt.FIELDS?r=n:o=n,{relationsQuery:ee(e?.relationsQuery)?e.relationsQuery:null,dataToFetch:t,svMap:r,kvMap:o,fetchTo:ee(e?.fetchTo)?e.fetchTo:Ut.METADATA}}selectTranslation(e,t){return this.relatedAttributesConfigForm.get("dataToFetch").value===Tt.LATEST_TELEMETRY?e:t}onConfigurationSet(e){this.relatedAttributesConfigForm=this.fb.group({relationsQuery:[e.relationsQuery,[D.required]],dataToFetch:[e.dataToFetch,[]],kvMap:[e.kvMap,[D.required]],svMap:[e.svMap,[D.required]],fetchTo:[e.fetchTo,[]]})}updateValidators(e){this.relatedAttributesConfigForm.get("dataToFetch").value===Tt.FIELDS?(this.relatedAttributesConfigForm.get("svMap").enable({emitEvent:!1}),this.relatedAttributesConfigForm.get("kvMap").disable({emitEvent:!1}),this.relatedAttributesConfigForm.get("svMap").updateValueAndValidity()):(this.relatedAttributesConfigForm.get("svMap").disable({emitEvent:!1}),this.relatedAttributesConfigForm.get("kvMap").enable({emitEvent:!1}),this.relatedAttributesConfigForm.get("kvMap").updateValueAndValidity())}validatorTriggers(){return["dataToFetch"]}}e("RelatedAttributesConfigComponent",Vn),Vn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Vn,deps:[{token:G.Store},{token:E.FormBuilder},{token:j.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Vn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Vn,selector:"tb-enrichment-node-related-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n
\n
tb.rulenode.data-to-fetch
\n \n \n {{ data.name }}\n \n \n \n \n \n \n \n \n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n'],dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"directive",type:_e.ToggleOption,selector:"tb-toggle-option",inputs:["value"]},{kind:"component",type:ze.ToggleSelectComponent,selector:"tb-toggle-select",inputs:["disabled","selectMediaBreakpoint","appearance","disablePagination"]},{kind:"component",type:yn,selector:"tb-kv-map-config",inputs:["disabled","uniqueKeyValuePairValidator","labelText","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","popupHelpLink","required"]},{kind:"component",type:bn,selector:"tb-relations-query-config",inputs:["disabled","required"]},{kind:"component",type:Cn,selector:"tb-msg-metadata-chip",inputs:["labelText"]},{kind:"component",type:Fn,selector:"tb-sv-map-config",inputs:["selectOptions","disabled","labelText","requiredText","targetKeyPrefix","selectText","selectRequiredText","valText","valRequiredText","hintText","popupHelpLink","required"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Vn,decorators:[{type:n,args:[{selector:"tb-enrichment-node-related-attributes-config",template:'
\n \n \n
\n
tb.rulenode.data-to-fetch
\n \n \n {{ data.name }}\n \n \n \n \n \n \n \n \n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n']}]}],ctorParameters:function(){return[{type:G.Store},{type:E.FormBuilder},{type:j.TranslateService}]}});class Pn extends s{constructor(e,t,n){super(e),this.store=e,this.fb=t,this.translate=n,this.fetchToData=[],this.DataToFetch=Tt;for(const e of It.keys())e!==Tt.FIELDS&&this.fetchToData.push({value:e,name:this.translate.instant(It.get(e))})}configForm(){return this.tenantAttributesConfigForm}prepareInputConfig(e){let t,n;return t=ee(e?.telemetry)?e.telemetry?Tt.LATEST_TELEMETRY:Tt.ATTRIBUTES:ee(e?.dataToFetch)?e.dataToFetch:Tt.ATTRIBUTES,n=ee(e?.attrMapping)?e.attrMapping:ee(e?.dataMapping)?e.dataMapping:null,{dataToFetch:t,dataMapping:n,fetchTo:ee(e?.fetchTo)?e.fetchTo:Ut.METADATA}}selectTranslation(e,t){return this.tenantAttributesConfigForm.get("dataToFetch").value===Tt.LATEST_TELEMETRY?e:t}onConfigurationSet(e){this.tenantAttributesConfigForm=this.fb.group({dataToFetch:[e.dataToFetch,[]],dataMapping:[e.dataMapping,[D.required]],fetchTo:[e.fetchTo,[]]})}}e("TenantAttributesConfigComponent",Pn),Pn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Pn,deps:[{token:G.Store},{token:E.FormBuilder},{token:j.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Pn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Pn,selector:"tb-enrichment-node-tenant-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n
tb.rulenode.mapping-of-tenant
\n
\n
\n \n \n {{ data.name }}\n \n \n
\n
\n \n \n \n \n
\n',styles:[":host .fetch-to-data-toggle{max-width:420px;width:100%}\n"],dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.DefaultLayoutAlignDirective,selector:" [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg]",inputs:["fxLayoutAlign","fxLayoutAlign.xs","fxLayoutAlign.sm","fxLayoutAlign.md","fxLayoutAlign.lg","fxLayoutAlign.xl","fxLayoutAlign.lt-sm","fxLayoutAlign.lt-md","fxLayoutAlign.lt-lg","fxLayoutAlign.lt-xl","fxLayoutAlign.gt-xs","fxLayoutAlign.gt-sm","fxLayoutAlign.gt-md","fxLayoutAlign.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"directive",type:_e.ToggleOption,selector:"tb-toggle-option",inputs:["value"]},{kind:"component",type:ze.ToggleSelectComponent,selector:"tb-toggle-select",inputs:["disabled","selectMediaBreakpoint","appearance","disablePagination"]},{kind:"component",type:yn,selector:"tb-kv-map-config",inputs:["disabled","uniqueKeyValuePairValidator","labelText","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","popupHelpLink","required"]},{kind:"component",type:Cn,selector:"tb-msg-metadata-chip",inputs:["labelText"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Pn,decorators:[{type:n,args:[{selector:"tb-enrichment-node-tenant-attributes-config",template:'
\n
tb.rulenode.mapping-of-tenant
\n
\n
\n \n \n {{ data.name }}\n \n \n
\n
\n \n \n \n \n
\n',styles:[":host .fetch-to-data-toggle{max-width:420px;width:100%}\n"]}]}],ctorParameters:function(){return[{type:G.Store},{type:E.FormBuilder},{type:j.TranslateService}]}});class wn extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.fetchDeviceCredentialsConfigForm}prepareInputConfig(e){return{fetchTo:ee(e?.fetchTo)?e.fetchTo:Ut.METADATA}}onConfigurationSet(e){this.fetchDeviceCredentialsConfigForm=this.fb.group({fetchTo:[e.fetchTo,[]]})}}e("FetchDeviceCredentialsConfigComponent",wn),wn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:wn,deps:[{token:G.Store},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),wn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:wn,selector:"./tb-enrichment-node-fetch-device-credentials-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n
\n',dependencies:[{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:Cn,selector:"tb-msg-metadata-chip",inputs:["labelText"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:wn,decorators:[{type:n,args:[{selector:"./tb-enrichment-node-fetch-device-credentials-config",template:'
\n \n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.FormBuilder}]}});class Rn{}e("RulenodeCoreConfigEnrichmentModule",Rn),Rn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Rn,deps:[],target:t.ɵɵFactoryTarget.NgModule}),Rn.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.9",ngImport:t,type:Rn,declarations:[qn,Mn,An,En,Dn,Gn,Vn,Pn,Sn,wn],imports:[K,I,In],exports:[qn,Mn,An,En,Dn,Gn,Vn,Pn,Sn,wn]}),Rn.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Rn,imports:[K,I,In]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Rn,decorators:[{type:l,args:[{declarations:[qn,Mn,An,En,Dn,Gn,Vn,Pn,Sn,wn],imports:[K,I,In],exports:[qn,Mn,An,En,Dn,Gn,Vn,Pn,Sn,wn]}]}]});class On extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.allAzureIotHubCredentialsTypes=Et,this.azureIotHubCredentialsTypeTranslationsMap=Dt}configForm(){return this.azureIotHubConfigForm}onConfigurationSet(e){this.azureIotHubConfigForm=this.fb.group({topicPattern:[e?e.topicPattern:null,[D.required]],host:[e?e.host:null,[D.required]],port:[e?e.port:null,[D.required,D.min(1),D.max(65535)]],connectTimeoutSec:[e?e.connectTimeoutSec:null,[D.required,D.min(1),D.max(200)]],clientId:[e?e.clientId:null,[D.required]],cleanSession:[!!e&&e.cleanSession,[]],ssl:[!!e&&e.ssl,[]],credentials:this.fb.group({type:[e&&e.credentials?e.credentials.type:null,[D.required]],sasKey:[e&&e.credentials?e.credentials.sasKey:null,[]],caCert:[e&&e.credentials?e.credentials.caCert:null,[]],caCertFileName:[e&&e.credentials?e.credentials.caCertFileName:null,[]],privateKey:[e&&e.credentials?e.credentials.privateKey:null,[]],privateKeyFileName:[e&&e.credentials?e.credentials.privateKeyFileName:null,[]],cert:[e&&e.credentials?e.credentials.cert:null,[]],certFileName:[e&&e.credentials?e.credentials.certFileName:null,[]],password:[e&&e.credentials?e.credentials.password:null,[]]})})}prepareOutputConfig(e){const t=e.credentials.type;return"sas"===t&&(e.credentials={type:t,sasKey:e.credentials.sasKey,caCert:e.credentials.caCert,caCertFileName:e.credentials.caCertFileName}),e}validatorTriggers(){return["credentials.type"]}updateValidators(e){const t=this.azureIotHubConfigForm.get("credentials"),n=t.get("type").value;switch(e&&t.reset({type:n},{emitEvent:!1}),t.get("sasKey").setValidators([]),t.get("privateKey").setValidators([]),t.get("privateKeyFileName").setValidators([]),t.get("cert").setValidators([]),t.get("certFileName").setValidators([]),n){case"sas":t.get("sasKey").setValidators([D.required]);break;case"cert.PEM":t.get("privateKey").setValidators([D.required]),t.get("privateKeyFileName").setValidators([D.required]),t.get("cert").setValidators([D.required]),t.get("certFileName").setValidators([D.required])}t.get("sasKey").updateValueAndValidity({emitEvent:e}),t.get("privateKey").updateValueAndValidity({emitEvent:e}),t.get("privateKeyFileName").updateValueAndValidity({emitEvent:e}),t.get("cert").updateValueAndValidity({emitEvent:e}),t.get("certFileName").updateValueAndValidity({emitEvent:e})}}e("AzureIotHubConfigComponent",On),On.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:On,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),On.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:On,selector:"tb-external-node-azure-iot-hub-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.topic\n \n \n {{ \'tb.rulenode.topic-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.hostname\n \n \n {{ \'tb.rulenode.hostname-required\' | translate }}\n \n \n \n tb.rulenode.device-id\n \n \n {{ \'tb.rulenode.device-id-required\' | translate }}\n \n \n \n \n \n tb.rulenode.credentials\n \n {{ azureIotHubCredentialsTypeTranslationsMap.get(azureIotHubConfigForm.get(\'credentials.type\').value) | translate }}\n \n \n
\n \n tb.rulenode.credentials-type\n \n \n {{ azureIotHubCredentialsTypeTranslationsMap.get(credentialsType) | translate }}\n \n \n \n {{ \'tb.rulenode.credentials-type-required\' | translate }}\n \n \n
\n \n \n \n \n tb.rulenode.sas-key\n \n \n \n {{ \'tb.rulenode.sas-key-required\' | translate }}\n \n \n \n \n \n \n \n \n \n \n \n \n \n tb.rulenode.private-key-password\n \n \n \n \n
\n
\n
\n
\n
\n',styles:[":host .tb-mqtt-credentials-panel-group{margin:0 6px}\n"],dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:B.NgSwitch,selector:"[ngSwitch]",inputs:["ngSwitch"]},{kind:"directive",type:B.NgSwitchCase,selector:"[ngSwitchCase]",inputs:["ngSwitchCase"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:z.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:Ae.MatAccordion,selector:"mat-accordion",inputs:["multi","hideToggle","displayMode","togglePosition"],exportAs:["matAccordion"]},{kind:"component",type:Ae.MatExpansionPanel,selector:"mat-expansion-panel",inputs:["disabled","expanded","hideToggle","togglePosition"],outputs:["opened","closed","expandedChange","afterExpand","afterCollapse"],exportAs:["matExpansionPanel"]},{kind:"component",type:Ae.MatExpansionPanelHeader,selector:"mat-expansion-panel-header",inputs:["tabIndex","expandedHeight","collapsedHeight"]},{kind:"directive",type:Ae.MatExpansionPanelTitle,selector:"mat-panel-title"},{kind:"directive",type:Ae.MatExpansionPanelDescription,selector:"mat-panel-description"},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:E.FormGroupName,selector:"[formGroupName]",inputs:["formGroupName"]},{kind:"component",type:Be.FileInputComponent,selector:"tb-file-input",inputs:["label","accept","noFileText","inputId","allowedExtensions","dropLabel","contentConvertFunction","required","requiredAsError","disabled","existingFileName","readAsBinary","workFromFileObj","multipleFile"],outputs:["fileNameChanged"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Ke.TogglePasswordComponent,selector:"tb-toggle-password"},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:On,decorators:[{type:n,args:[{selector:"tb-external-node-azure-iot-hub-config",template:'
\n \n tb.rulenode.topic\n \n \n {{ \'tb.rulenode.topic-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.hostname\n \n \n {{ \'tb.rulenode.hostname-required\' | translate }}\n \n \n \n tb.rulenode.device-id\n \n \n {{ \'tb.rulenode.device-id-required\' | translate }}\n \n \n \n \n \n tb.rulenode.credentials\n \n {{ azureIotHubCredentialsTypeTranslationsMap.get(azureIotHubConfigForm.get(\'credentials.type\').value) | translate }}\n \n \n
\n \n tb.rulenode.credentials-type\n \n \n {{ azureIotHubCredentialsTypeTranslationsMap.get(credentialsType) | translate }}\n \n \n \n {{ \'tb.rulenode.credentials-type-required\' | translate }}\n \n \n
\n \n \n \n \n tb.rulenode.sas-key\n \n \n \n {{ \'tb.rulenode.sas-key-required\' | translate }}\n \n \n \n \n \n \n \n \n \n \n \n \n \n tb.rulenode.private-key-password\n \n \n \n \n
\n
\n
\n
\n
\n',styles:[":host .tb-mqtt-credentials-panel-group{margin:0 6px}\n"]}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class Bn extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.ackValues=["all","-1","0","1"],this.ToByteStandartCharsetTypesValues=Pt,this.ToByteStandartCharsetTypeTranslationMap=wt}configForm(){return this.kafkaConfigForm}onConfigurationSet(e){this.kafkaConfigForm=this.fb.group({topicPattern:[e?e.topicPattern:null,[D.required]],keyPattern:[e?e.keyPattern:null],bootstrapServers:[e?e.bootstrapServers:null,[D.required]],retries:[e?e.retries:null,[D.min(0)]],batchSize:[e?e.batchSize:null,[D.min(0)]],linger:[e?e.linger:null,[D.min(0)]],bufferMemory:[e?e.bufferMemory:null,[D.min(0)]],acks:[e?e.acks:null,[D.required]],keySerializer:[e?e.keySerializer:null,[D.required]],valueSerializer:[e?e.valueSerializer:null,[D.required]],otherProperties:[e?e.otherProperties:null,[]],addMetadataKeyValuesAsKafkaHeaders:[!!e&&e.addMetadataKeyValuesAsKafkaHeaders,[]],kafkaHeadersCharset:[e?e.kafkaHeadersCharset:null,[]]})}validatorTriggers(){return["addMetadataKeyValuesAsKafkaHeaders"]}updateValidators(e){this.kafkaConfigForm.get("addMetadataKeyValuesAsKafkaHeaders").value?this.kafkaConfigForm.get("kafkaHeadersCharset").setValidators([D.required]):this.kafkaConfigForm.get("kafkaHeadersCharset").setValidators([]),this.kafkaConfigForm.get("kafkaHeadersCharset").updateValueAndValidity({emitEvent:e})}}e("KafkaConfigComponent",Bn),Bn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Bn,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Bn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Bn,selector:"tb-external-node-kafka-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.topic-pattern\n \n \n {{ \'tb.rulenode.topic-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.key-pattern\n \n tb.rulenode.general-pattern-hint\n \n
tb.rulenode.key-pattern-hint
\n \n tb.rulenode.bootstrap-servers\n \n \n {{ \'tb.rulenode.bootstrap-servers-required\' | translate }}\n \n \n \n tb.rulenode.retries\n \n \n {{ \'tb.rulenode.min-retries-message\' | translate }}\n \n \n \n tb.rulenode.batch-size-bytes\n \n \n {{ \'tb.rulenode.min-batch-size-bytes-message\' | translate }}\n \n \n \n tb.rulenode.linger-ms\n \n \n {{ \'tb.rulenode.min-linger-ms-message\' | translate }}\n \n \n \n tb.rulenode.buffer-memory-bytes\n \n \n {{ \'tb.rulenode.min-buffer-memory-bytes-message\' | translate }}\n \n \n \n tb.rulenode.acks\n \n \n {{ ackValue }}\n \n \n \n \n tb.rulenode.key-serializer\n \n \n {{ \'tb.rulenode.key-serializer-required\' | translate }}\n \n \n \n tb.rulenode.value-serializer\n \n \n {{ \'tb.rulenode.value-serializer-required\' | translate }}\n \n \n \n \n \n \n {{ \'tb.rulenode.add-metadata-key-values-as-kafka-headers\' | translate }}\n \n
tb.rulenode.add-metadata-key-values-as-kafka-headers-hint
\n \n tb.rulenode.charset-encoding\n \n \n {{ ToByteStandartCharsetTypeTranslationMap.get(charset) | translate }}\n \n \n \n
\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:U.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:on,selector:"tb-kv-map-config-old",inputs:["disabled","uniqueKeyValuePairValidator","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Bn,decorators:[{type:n,args:[{selector:"tb-external-node-kafka-config",template:'
\n \n tb.rulenode.topic-pattern\n \n \n {{ \'tb.rulenode.topic-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.key-pattern\n \n tb.rulenode.general-pattern-hint\n \n
tb.rulenode.key-pattern-hint
\n \n tb.rulenode.bootstrap-servers\n \n \n {{ \'tb.rulenode.bootstrap-servers-required\' | translate }}\n \n \n \n tb.rulenode.retries\n \n \n {{ \'tb.rulenode.min-retries-message\' | translate }}\n \n \n \n tb.rulenode.batch-size-bytes\n \n \n {{ \'tb.rulenode.min-batch-size-bytes-message\' | translate }}\n \n \n \n tb.rulenode.linger-ms\n \n \n {{ \'tb.rulenode.min-linger-ms-message\' | translate }}\n \n \n \n tb.rulenode.buffer-memory-bytes\n \n \n {{ \'tb.rulenode.min-buffer-memory-bytes-message\' | translate }}\n \n \n \n tb.rulenode.acks\n \n \n {{ ackValue }}\n \n \n \n \n tb.rulenode.key-serializer\n \n \n {{ \'tb.rulenode.key-serializer-required\' | translate }}\n \n \n \n tb.rulenode.value-serializer\n \n \n {{ \'tb.rulenode.value-serializer-required\' | translate }}\n \n \n \n \n \n \n {{ \'tb.rulenode.add-metadata-key-values-as-kafka-headers\' | translate }}\n \n
tb.rulenode.add-metadata-key-values-as-kafka-headers-hint
\n \n tb.rulenode.charset-encoding\n \n \n {{ ToByteStandartCharsetTypeTranslationMap.get(charset) | translate }}\n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class Kn extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.mqttConfigForm}onConfigurationSet(e){this.mqttConfigForm=this.fb.group({topicPattern:[e?e.topicPattern:null,[D.required]],host:[e?e.host:null,[D.required]],port:[e?e.port:null,[D.required,D.min(1),D.max(65535)]],connectTimeoutSec:[e?e.connectTimeoutSec:null,[D.required,D.min(1),D.max(200)]],clientId:[e?e.clientId:null,[]],appendClientIdSuffix:[{value:!!e&&e.appendClientIdSuffix,disabled:!(e&&oe(e.clientId))},[]],cleanSession:[!!e&&e.cleanSession,[]],retainedMessage:[!!e&&e.retainedMessage,[]],ssl:[!!e&&e.ssl,[]],credentials:[e?e.credentials:null,[]]})}updateValidators(e){oe(this.mqttConfigForm.get("clientId").value)?this.mqttConfigForm.get("appendClientIdSuffix").enable({emitEvent:!1}):this.mqttConfigForm.get("appendClientIdSuffix").disable({emitEvent:!1}),this.mqttConfigForm.get("appendClientIdSuffix").updateValueAndValidity({emitEvent:e})}validatorTriggers(){return["clientId"]}}e("MqttConfigComponent",Kn),Kn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Kn,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Kn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Kn,selector:"tb-external-node-mqtt-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.topic-pattern\n \n \n {{ \'tb.rulenode.topic-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n \n tb.rulenode.host\n \n \n {{ \'tb.rulenode.host-required\' | translate }}\n \n \n \n tb.rulenode.port\n \n \n {{ \'tb.rulenode.port-required\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n \n tb.rulenode.connect-timeout\n \n \n {{ \'tb.rulenode.connect-timeout-required\' | translate }}\n \n \n {{ \'tb.rulenode.connect-timeout-range\' | translate }}\n \n \n {{ \'tb.rulenode.connect-timeout-range\' | translate }}\n \n \n
\n \n tb.rulenode.client-id\n \n {{\'tb.rulenode.client-id-hint\' | translate}}\n \n \n {{ \'tb.rulenode.append-client-id-suffix\' | translate }}\n \n
{{ "tb.rulenode.client-id-suffix-hint" | translate }}
\n \n {{ \'tb.rulenode.clean-session\' | translate }}\n \n \n {{ "tb.rulenode.retained-message" | translate }}\n \n \n {{ \'tb.rulenode.enable-ssl\' | translate }}\n \n \n
\n',styles:[":host .tb-mqtt-credentials-panel-group{margin:0 6px}\n"],dependencies:[{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:U.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:vn,selector:"tb-credentials-config",inputs:["required","disableCertPemCredentials","passwordFieldRequired"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Kn,decorators:[{type:n,args:[{selector:"tb-external-node-mqtt-config",template:'
\n \n tb.rulenode.topic-pattern\n \n \n {{ \'tb.rulenode.topic-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n \n tb.rulenode.host\n \n \n {{ \'tb.rulenode.host-required\' | translate }}\n \n \n \n tb.rulenode.port\n \n \n {{ \'tb.rulenode.port-required\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n \n tb.rulenode.connect-timeout\n \n \n {{ \'tb.rulenode.connect-timeout-required\' | translate }}\n \n \n {{ \'tb.rulenode.connect-timeout-range\' | translate }}\n \n \n {{ \'tb.rulenode.connect-timeout-range\' | translate }}\n \n \n
\n \n tb.rulenode.client-id\n \n {{\'tb.rulenode.client-id-hint\' | translate}}\n \n \n {{ \'tb.rulenode.append-client-id-suffix\' | translate }}\n \n
{{ "tb.rulenode.client-id-suffix-hint" | translate }}
\n \n {{ \'tb.rulenode.clean-session\' | translate }}\n \n \n {{ "tb.rulenode.retained-message" | translate }}\n \n \n {{ \'tb.rulenode.enable-ssl\' | translate }}\n \n \n
\n',styles:[":host .tb-mqtt-credentials-panel-group{margin:0 6px}\n"]}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class Un extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.notificationType=q,this.entityType=x}configForm(){return this.notificationConfigForm}onConfigurationSet(e){this.notificationConfigForm=this.fb.group({templateId:[e?e.templateId:null,[D.required]],targets:[e?e.targets:[],[D.required]]})}}e("NotificationConfigComponent",Un),Un.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Un,deps:[{token:G.Store},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Un.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Un,selector:"tb-external-node-notification-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n
\n',dependencies:[{kind:"component",type:He.EntityListComponent,selector:"tb-entity-list",inputs:["entityType","subType","labelText","placeholderText","requiredText","required","disabled","subscriptSizing","hint"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:je.TemplateAutocompleteComponent,selector:"tb-template-autocomplete",inputs:["required","allowCreate","allowEdit","disabled","notificationTypes"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Un,decorators:[{type:n,args:[{selector:"tb-external-node-notification-config",template:'
\n \n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.FormBuilder}]}});class _n extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.pubSubConfigForm}onConfigurationSet(e){this.pubSubConfigForm=this.fb.group({projectId:[e?e.projectId:null,[D.required]],topicName:[e?e.topicName:null,[D.required]],serviceAccountKey:[e?e.serviceAccountKey:null,[D.required]],serviceAccountKeyFileName:[e?e.serviceAccountKeyFileName:null,[D.required]],messageAttributes:[e?e.messageAttributes:null,[]]})}}e("PubSubConfigComponent",_n),_n.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:_n,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),_n.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:_n,selector:"tb-external-node-pub-sub-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.gcp-project-id\n \n \n {{ \'tb.rulenode.gcp-project-id-required\' | translate }}\n \n \n \n tb.rulenode.pubsub-topic-name\n \n \n {{ \'tb.rulenode.pubsub-topic-name-required\' | translate }}\n \n \n \n \n \n
\n \n \n
\n',dependencies:[{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:Be.FileInputComponent,selector:"tb-file-input",inputs:["label","accept","noFileText","inputId","allowedExtensions","dropLabel","contentConvertFunction","required","requiredAsError","disabled","existingFileName","readAsBinary","workFromFileObj","multipleFile"],outputs:["fileNameChanged"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:on,selector:"tb-kv-map-config-old",inputs:["disabled","uniqueKeyValuePairValidator","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{kind:"pipe",type:fe.SafePipe,name:"safe"},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:_n,decorators:[{type:n,args:[{selector:"tb-external-node-pub-sub-config",template:'
\n \n tb.rulenode.gcp-project-id\n \n \n {{ \'tb.rulenode.gcp-project-id-required\' | translate }}\n \n \n \n tb.rulenode.pubsub-topic-name\n \n \n {{ \'tb.rulenode.pubsub-topic-name-required\' | translate }}\n \n \n \n \n \n
\n \n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class zn extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.messageProperties=[null,"BASIC","TEXT_PLAIN","MINIMAL_BASIC","MINIMAL_PERSISTENT_BASIC","PERSISTENT_BASIC","PERSISTENT_TEXT_PLAIN"]}configForm(){return this.rabbitMqConfigForm}onConfigurationSet(e){this.rabbitMqConfigForm=this.fb.group({exchangeNamePattern:[e?e.exchangeNamePattern:null,[]],routingKeyPattern:[e?e.routingKeyPattern:null,[]],messageProperties:[e?e.messageProperties:null,[]],host:[e?e.host:null,[D.required]],port:[e?e.port:null,[D.required,D.min(1),D.max(65535)]],virtualHost:[e?e.virtualHost:null,[]],username:[e?e.username:null,[]],password:[e?e.password:null,[]],automaticRecoveryEnabled:[!!e&&e.automaticRecoveryEnabled,[]],connectionTimeout:[e?e.connectionTimeout:null,[D.min(0)]],handshakeTimeout:[e?e.handshakeTimeout:null,[D.min(0)]],clientProperties:[e?e.clientProperties:null,[]]})}}e("RabbitMqConfigComponent",zn),zn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:zn,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),zn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:zn,selector:"tb-external-node-rabbit-mq-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.exchange-name-pattern\n \n \n \n tb.rulenode.routing-key-pattern\n \n \n \n tb.rulenode.message-properties\n \n \n {{ property }}\n \n \n \n
\n \n tb.rulenode.host\n \n \n {{ \'tb.rulenode.host-required\' | translate }}\n \n \n \n tb.rulenode.port\n \n \n {{ \'tb.rulenode.port-required\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n
\n \n tb.rulenode.virtual-host\n \n \n \n tb.rulenode.username\n \n \n \n tb.rulenode.password\n \n \n \n \n {{ \'tb.rulenode.automatic-recovery\' | translate }}\n \n \n tb.rulenode.connection-timeout-ms\n \n \n {{ \'tb.rulenode.min-connection-timeout-ms-message\' | translate }}\n \n \n \n tb.rulenode.handshake-timeout-ms\n \n \n {{ \'tb.rulenode.min-handshake-timeout-ms-message\' | translate }}\n \n \n \n \n \n
\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:U.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:z.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Ke.TogglePasswordComponent,selector:"tb-toggle-password"},{kind:"component",type:on,selector:"tb-kv-map-config-old",inputs:["disabled","uniqueKeyValuePairValidator","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:zn,decorators:[{type:n,args:[{selector:"tb-external-node-rabbit-mq-config",template:'
\n \n tb.rulenode.exchange-name-pattern\n \n \n \n tb.rulenode.routing-key-pattern\n \n \n \n tb.rulenode.message-properties\n \n \n {{ property }}\n \n \n \n
\n \n tb.rulenode.host\n \n \n {{ \'tb.rulenode.host-required\' | translate }}\n \n \n \n tb.rulenode.port\n \n \n {{ \'tb.rulenode.port-required\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n
\n \n tb.rulenode.virtual-host\n \n \n \n tb.rulenode.username\n \n \n \n tb.rulenode.password\n \n \n \n \n {{ \'tb.rulenode.automatic-recovery\' | translate }}\n \n \n tb.rulenode.connection-timeout-ms\n \n \n {{ \'tb.rulenode.min-connection-timeout-ms-message\' | translate }}\n \n \n \n tb.rulenode.handshake-timeout-ms\n \n \n {{ \'tb.rulenode.min-handshake-timeout-ms-message\' | translate }}\n \n \n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class Hn extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.proxySchemes=["http","https"],this.httpRequestTypes=Object.keys(Vt)}configForm(){return this.restApiCallConfigForm}onConfigurationSet(e){this.restApiCallConfigForm=this.fb.group({restEndpointUrlPattern:[e?e.restEndpointUrlPattern:null,[D.required]],requestMethod:[e?e.requestMethod:null,[D.required]],useSimpleClientHttpFactory:[!!e&&e.useSimpleClientHttpFactory,[]],parseToPlainText:[!!e&&e.parseToPlainText,[]],ignoreRequestBody:[!!e&&e.ignoreRequestBody,[]],enableProxy:[!!e&&e.enableProxy,[]],useSystemProxyProperties:[!!e&&e.enableProxy,[]],proxyScheme:[e?e.proxyHost:null,[]],proxyHost:[e?e.proxyHost:null,[]],proxyPort:[e?e.proxyPort:null,[]],proxyUser:[e?e.proxyUser:null,[]],proxyPassword:[e?e.proxyPassword:null,[]],readTimeoutMs:[e?e.readTimeoutMs:null,[]],maxParallelRequestsCount:[e?e.maxParallelRequestsCount:null,[D.min(0)]],headers:[e?e.headers:null,[]],useRedisQueueForMsgPersistence:[!!e&&e.useRedisQueueForMsgPersistence,[]],trimQueue:[!!e&&e.trimQueue,[]],maxQueueSize:[e?e.maxQueueSize:null,[]],credentials:[e?e.credentials:null,[]]})}validatorTriggers(){return["useSimpleClientHttpFactory","useRedisQueueForMsgPersistence","enableProxy","useSystemProxyProperties"]}updateValidators(e){const t=this.restApiCallConfigForm.get("useSimpleClientHttpFactory").value,n=this.restApiCallConfigForm.get("useRedisQueueForMsgPersistence").value,r=this.restApiCallConfigForm.get("enableProxy").value,o=this.restApiCallConfigForm.get("useSystemProxyProperties").value;r&&!o?(this.restApiCallConfigForm.get("proxyHost").setValidators(r?[D.required]:[]),this.restApiCallConfigForm.get("proxyPort").setValidators(r?[D.required,D.min(1),D.max(65535)]:[])):(this.restApiCallConfigForm.get("proxyHost").setValidators([]),this.restApiCallConfigForm.get("proxyPort").setValidators([]),t?this.restApiCallConfigForm.get("readTimeoutMs").setValidators([]):this.restApiCallConfigForm.get("readTimeoutMs").setValidators([D.min(0)])),n?this.restApiCallConfigForm.get("maxQueueSize").setValidators([D.min(0)]):this.restApiCallConfigForm.get("maxQueueSize").setValidators([]),this.restApiCallConfigForm.get("readTimeoutMs").updateValueAndValidity({emitEvent:e}),this.restApiCallConfigForm.get("maxQueueSize").updateValueAndValidity({emitEvent:e}),this.restApiCallConfigForm.get("proxyHost").updateValueAndValidity({emitEvent:e}),this.restApiCallConfigForm.get("proxyPort").updateValueAndValidity({emitEvent:e}),this.restApiCallConfigForm.get("credentials").updateValueAndValidity({emitEvent:e})}}e("RestApiCallConfigComponent",Hn),Hn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Hn,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Hn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Hn,selector:"tb-external-node-rest-api-call-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.endpoint-url-pattern\n \n \n {{ \'tb.rulenode.endpoint-url-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.request-method\n \n \n {{ requestType }}\n \n \n \n \n {{ \'tb.rulenode.enable-proxy\' | translate }}\n \n \n {{ \'tb.rulenode.use-simple-client-http-factory\' | translate }}\n \n \n {{ \'tb.rulenode.parse-to-plain-text\' | translate }}\n \n
tb.rulenode.parse-to-plain-text-hint
\n \n {{ \'tb.rulenode.ignore-request-body\' | translate }}\n \n
\n \n {{ \'tb.rulenode.use-system-proxy-properties\' | translate }}\n \n
\n
\n \n tb.rulenode.proxy-scheme\n \n \n {{ proxyScheme }}\n \n \n \n \n tb.rulenode.proxy-host\n \n \n {{ \'tb.rulenode.proxy-host-required\' | translate }}\n \n \n \n tb.rulenode.proxy-port\n \n \n {{ \'tb.rulenode.proxy-port-required\' | translate }}\n \n \n {{ \'tb.rulenode.proxy-port-range\' | translate }}\n \n \n
\n \n tb.rulenode.proxy-user\n \n \n \n tb.rulenode.proxy-password\n \n \n
\n
\n \n tb.rulenode.read-timeout\n \n tb.rulenode.read-timeout-hint\n \n \n tb.rulenode.max-parallel-requests-count\n \n tb.rulenode.max-parallel-requests-count-hint\n \n \n
\n \n \n \n {{ \'tb.rulenode.use-redis-queue\' | translate }}\n \n
\n \n {{ \'tb.rulenode.trim-redis-queue\' | translate }}\n \n \n tb.rulenode.redis-queue-max-size\n \n \n
\n \n
\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:U.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:vn,selector:"tb-credentials-config",inputs:["required","disableCertPemCredentials","passwordFieldRequired"]},{kind:"component",type:on,selector:"tb-kv-map-config-old",inputs:["disabled","uniqueKeyValuePairValidator","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{kind:"pipe",type:fe.SafePipe,name:"safe"},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Hn,decorators:[{type:n,args:[{selector:"tb-external-node-rest-api-call-config",template:'
\n \n tb.rulenode.endpoint-url-pattern\n \n \n {{ \'tb.rulenode.endpoint-url-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.request-method\n \n \n {{ requestType }}\n \n \n \n \n {{ \'tb.rulenode.enable-proxy\' | translate }}\n \n \n {{ \'tb.rulenode.use-simple-client-http-factory\' | translate }}\n \n \n {{ \'tb.rulenode.parse-to-plain-text\' | translate }}\n \n
tb.rulenode.parse-to-plain-text-hint
\n \n {{ \'tb.rulenode.ignore-request-body\' | translate }}\n \n
\n \n {{ \'tb.rulenode.use-system-proxy-properties\' | translate }}\n \n
\n
\n \n tb.rulenode.proxy-scheme\n \n \n {{ proxyScheme }}\n \n \n \n \n tb.rulenode.proxy-host\n \n \n {{ \'tb.rulenode.proxy-host-required\' | translate }}\n \n \n \n tb.rulenode.proxy-port\n \n \n {{ \'tb.rulenode.proxy-port-required\' | translate }}\n \n \n {{ \'tb.rulenode.proxy-port-range\' | translate }}\n \n \n
\n \n tb.rulenode.proxy-user\n \n \n \n tb.rulenode.proxy-password\n \n \n
\n
\n \n tb.rulenode.read-timeout\n \n tb.rulenode.read-timeout-hint\n \n \n tb.rulenode.max-parallel-requests-count\n \n tb.rulenode.max-parallel-requests-count-hint\n \n \n
\n \n \n \n {{ \'tb.rulenode.use-redis-queue\' | translate }}\n \n
\n \n {{ \'tb.rulenode.trim-redis-queue\' | translate }}\n \n \n tb.rulenode.redis-queue-max-size\n \n \n
\n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class jn extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.smtpProtocols=["smtp","smtps"],this.tlsVersions=["TLSv1","TLSv1.1","TLSv1.2","TLSv1.3"]}configForm(){return this.sendEmailConfigForm}onConfigurationSet(e){this.sendEmailConfigForm=this.fb.group({useSystemSmtpSettings:[!!e&&e.useSystemSmtpSettings,[]],smtpProtocol:[e?e.smtpProtocol:null,[]],smtpHost:[e?e.smtpHost:null,[]],smtpPort:[e?e.smtpPort:null,[]],timeout:[e?e.timeout:null,[]],enableTls:[!!e&&e.enableTls,[]],tlsVersion:[e?e.tlsVersion:null,[]],enableProxy:[!!e&&e.enableProxy,[]],proxyHost:[e?e.proxyHost:null,[]],proxyPort:[e?e.proxyPort:null,[]],proxyUser:[e?e.proxyUser:null,[]],proxyPassword:[e?e.proxyPassword:null,[]],username:[e?e.username:null,[]],password:[e?e.password:null,[]]})}validatorTriggers(){return["useSystemSmtpSettings","enableProxy"]}updateValidators(e){const t=this.sendEmailConfigForm.get("useSystemSmtpSettings").value,n=this.sendEmailConfigForm.get("enableProxy").value;t?(this.sendEmailConfigForm.get("smtpProtocol").setValidators([]),this.sendEmailConfigForm.get("smtpHost").setValidators([]),this.sendEmailConfigForm.get("smtpPort").setValidators([]),this.sendEmailConfigForm.get("timeout").setValidators([]),this.sendEmailConfigForm.get("proxyHost").setValidators([]),this.sendEmailConfigForm.get("proxyPort").setValidators([])):(this.sendEmailConfigForm.get("smtpProtocol").setValidators([D.required]),this.sendEmailConfigForm.get("smtpHost").setValidators([D.required]),this.sendEmailConfigForm.get("smtpPort").setValidators([D.required,D.min(1),D.max(65535)]),this.sendEmailConfigForm.get("timeout").setValidators([D.required,D.min(0)]),this.sendEmailConfigForm.get("proxyHost").setValidators(n?[D.required]:[]),this.sendEmailConfigForm.get("proxyPort").setValidators(n?[D.required,D.min(1),D.max(65535)]:[])),this.sendEmailConfigForm.get("smtpProtocol").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("smtpHost").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("smtpPort").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("timeout").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("proxyHost").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("proxyPort").updateValueAndValidity({emitEvent:e})}}e("SendEmailConfigComponent",jn),jn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:jn,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),jn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:jn,selector:"tb-external-node-send-email-config",usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.use-system-smtp-settings\' | translate }}\n \n
\n \n tb.rulenode.smtp-protocol\n \n \n {{ smtpProtocol.toUpperCase() }}\n \n \n \n
\n \n tb.rulenode.smtp-host\n \n \n {{ \'tb.rulenode.smtp-host-required\' | translate }}\n \n \n \n tb.rulenode.smtp-port\n \n \n {{ \'tb.rulenode.smtp-port-required\' | translate }}\n \n \n {{ \'tb.rulenode.smtp-port-range\' | translate }}\n \n \n {{ \'tb.rulenode.smtp-port-range\' | translate }}\n \n \n
\n \n tb.rulenode.timeout-msec\n \n \n {{ \'tb.rulenode.timeout-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-timeout-msec-message\' | translate }}\n \n \n \n {{ \'tb.rulenode.enable-tls\' | translate }}\n \n \n tb.rulenode.tls-version\n \n \n {{ tlsVersion }}\n \n \n \n \n {{ \'tb.rulenode.enable-proxy\' | translate }}\n \n
\n
\n \n tb.rulenode.proxy-host\n \n \n {{ \'tb.rulenode.proxy-host-required\' | translate }}\n \n \n \n tb.rulenode.proxy-port\n \n \n {{ \'tb.rulenode.proxy-port-required\' | translate }}\n \n \n {{ \'tb.rulenode.proxy-port-range\' | translate }}\n \n \n
\n \n tb.rulenode.proxy-user\n \n \n \n tb.rulenode.proxy-password\n \n \n
\n \n tb.rulenode.username\n \n \n \n tb.rulenode.password\n \n \n \n
\n
\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:Qe.TbCheckboxComponent,selector:"tb-checkbox",inputs:["disabled","trueValue","falseValue"],outputs:["valueChange"]},{kind:"component",type:U.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:z.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Ke.TogglePasswordComponent,selector:"tb-toggle-password"},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:jn,decorators:[{type:n,args:[{selector:"tb-external-node-send-email-config",template:'
\n \n {{ \'tb.rulenode.use-system-smtp-settings\' | translate }}\n \n
\n \n tb.rulenode.smtp-protocol\n \n \n {{ smtpProtocol.toUpperCase() }}\n \n \n \n
\n \n tb.rulenode.smtp-host\n \n \n {{ \'tb.rulenode.smtp-host-required\' | translate }}\n \n \n \n tb.rulenode.smtp-port\n \n \n {{ \'tb.rulenode.smtp-port-required\' | translate }}\n \n \n {{ \'tb.rulenode.smtp-port-range\' | translate }}\n \n \n {{ \'tb.rulenode.smtp-port-range\' | translate }}\n \n \n
\n \n tb.rulenode.timeout-msec\n \n \n {{ \'tb.rulenode.timeout-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-timeout-msec-message\' | translate }}\n \n \n \n {{ \'tb.rulenode.enable-tls\' | translate }}\n \n \n tb.rulenode.tls-version\n \n \n {{ tlsVersion }}\n \n \n \n \n {{ \'tb.rulenode.enable-proxy\' | translate }}\n \n
\n
\n \n tb.rulenode.proxy-host\n \n \n {{ \'tb.rulenode.proxy-host-required\' | translate }}\n \n \n \n tb.rulenode.proxy-port\n \n \n {{ \'tb.rulenode.proxy-port-required\' | translate }}\n \n \n {{ \'tb.rulenode.proxy-port-range\' | translate }}\n \n \n
\n \n tb.rulenode.proxy-user\n \n \n \n tb.rulenode.proxy-password\n \n \n
\n \n tb.rulenode.username\n \n \n \n tb.rulenode.password\n \n \n \n
\n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class Qn extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.sendSmsConfigForm}onConfigurationSet(e){this.sendSmsConfigForm=this.fb.group({numbersToTemplate:[e?e.numbersToTemplate:null,[D.required]],smsMessageTemplate:[e?e.smsMessageTemplate:null,[D.required]],useSystemSmsSettings:[!!e&&e.useSystemSmsSettings,[]],smsProviderConfiguration:[e?e.smsProviderConfiguration:null,[]]})}validatorTriggers(){return["useSystemSmsSettings"]}updateValidators(e){this.sendSmsConfigForm.get("useSystemSmsSettings").value?this.sendSmsConfigForm.get("smsProviderConfiguration").setValidators([]):this.sendSmsConfigForm.get("smsProviderConfiguration").setValidators([D.required]),this.sendSmsConfigForm.get("smsProviderConfiguration").updateValueAndValidity({emitEvent:e})}}e("SendSmsConfigComponent",Qn),Qn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Qn,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Qn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Qn,selector:"tb-external-node-send-sms-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.numbers-to-template\n \n \n {{ \'tb.rulenode.numbers-to-template-required\' | translate }}\n \n \n \n \n tb.rulenode.sms-message-template\n \n \n {{ \'tb.rulenode.sms-message-template-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.use-system-sms-settings\' | translate }}\n \n \n \n
\n',dependencies:[{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:U.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:$e.SmsProviderConfigurationComponent,selector:"tb-sms-provider-configuration",inputs:["required","disabled"]},{kind:"pipe",type:fe.SafePipe,name:"safe"},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Qn,decorators:[{type:n,args:[{selector:"tb-external-node-send-sms-config",template:'
\n \n tb.rulenode.numbers-to-template\n \n \n {{ \'tb.rulenode.numbers-to-template-required\' | translate }}\n \n \n \n \n tb.rulenode.sms-message-template\n \n \n {{ \'tb.rulenode.sms-message-template-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.use-system-sms-settings\' | translate }}\n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class $n extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.slackChanelTypes=Object.keys(A),this.slackChanelTypesTranslateMap=M}configForm(){return this.slackConfigForm}onConfigurationSet(e){this.slackConfigForm=this.fb.group({botToken:[e?e.botToken:null],useSystemSettings:[!!e&&e.useSystemSettings],messageTemplate:[e?e.messageTemplate:null,[D.required]],conversationType:[e?e.conversationType:null,[D.required]],conversation:[e?e.conversation:null,[D.required]]})}validatorTriggers(){return["useSystemSettings"]}updateValidators(e){this.slackConfigForm.get("useSystemSettings").value?this.slackConfigForm.get("botToken").clearValidators():this.slackConfigForm.get("botToken").setValidators([D.required]),this.slackConfigForm.get("botToken").updateValueAndValidity({emitEvent:e})}}e("SlackConfigComponent",$n),$n.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:$n,deps:[{token:G.Store},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),$n.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:$n,selector:"tb-external-node-slack-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.message-template\n \n \n {{ \'tb.rulenode.message-template-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.use-system-slack-settings\' | translate }}\n \n \n tb.rulenode.slack-api-token\n \n \n {{ \'tb.rulenode.slack-api-token-required\' | translate }}\n \n \n \n \n \n {{ slackChanelTypesTranslateMap.get(slackChanelType) | translate }}\n \n \n \n \n
\n',styles:[":host .tb-title{display:block;padding-bottom:6px}:host ::ng-deep .mat-mdc-radio-group{display:flex;flex-direction:row;margin-bottom:22px;gap:12px}:host ::ng-deep .mat-mdc-radio-group .mat-mdc-radio-button{flex:1 1 100%;padding:4px;border:1px solid rgba(0,0,0,.12);border-radius:6px}@media screen and (max-width: 599px){:host ::ng-deep .mat-mdc-radio-group{flex-direction:column}}\n"],dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:U.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Je.MatRadioGroup,selector:"mat-radio-group",exportAs:["matRadioGroup"]},{kind:"component",type:Je.MatRadioButton,selector:"mat-radio-button",inputs:["disableRipple","tabIndex"],exportAs:["matRadioButton"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Ye.SlackConversationAutocompleteComponent,selector:"tb-slack-conversation-autocomplete",inputs:["labelText","requiredText","required","disabled","slackChanelType","token"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:$n,decorators:[{type:n,args:[{selector:"tb-external-node-slack-config",template:'
\n \n tb.rulenode.message-template\n \n \n {{ \'tb.rulenode.message-template-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.use-system-slack-settings\' | translate }}\n \n \n tb.rulenode.slack-api-token\n \n \n {{ \'tb.rulenode.slack-api-token-required\' | translate }}\n \n \n \n \n \n {{ slackChanelTypesTranslateMap.get(slackChanelType) | translate }}\n \n \n \n \n
\n',styles:[":host .tb-title{display:block;padding-bottom:6px}:host ::ng-deep .mat-mdc-radio-group{display:flex;flex-direction:row;margin-bottom:22px;gap:12px}:host ::ng-deep .mat-mdc-radio-group .mat-mdc-radio-button{flex:1 1 100%;padding:4px;border:1px solid rgba(0,0,0,.12);border-radius:6px}@media screen and (max-width: 599px){:host ::ng-deep .mat-mdc-radio-group{flex-direction:column}}\n"]}]}],ctorParameters:function(){return[{type:G.Store},{type:E.FormBuilder}]}});class Jn extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.snsConfigForm}onConfigurationSet(e){this.snsConfigForm=this.fb.group({topicArnPattern:[e?e.topicArnPattern:null,[D.required]],accessKeyId:[e?e.accessKeyId:null,[D.required]],secretAccessKey:[e?e.secretAccessKey:null,[D.required]],region:[e?e.region:null,[D.required]]})}}e("SnsConfigComponent",Jn),Jn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Jn,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Jn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Jn,selector:"tb-external-node-sns-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.topic-arn-pattern\n \n \n {{ \'tb.rulenode.topic-arn-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.aws-access-key-id\n \n \n {{ \'tb.rulenode.aws-access-key-id-required\' | translate }}\n \n \n \n tb.rulenode.aws-secret-access-key\n \n \n {{ \'tb.rulenode.aws-secret-access-key-required\' | translate }}\n \n \n \n tb.rulenode.aws-region\n \n \n {{ \'tb.rulenode.aws-region-required\' | translate }}\n \n \n
\n',dependencies:[{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Jn,decorators:[{type:n,args:[{selector:"tb-external-node-sns-config",template:'
\n \n tb.rulenode.topic-arn-pattern\n \n \n {{ \'tb.rulenode.topic-arn-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.aws-access-key-id\n \n \n {{ \'tb.rulenode.aws-access-key-id-required\' | translate }}\n \n \n \n tb.rulenode.aws-secret-access-key\n \n \n {{ \'tb.rulenode.aws-secret-access-key-required\' | translate }}\n \n \n \n tb.rulenode.aws-region\n \n \n {{ \'tb.rulenode.aws-region-required\' | translate }}\n \n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class Yn extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.sqsQueueType=qt,this.sqsQueueTypes=Object.keys(qt),this.sqsQueueTypeTranslationsMap=At}configForm(){return this.sqsConfigForm}onConfigurationSet(e){this.sqsConfigForm=this.fb.group({queueType:[e?e.queueType:null,[D.required]],queueUrlPattern:[e?e.queueUrlPattern:null,[D.required]],delaySeconds:[e?e.delaySeconds:null,[D.min(0),D.max(900)]],messageAttributes:[e?e.messageAttributes:null,[]],accessKeyId:[e?e.accessKeyId:null,[D.required]],secretAccessKey:[e?e.secretAccessKey:null,[D.required]],region:[e?e.region:null,[D.required]]})}}e("SqsConfigComponent",Yn),Yn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Yn,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Yn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Yn,selector:"tb-external-node-sqs-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.queue-type\n \n \n {{ sqsQueueTypeTranslationsMap.get(type) | translate }}\n \n \n \n \n tb.rulenode.queue-url-pattern\n \n \n {{ \'tb.rulenode.queue-url-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.delay-seconds\n \n \n {{ \'tb.rulenode.min-delay-seconds-message\' | translate }}\n \n \n {{ \'tb.rulenode.max-delay-seconds-message\' | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.aws-access-key-id\n \n \n {{ \'tb.rulenode.aws-access-key-id-required\' | translate }}\n \n \n \n tb.rulenode.aws-secret-access-key\n \n \n {{ \'tb.rulenode.aws-secret-access-key-required\' | translate }}\n \n \n \n tb.rulenode.aws-region\n \n \n {{ \'tb.rulenode.aws-region-required\' | translate }}\n \n \n
\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:on,selector:"tb-kv-map-config-old",inputs:["disabled","uniqueKeyValuePairValidator","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{kind:"pipe",type:fe.SafePipe,name:"safe"},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Yn,decorators:[{type:n,args:[{selector:"tb-external-node-sqs-config",template:'
\n \n tb.rulenode.queue-type\n \n \n {{ sqsQueueTypeTranslationsMap.get(type) | translate }}\n \n \n \n \n tb.rulenode.queue-url-pattern\n \n \n {{ \'tb.rulenode.queue-url-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.delay-seconds\n \n \n {{ \'tb.rulenode.min-delay-seconds-message\' | translate }}\n \n \n {{ \'tb.rulenode.max-delay-seconds-message\' | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.aws-access-key-id\n \n \n {{ \'tb.rulenode.aws-access-key-id-required\' | translate }}\n \n \n \n tb.rulenode.aws-secret-access-key\n \n \n {{ \'tb.rulenode.aws-secret-access-key-required\' | translate }}\n \n \n \n tb.rulenode.aws-region\n \n \n {{ \'tb.rulenode.aws-region-required\' | translate }}\n \n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class Wn{}e("RulenodeCoreConfigExternalModule",Wn),Wn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Wn,deps:[],target:t.ɵɵFactoryTarget.NgModule}),Wn.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.9",ngImport:t,type:Wn,declarations:[Jn,Yn,_n,Bn,Kn,Un,zn,Hn,jn,On,Qn,$n],imports:[K,I,Ve,In],exports:[Jn,Yn,_n,Bn,Kn,Un,zn,Hn,jn,On,Qn,$n]}),Wn.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Wn,imports:[K,I,Ve,In]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Wn,decorators:[{type:l,args:[{declarations:[Jn,Yn,_n,Bn,Kn,Un,zn,Hn,jn,On,Qn,$n],imports:[K,I,Ve,In],exports:[Jn,Yn,_n,Bn,Kn,Un,zn,Hn,jn,On,Qn,$n]}]}]});class Xn extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.searchText=""}configForm(){return this.alarmStatusConfigForm}prepareInputConfig(e){return{alarmStatusList:ee(e?.alarmStatusList)?e.alarmStatusList:null}}onConfigurationSet(e){this.alarmStatusConfigForm=this.fb.group({alarmStatusList:[e.alarmStatusList,[D.required]]})}}e("CheckAlarmStatusComponent",Xn),Xn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Xn,deps:[{token:G.Store},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Xn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Xn,selector:"tb-filter-node-check-alarm-status-config",usesInheritance:!0,ngImport:t,template:'
\n
\n
tb.rulenode.alarm-status
\n
\n tb.rulenode.alarm-required\n
\n
\n \n
\n\n\n\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n'],dependencies:[{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Tn,selector:"tb-alarm-status-select"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Xn,decorators:[{type:n,args:[{selector:"tb-filter-node-check-alarm-status-config",template:'
\n
\n
tb.rulenode.alarm-status
\n
\n tb.rulenode.alarm-required\n
\n
\n \n
\n\n\n\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n']}]}],ctorParameters:function(){return[{type:G.Store},{type:E.FormBuilder}]}});class Zn extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.checkMessageConfigForm}prepareInputConfig(e){return{messageNames:ee(e?.messageNames)?e.messageNames:null,metadataNames:ee(e?.metadataNames)?e.metadataNames:null,checkAllKeys:!!ee(e?.checkAllKeys)&&e.checkAllKeys}}atLeastOne(e,t=null){return n=>{t||(t=Object.keys(n.controls));return n?.controls&&t.some((t=>!e(n.controls[t])))?null:{atLeastOne:!0}}}onConfigurationSet(e){this.checkMessageConfigForm=this.fb.group({messageNames:[e.messageNames,[]],metadataNames:[e.metadataNames,[]],checkAllKeys:[e.checkAllKeys,[]]},{validators:this.atLeastOne(D.required,["messageNames","metadataNames"])})}get touchedValidationControl(){return["messageNames","metadataNames"].some((e=>this.checkMessageConfigForm.get(e).touched))}}e("CheckMessageConfigComponent",Zn),Zn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Zn,deps:[{token:G.Store},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Zn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Zn,selector:"tb-filter-node-check-message-config",usesInheritance:!0,ngImport:t,template:'
\n
\n
tb.rulenode.fields-to-check
\n
\n tb.rulenode.at-least-one-field-required\n
\n
\n \n help\n \n \n help\n \n
\n \n {{ \'tb.rulenode.check-all-keys\' | translate }}\n \n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n'],dependencies:[{kind:"component",type:Ue.StringItemsListComponent,selector:"tb-string-items-list",inputs:["required","disabled","label","placeholder","hint","requiredText","floatLabel","appearance","editable","subscriptSizing","predefinedValues"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:z.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"directive",type:se.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"component",type:J.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Y.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Zn,decorators:[{type:n,args:[{selector:"tb-filter-node-check-message-config",template:'
\n
\n
tb.rulenode.fields-to-check
\n
\n tb.rulenode.at-least-one-field-required\n
\n
\n \n help\n \n \n help\n \n
\n \n {{ \'tb.rulenode.check-all-keys\' | translate }}\n \n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n']}]}],ctorParameters:function(){return[{type:G.Store},{type:E.FormBuilder}]}});class er extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.entitySearchDirection=Object.values(g),this.entitySearchDirectionTranslationsMap=y}configForm(){return this.checkRelationConfigForm}prepareInputConfig(e){return{checkForSingleEntity:!!ee(e?.checkForSingleEntity)&&e.checkForSingleEntity,direction:ee(e?.direction)?e.direction:null,entityType:ee(e?.entityType)?e.entityType:null,entityId:ee(e?.entityId)?e.entityId:null,relationType:ee(e?.relationType)?e.relationType:null}}onConfigurationSet(e){this.checkRelationConfigForm=this.fb.group({checkForSingleEntity:[e.checkForSingleEntity,[]],direction:[e.direction,[]],entityType:[e.entityType,e&&e.checkForSingleEntity?[D.required]:[]],entityId:[e.entityId,e&&e.checkForSingleEntity?[D.required]:[]],relationType:[e.relationType,[D.required]]})}validatorTriggers(){return["checkForSingleEntity"]}updateValidators(e){const t=this.checkRelationConfigForm.get("checkForSingleEntity").value;this.checkRelationConfigForm.get("entityType").setValidators(t?[D.required]:[]),this.checkRelationConfigForm.get("entityType").updateValueAndValidity({emitEvent:e}),this.checkRelationConfigForm.get("entityId").setValidators(t?[D.required]:[]),this.checkRelationConfigForm.get("entityId").updateValueAndValidity({emitEvent:e})}}e("CheckRelationConfigComponent",er),er.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:er,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),er.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:er,selector:"tb-filter-node-check-relation-config",usesInheritance:!0,ngImport:t,template:'
\n
tb.rulenode.relation-search-parameters
\n
\n \n {{ \'relation.direction\' | translate }}\n \n \n {{ entitySearchDirectionTranslationsMap.get(direction) | translate }} tb.rulenode.relations-query-config-direction-suffix\n \n \n \n \n \n
\n \n {{ \'tb.rulenode.check-relation-to-specific-entity\' | translate }}\n \n
\n
\n \n \n \n \n
\n
\n
\n',styles:[":host .slide-toggle{margin-bottom:18px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n'],dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:We.EntityAutocompleteComponent,selector:"tb-entity-autocomplete",inputs:["entityType","entitySubtype","excludeEntityIds","labelText","requiredText","useFullEntityId","appearance","required","disabled"],outputs:["entityChanged"]},{kind:"component",type:ge.EntityTypeSelectComponent,selector:"tb-entity-type-select",inputs:["allowedEntityTypes","useAliasEntityTypes","filterAllowedEntityTypes","showLabel","required","disabled"]},{kind:"component",type:Re.RelationTypeAutocompleteComponent,selector:"tb-relation-type-autocomplete",inputs:["showLabel","additionalClasses","appearance","required","disabled","subscriptSizing"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:J.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Y.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:er,decorators:[{type:n,args:[{selector:"tb-filter-node-check-relation-config",template:'
\n
tb.rulenode.relation-search-parameters
\n
\n \n {{ \'relation.direction\' | translate }}\n \n \n {{ entitySearchDirectionTranslationsMap.get(direction) | translate }} tb.rulenode.relations-query-config-direction-suffix\n \n \n \n \n \n
\n \n {{ \'tb.rulenode.check-relation-to-specific-entity\' | translate }}\n \n
\n
\n \n \n \n \n
\n
\n
\n',styles:[":host .slide-toggle{margin-bottom:18px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n']}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class tr extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.perimeterType=ct,this.perimeterTypes=Object.values(ct),this.perimeterTypeTranslationMap=ft,this.rangeUnits=Object.values(xt),this.rangeUnitTranslationMap=bt,this.defaultPaddingEnable=!0}configForm(){return this.geoFilterConfigForm}prepareInputConfig(e){return{latitudeKeyName:ee(e?.latitudeKeyName)?e.latitudeKeyName:null,longitudeKeyName:ee(e?.longitudeKeyName)?e.longitudeKeyName:null,perimeterType:ee(e?.perimeterType)?e.perimeterType:null,fetchPerimeterInfoFromMessageMetadata:!!ee(e?.fetchPerimeterInfoFromMessageMetadata)&&e.fetchPerimeterInfoFromMessageMetadata,perimeterKeyName:ee(e?.perimeterKeyName)?e.perimeterKeyName:null,centerLatitude:ee(e?.centerLatitude)?e.centerLatitude:null,centerLongitude:ee(e?.centerLongitude)?e.centerLongitude:null,range:ee(e?.range)?e.range:null,rangeUnit:ee(e?.rangeUnit)?e.rangeUnit:null,polygonsDefinition:ee(e?.polygonsDefinition)?e.polygonsDefinition:null}}onConfigurationSet(e){this.geoFilterConfigForm=this.fb.group({latitudeKeyName:[e.latitudeKeyName,[D.required]],longitudeKeyName:[e.longitudeKeyName,[D.required]],perimeterType:[e.perimeterType,[D.required]],fetchPerimeterInfoFromMessageMetadata:[e.fetchPerimeterInfoFromMessageMetadata,[]],perimeterKeyName:[e.perimeterKeyName,[]],centerLatitude:[e.centerLatitude,[]],centerLongitude:[e.centerLongitude,[]],range:[e.range,[]],rangeUnit:[e.rangeUnit,[]],polygonsDefinition:[e.polygonsDefinition,[]]})}validatorTriggers(){return["fetchPerimeterInfoFromMessageMetadata","perimeterType"]}updateValidators(e){const t=this.geoFilterConfigForm.get("fetchPerimeterInfoFromMessageMetadata").value,n=this.geoFilterConfigForm.get("perimeterType").value;t?this.geoFilterConfigForm.get("perimeterKeyName").setValidators([D.required]):this.geoFilterConfigForm.get("perimeterKeyName").setValidators([]),t||n!==ct.CIRCLE?(this.geoFilterConfigForm.get("centerLatitude").setValidators([]),this.geoFilterConfigForm.get("centerLongitude").setValidators([]),this.geoFilterConfigForm.get("range").setValidators([]),this.geoFilterConfigForm.get("rangeUnit").setValidators([]),this.defaultPaddingEnable=!0):(this.geoFilterConfigForm.get("centerLatitude").setValidators([D.required,D.min(-90),D.max(90)]),this.geoFilterConfigForm.get("centerLongitude").setValidators([D.required,D.min(-180),D.max(180)]),this.geoFilterConfigForm.get("range").setValidators([D.required,D.min(0)]),this.geoFilterConfigForm.get("rangeUnit").setValidators([D.required]),this.defaultPaddingEnable=!1),t||n!==ct.POLYGON?this.geoFilterConfigForm.get("polygonsDefinition").setValidators([]):this.geoFilterConfigForm.get("polygonsDefinition").setValidators([D.required]),this.geoFilterConfigForm.get("perimeterKeyName").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("centerLatitude").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("centerLongitude").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("range").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("rangeUnit").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("polygonsDefinition").updateValueAndValidity({emitEvent:e})}}e("GpsGeoFilterConfigComponent",tr),tr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:tr,deps:[{token:G.Store},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),tr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:tr,selector:"tb-filter-node-gps-geofencing-config",usesInheritance:!0,ngImport:t,template:'
\n
\n
tb.rulenode.coordinate-field-names
\n
\n
\n \n {{ \'tb.rulenode.latitude-field-name\' | translate }}\n \n \n {{ \'tb.rulenode.latitude-field-name-required\' | translate }}\n \n \n \n {{ \'tb.rulenode.longitude-field-name\' | translate }}\n \n \n {{ \'tb.rulenode.longitude-field-name-required\' | translate }}\n \n \n
\n
tb.rulenode.coordinate-field-hint
\n
\n
\n
\n
tb.rulenode.geofence-configuration
\n
\n \n {{ \'tb.rulenode.perimeter-type\' | translate }}\n \n \n {{ perimeterTypeTranslationMap.get(type) | translate }}\n \n \n \n
\n \n {{ \'tb.rulenode.fetch-perimeter-info-from-metadata\' | translate }}\n \n
\n \n {{ \'tb.rulenode.perimeter-key-name\' | translate }}\n \n \n {{ \'tb.rulenode.perimeter-key-name-required\' | translate }}\n \n {{ \'tb.rulenode.perimeter-key-name-hint\' | translate }}\n \n
\n
\n \n {{ \'tb.rulenode.circle-center-latitude\' | translate }}\n \n \n {{ \'tb.rulenode.circle-center-latitude-required\' | translate }}\n \n \n \n {{ \'tb.rulenode.circle-center-longitude\' | translate }}\n \n \n {{ \'tb.rulenode.circle-center-longitude-required\' | translate }}\n \n \n
\n
\n \n {{ \'tb.rulenode.range\' | translate }}\n \n \n {{ \'tb.rulenode.range-required\' | translate }}\n \n \n \n {{ \'tb.rulenode.range-units\' | translate }}\n \n \n {{ rangeUnitTranslationMap.get(type) | translate }}\n \n \n \n {{ \'tb.rulenode.range-units-required\' | translate }}\n \n \n
\n
\n \n {{ \'tb.rulenode.polygon-definition\' | translate }}\n \n {{ \'tb.rulenode.polygon-definition-hint\' | translate }}\n \n {{ \'tb.rulenode.polygon-definition-required\' | translate }}\n \n \n
\n
\n
\n',styles:[":host .slide-toggle{margin-bottom:18px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n'],dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:J.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Y.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:tr,decorators:[{type:n,args:[{selector:"tb-filter-node-gps-geofencing-config",template:'
\n
\n
tb.rulenode.coordinate-field-names
\n
\n
\n \n {{ \'tb.rulenode.latitude-field-name\' | translate }}\n \n \n {{ \'tb.rulenode.latitude-field-name-required\' | translate }}\n \n \n \n {{ \'tb.rulenode.longitude-field-name\' | translate }}\n \n \n {{ \'tb.rulenode.longitude-field-name-required\' | translate }}\n \n \n
\n
tb.rulenode.coordinate-field-hint
\n
\n
\n
\n
tb.rulenode.geofence-configuration
\n
\n \n {{ \'tb.rulenode.perimeter-type\' | translate }}\n \n \n {{ perimeterTypeTranslationMap.get(type) | translate }}\n \n \n \n
\n \n {{ \'tb.rulenode.fetch-perimeter-info-from-metadata\' | translate }}\n \n
\n \n {{ \'tb.rulenode.perimeter-key-name\' | translate }}\n \n \n {{ \'tb.rulenode.perimeter-key-name-required\' | translate }}\n \n {{ \'tb.rulenode.perimeter-key-name-hint\' | translate }}\n \n
\n
\n \n {{ \'tb.rulenode.circle-center-latitude\' | translate }}\n \n \n {{ \'tb.rulenode.circle-center-latitude-required\' | translate }}\n \n \n \n {{ \'tb.rulenode.circle-center-longitude\' | translate }}\n \n \n {{ \'tb.rulenode.circle-center-longitude-required\' | translate }}\n \n \n
\n
\n \n {{ \'tb.rulenode.range\' | translate }}\n \n \n {{ \'tb.rulenode.range-required\' | translate }}\n \n \n \n {{ \'tb.rulenode.range-units\' | translate }}\n \n \n {{ rangeUnitTranslationMap.get(type) | translate }}\n \n \n \n {{ \'tb.rulenode.range-units-required\' | translate }}\n \n \n
\n
\n \n {{ \'tb.rulenode.polygon-definition\' | translate }}\n \n {{ \'tb.rulenode.polygon-definition-hint\' | translate }}\n \n {{ \'tb.rulenode.polygon-definition-required\' | translate }}\n \n \n
\n
\n
\n',styles:[":host .slide-toggle{margin-bottom:18px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n']}]}],ctorParameters:function(){return[{type:G.Store},{type:E.FormBuilder}]}});class nr extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.messageTypeConfigForm}prepareInputConfig(e){return{messageTypes:ee(e?.messageTypes)?e.messageTypes:null}}onConfigurationSet(e){this.messageTypeConfigForm=this.fb.group({messageTypes:[e.messageTypes,[D.required]]})}}e("MessageTypeConfigComponent",nr),nr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:nr,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),nr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:nr,selector:"tb-filter-node-message-type-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n
\n',dependencies:[{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:hn,selector:"tb-message-types-config",inputs:["required","label","placeholder","disabled"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:nr,decorators:[{type:n,args:[{selector:"tb-filter-node-message-type-config",template:'
\n \n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class rr extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.allowedEntityTypes=[x.DEVICE,x.ASSET,x.ENTITY_VIEW,x.TENANT,x.CUSTOMER,x.USER,x.DASHBOARD,x.RULE_CHAIN,x.RULE_NODE]}configForm(){return this.originatorTypeConfigForm}prepareInputConfig(e){return{originatorTypes:ee(e?.originatorTypes)?e.originatorTypes:null}}onConfigurationSet(e){this.originatorTypeConfigForm=this.fb.group({originatorTypes:[e.originatorTypes,[D.required]]})}}e("OriginatorTypeConfigComponent",rr),rr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:rr,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),rr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:rr,selector:"tb-filter-node-originator-type-config",usesInheritance:!0,ngImport:t,template:'
\n \n help\n \n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n'],dependencies:[{kind:"component",type:Xe.EntityTypeListComponent,selector:"tb-entity-type-list",inputs:["required","additionalClasses","appearance","label","floatLabel","disabled","subscriptSizing","allowedEntityTypes","emptyInputPlaceholder","filledInputPlaceholder","ignoreAuthorityFilter"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:z.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"directive",type:se.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:rr,decorators:[{type:n,args:[{selector:"tb-filter-node-originator-type-config",template:'
\n \n help\n \n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n']}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class or extends s{constructor(e,t,n,o){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=n,this.translate=o,this.tbelEnabled=X(this.store).tbelEnabled,this.scriptLanguage=d,this.changeScript=new r,this.hasScript=!0,this.testScriptLabel="tb.rulenode.test-filter-function"}configForm(){return this.scriptConfigForm}onConfigurationSet(e){this.scriptConfigForm=this.fb.group({scriptLang:[e.scriptLang,[D.required]],jsScript:[e.jsScript,[]],tbelScript:[e.tbelScript,[]]})}validatorTriggers(){return["scriptLang"]}updateValidators(e){let t=this.scriptConfigForm.get("scriptLang").value;t!==d.TBEL||this.tbelEnabled||(t=d.JS,this.scriptConfigForm.get("scriptLang").patchValue(t,{emitEvent:!1}),setTimeout((()=>{this.scriptConfigForm.updateValueAndValidity({emitEvent:!0})}))),this.scriptConfigForm.get("jsScript").setValidators(t===d.JS?[D.required]:[]),this.scriptConfigForm.get("jsScript").updateValueAndValidity({emitEvent:e}),this.scriptConfigForm.get("tbelScript").setValidators(t===d.TBEL?[D.required]:[]),this.scriptConfigForm.get("tbelScript").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=d.JS)),{scriptLang:ee(e?.scriptLang)?e.scriptLang:d.JS,jsScript:ee(e?.jsScript)?e.jsScript:null,tbelScript:ee(e?.tbelScript)?e.tbelScript:null}}testScript(e){const t=this.scriptConfigForm.get("scriptLang").value,n=t===d.JS?"jsScript":"tbelScript",r=t===d.JS?"rulenode/filter_node_script_fn":"rulenode/tbel/filter_node_script_fn",o=this.scriptConfigForm.get(n).value;this.nodeScriptTestService.testNodeScript(o,"filter",this.translate.instant("tb.rulenode.filter"),"Filter",["msg","metadata","msgType"],this.ruleNodeId,r,t,e).subscribe((e=>{e&&(this.scriptConfigForm.get(n).setValue(e),this.changeScript.emit())}))}onValidate(){this.scriptConfigForm.get("scriptLang").value===d.JS&&this.jsFuncComponent.validateOnSubmit()}}e("ScriptConfigComponent",or),or.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:or,deps:[{token:G.Store},{token:E.UntypedFormBuilder},{token:Z.NodeScriptTestService},{token:j.TranslateService}],target:t.ɵɵFactoryTarget.Component}),or.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:or,selector:"tb-filter-node-script-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0},{propertyName:"tbelFuncComponent",first:!0,predicate:["tbelFuncComponent"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n \n \n \n \n
\n',dependencies:[{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ae.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","scriptLanguage","noValidate","required"]},{kind:"component",type:ie.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ie.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:se.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:me.TbScriptLangComponent,selector:"tb-script-lang",inputs:["disabled"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:or,decorators:[{type:n,args:[{selector:"tb-filter-node-script-config",template:'
\n \n \n \n \n \n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder},{type:Z.NodeScriptTestService},{type:j.TranslateService}]},propDecorators:{jsFuncComponent:[{type:o,args:["jsFuncComponent",{static:!1}]}],tbelFuncComponent:[{type:o,args:["tbelFuncComponent",{static:!1}]}]}});class ar extends s{constructor(e,t,n,o){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=n,this.translate=o,this.tbelEnabled=X(this.store).tbelEnabled,this.scriptLanguage=d,this.changeScript=new r,this.hasScript=!0,this.testScriptLabel="tb.rulenode.test-switch-function"}configForm(){return this.switchConfigForm}onConfigurationSet(e){this.switchConfigForm=this.fb.group({scriptLang:[e.scriptLang,[D.required]],jsScript:[e.jsScript,[]],tbelScript:[e.tbelScript,[]]})}validatorTriggers(){return["scriptLang"]}updateValidators(e){let t=this.switchConfigForm.get("scriptLang").value;t!==d.TBEL||this.tbelEnabled||(t=d.JS,this.switchConfigForm.get("scriptLang").patchValue(t,{emitEvent:!1}),setTimeout((()=>{this.switchConfigForm.updateValueAndValidity({emitEvent:!0})}))),this.switchConfigForm.get("jsScript").setValidators(t===d.JS?[D.required]:[]),this.switchConfigForm.get("jsScript").updateValueAndValidity({emitEvent:e}),this.switchConfigForm.get("tbelScript").setValidators(t===d.TBEL?[D.required]:[]),this.switchConfigForm.get("tbelScript").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=d.JS)),{scriptLang:ee(e?.scriptLang)?e.scriptLang:d.JS,jsScript:ee(e?.jsScript)?e.jsScript:null,tbelScript:ee(e?.tbelScript)?e.tbelScript:null}}testScript(e){const t=this.switchConfigForm.get("scriptLang").value,n=t===d.JS?"jsScript":"tbelScript",r=t===d.JS?"rulenode/switch_node_script_fn":"rulenode/tbel/switch_node_script_fn",o=this.switchConfigForm.get(n).value;this.nodeScriptTestService.testNodeScript(o,"switch",this.translate.instant("tb.rulenode.switch"),"Switch",["msg","metadata","msgType"],this.ruleNodeId,r,t,e).subscribe((e=>{e&&(this.switchConfigForm.get(n).setValue(e),this.changeScript.emit())}))}onValidate(){this.switchConfigForm.get("scriptLang").value===d.JS&&this.jsFuncComponent.validateOnSubmit()}}e("SwitchConfigComponent",ar),ar.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:ar,deps:[{token:G.Store},{token:E.UntypedFormBuilder},{token:Z.NodeScriptTestService},{token:j.TranslateService}],target:t.ɵɵFactoryTarget.Component}),ar.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:ar,selector:"tb-filter-node-switch-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0},{propertyName:"tbelFuncComponent",first:!0,predicate:["tbelFuncComponent"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n \n \n \n \n
\n',dependencies:[{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ae.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","scriptLanguage","noValidate","required"]},{kind:"component",type:ie.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ie.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:se.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:me.TbScriptLangComponent,selector:"tb-script-lang",inputs:["disabled"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:ar,decorators:[{type:n,args:[{selector:"tb-filter-node-switch-config",template:'
\n \n \n \n \n \n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder},{type:Z.NodeScriptTestService},{type:j.TranslateService}]},propDecorators:{jsFuncComponent:[{type:o,args:["jsFuncComponent",{static:!1}]}],tbelFuncComponent:[{type:o,args:["tbelFuncComponent",{static:!1}]}]}});class ir{}e("RuleNodeCoreConfigFilterModule",ir),ir.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:ir,deps:[],target:t.ɵɵFactoryTarget.NgModule}),ir.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.9",ngImport:t,type:ir,declarations:[Zn,er,tr,nr,rr,or,ar,Xn],imports:[K,I,In],exports:[Zn,er,tr,nr,rr,or,ar,Xn]}),ir.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:ir,imports:[K,I,In]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:ir,decorators:[{type:l,args:[{declarations:[Zn,er,tr,nr,rr,or,ar,Xn],imports:[K,I,In],exports:[Zn,er,tr,nr,rr,or,ar,Xn]}]}]});class lr extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.originatorSource=mt,this.originatorSources=Object.keys(mt),this.originatorSourceTranslationMap=ut,this.allowedEntityTypes=[x.DEVICE,x.ASSET,x.ENTITY_VIEW,x.USER,x.EDGE]}configForm(){return this.changeOriginatorConfigForm}onConfigurationSet(e){this.changeOriginatorConfigForm=this.fb.group({originatorSource:[e?e.originatorSource:null,[D.required]],entityType:[e?e.entityType:null,[]],entityNamePattern:[e?e.entityNamePattern:null,[]],relationsQuery:[e?e.relationsQuery:null,[]]})}validatorTriggers(){return["originatorSource"]}updateValidators(e){const t=this.changeOriginatorConfigForm.get("originatorSource").value;t===mt.RELATED?this.changeOriginatorConfigForm.get("relationsQuery").setValidators([D.required]):this.changeOriginatorConfigForm.get("relationsQuery").setValidators([]),t===mt.ENTITY?(this.changeOriginatorConfigForm.get("entityType").setValidators([D.required]),this.changeOriginatorConfigForm.get("entityNamePattern").setValidators([D.required,D.pattern(/.*\S.*/)])):(this.changeOriginatorConfigForm.get("entityType").patchValue(null,{emitEvent:e}),this.changeOriginatorConfigForm.get("entityNamePattern").patchValue(null,{emitEvent:e}),this.changeOriginatorConfigForm.get("entityType").setValidators([]),this.changeOriginatorConfigForm.get("entityNamePattern").setValidators([])),this.changeOriginatorConfigForm.get("relationsQuery").updateValueAndValidity({emitEvent:e}),this.changeOriginatorConfigForm.get("entityType").updateValueAndValidity({emitEvent:e}),this.changeOriginatorConfigForm.get("entityNamePattern").updateValueAndValidity({emitEvent:e})}}e("ChangeOriginatorConfigComponent",lr),lr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:lr,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),lr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:lr,selector:"tb-transformation-node-change-originator-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.originator-source\n \n \n {{ originatorSourceTranslationMap.get(source) | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.entity-name-pattern\n \n \n {{ \'tb.rulenode.entity-name-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n
\n \n \n \n
\n
\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ge.EntityTypeSelectComponent,selector:"tb-entity-type-select",inputs:["allowedEntityTypes","useAliasEntityTypes","filterAllowedEntityTypes","showLabel","required","disabled"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:kn,selector:"tb-relations-query-config-old",inputs:["disabled","required"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:lr,decorators:[{type:n,args:[{selector:"tb-transformation-node-change-originator-config",template:'
\n \n tb.rulenode.originator-source\n \n \n {{ originatorSourceTranslationMap.get(source) | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.entity-name-pattern\n \n \n {{ \'tb.rulenode.entity-name-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n
\n \n \n \n
\n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class sr extends s{constructor(e,t,n,o){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=n,this.translate=o,this.tbelEnabled=X(this.store).tbelEnabled,this.scriptLanguage=d,this.changeScript=new r,this.hasScript=!0,this.testScriptLabel="tb.rulenode.test-transformer-function"}configForm(){return this.scriptConfigForm}onConfigurationSet(e){this.scriptConfigForm=this.fb.group({scriptLang:[e?e.scriptLang:d.JS,[D.required]],jsScript:[e?e.jsScript:null,[D.required]],tbelScript:[e?e.tbelScript:null,[]]})}validatorTriggers(){return["scriptLang"]}updateValidators(e){let t=this.scriptConfigForm.get("scriptLang").value;t!==d.TBEL||this.tbelEnabled||(t=d.JS,this.scriptConfigForm.get("scriptLang").patchValue(t,{emitEvent:!1}),setTimeout((()=>{this.scriptConfigForm.updateValueAndValidity({emitEvent:!0})}))),this.scriptConfigForm.get("jsScript").setValidators(t===d.JS?[D.required]:[]),this.scriptConfigForm.get("jsScript").updateValueAndValidity({emitEvent:e}),this.scriptConfigForm.get("tbelScript").setValidators(t===d.TBEL?[D.required]:[]),this.scriptConfigForm.get("tbelScript").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=d.JS)),e}testScript(e){const t=this.scriptConfigForm.get("scriptLang").value,n=t===d.JS?"jsScript":"tbelScript",r=t===d.JS?"rulenode/transformation_node_script_fn":"rulenode/tbel/transformation_node_script_fn",o=this.scriptConfigForm.get(n).value;this.nodeScriptTestService.testNodeScript(o,"update",this.translate.instant("tb.rulenode.transformer"),"Transform",["msg","metadata","msgType"],this.ruleNodeId,r,t,e).subscribe((e=>{e&&(this.scriptConfigForm.get(n).setValue(e),this.changeScript.emit())}))}onValidate(){this.scriptConfigForm.get("scriptLang").value===d.JS&&this.jsFuncComponent.validateOnSubmit()}}e("TransformScriptConfigComponent",sr),sr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:sr,deps:[{token:G.Store},{token:E.UntypedFormBuilder},{token:Z.NodeScriptTestService},{token:j.TranslateService}],target:t.ɵɵFactoryTarget.Component}),sr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:sr,selector:"tb-transformation-node-script-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0},{propertyName:"tbelFuncComponent",first:!0,predicate:["tbelFuncComponent"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n \n \n \n
\n \n
\n
\n',dependencies:[{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ae.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","scriptLanguage","noValidate","required"]},{kind:"component",type:ie.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ie.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:se.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:me.TbScriptLangComponent,selector:"tb-script-lang",inputs:["disabled"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:sr,decorators:[{type:n,args:[{selector:"tb-transformation-node-script-config",template:'
\n \n \n \n \n \n \n \n
\n \n
\n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder},{type:Z.NodeScriptTestService},{type:j.TranslateService}]},propDecorators:{jsFuncComponent:[{type:o,args:["jsFuncComponent",{static:!1}]}],tbelFuncComponent:[{type:o,args:["tbelFuncComponent",{static:!1}]}]}});class mr extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.mailBodyTypes=[{name:"tb.mail-body-type.plain-text",value:"false"},{name:"tb.mail-body-type.html",value:"true"},{name:"tb.mail-body-type.dynamic",value:"dynamic"}]}configForm(){return this.toEmailConfigForm}onConfigurationSet(e){this.toEmailConfigForm=this.fb.group({fromTemplate:[e?e.fromTemplate:null,[D.required]],toTemplate:[e?e.toTemplate:null,[D.required]],ccTemplate:[e?e.ccTemplate:null,[]],bccTemplate:[e?e.bccTemplate:null,[]],subjectTemplate:[e?e.subjectTemplate:null,[D.required]],mailBodyType:[e?e.mailBodyType:null],isHtmlTemplate:[e?e.isHtmlTemplate:null,[D.required]],bodyTemplate:[e?e.bodyTemplate:null,[D.required]]})}updateValidators(e){"dynamic"===this.toEmailConfigForm.get("mailBodyType").value?this.toEmailConfigForm.get("isHtmlTemplate").enable({emitEvent:!1}):this.toEmailConfigForm.get("isHtmlTemplate").disable({emitEvent:!1}),this.toEmailConfigForm.get("isHtmlTemplate").updateValueAndValidity({emitEvent:e})}validatorTriggers(){return["mailBodyType"]}}e("ToEmailConfigComponent",mr),mr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:mr,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),mr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:mr,selector:"tb-transformation-node-to-email-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.from-template\n \n \n {{ \'tb.rulenode.from-template-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.to-template\n \n \n {{ \'tb.rulenode.to-template-required\' | translate }}\n \n \n \n \n tb.rulenode.cc-template\n \n \n \n \n tb.rulenode.bcc-template\n \n \n \n \n tb.rulenode.subject-template\n \n \n {{ \'tb.rulenode.subject-template-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.mail-body-type\n \n \n {{ type.name | translate }}\n \n \n \n \n tb.rulenode.dynamic-mail-body-type\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.body-template\n \n \n {{ \'tb.rulenode.body-template-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:fe.SafePipe,name:"safe"},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:mr,decorators:[{type:n,args:[{selector:"tb-transformation-node-to-email-config",template:'
\n \n tb.rulenode.from-template\n \n \n {{ \'tb.rulenode.from-template-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.to-template\n \n \n {{ \'tb.rulenode.to-template-required\' | translate }}\n \n \n \n \n tb.rulenode.cc-template\n \n \n \n \n tb.rulenode.bcc-template\n \n \n \n \n tb.rulenode.subject-template\n \n \n {{ \'tb.rulenode.subject-template-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.mail-body-type\n \n \n {{ type.name | translate }}\n \n \n \n \n tb.rulenode.dynamic-mail-body-type\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.body-template\n \n \n {{ \'tb.rulenode.body-template-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class ur extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.separatorKeysCodes=[ue,pe,de]}onConfigurationSet(e){this.copyKeysConfigForm=this.fb.group({fromMetadata:[e?e.fromMetadata:null,[D.required]],keys:[e?e.keys:null,[D.required]]})}configForm(){return this.copyKeysConfigForm}removeKey(e){const t=this.copyKeysConfigForm.get("keys").value,n=t.indexOf(e);n>=0&&(t.splice(n,1),this.copyKeysConfigForm.get("keys").patchValue(t,{emitEvent:!0}))}addKey(e){const t=e.input;let n=e.value;if((n||"").trim()){n=n.trim();let e=this.copyKeysConfigForm.get("keys").value;e&&-1!==e.indexOf(n)||(e||(e=[]),e.push(n),this.copyKeysConfigForm.get("keys").patchValue(e,{emitEvent:!0}))}t&&(t.value="")}}e("CopyKeysConfigComponent",ur),ur.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:ur,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),ur.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:ur,selector:"tb-transformation-node-copy-keys-config",usesInheritance:!0,ngImport:t,template:'
\n
{{\'tb.rulenode.copy-from\' | translate}}
\n \n \n {{\'tb.rulenode.data-to-metadata\' | translate}}\n \n \n {{\'tb.rulenode.metadata-to-data\' | translate}}\n \n \n \n \n \n {{key}}\n close\n \n \n \n {{ \'tb.rulenode.keys-required\' | translate }}\n tb.rulenode.use-regular-expression-hint\n \n
\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Je.MatRadioGroup,selector:"mat-radio-group",exportAs:["matRadioGroup"]},{kind:"component",type:Je.MatRadioButton,selector:"mat-radio-button",inputs:["disableRipple","tabIndex"],exportAs:["matRadioButton"]},{kind:"component",type:ce.MatChipGrid,selector:"mat-chip-grid",inputs:["tabIndex","disabled","placeholder","required","value","errorStateMatcher"],outputs:["change","valueChange"]},{kind:"directive",type:ce.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputFor","matChipInputAddOnBlur","matChipInputSeparatorKeyCodes","placeholder","id","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]},{kind:"directive",type:ce.MatChipRemove,selector:"[matChipRemove]"},{kind:"component",type:ce.MatChipRow,selector:"mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]",inputs:["color","disabled","disableRipple","tabIndex","editable"],outputs:["edited"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:ur,decorators:[{type:n,args:[{selector:"tb-transformation-node-copy-keys-config",template:'
\n
{{\'tb.rulenode.copy-from\' | translate}}
\n \n \n {{\'tb.rulenode.data-to-metadata\' | translate}}\n \n \n {{\'tb.rulenode.metadata-to-data\' | translate}}\n \n \n \n \n \n {{key}}\n close\n \n \n \n {{ \'tb.rulenode.keys-required\' | translate }}\n tb.rulenode.use-regular-expression-hint\n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class pr extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.renameKeysConfigForm}onConfigurationSet(e){this.renameKeysConfigForm=this.fb.group({fromMetadata:[e?e.fromMetadata:null,[D.required]],renameKeysMapping:[e?e.renameKeysMapping:null,[D.required]]})}}e("RenameKeysConfigComponent",pr),pr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:pr,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),pr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:pr,selector:"tb-transformation-node-rename-keys-config",usesInheritance:!0,ngImport:t,template:'
\n
{{ \'tb.rulenode.rename-keys-in\' | translate }}
\n \n \n {{\'tb.rulenode.data\' | translate}}\n \n \n {{\'tb.rulenode.metadata\' | translate}}\n \n \n \n \n
\n',dependencies:[{kind:"directive",type:Je.MatRadioGroup,selector:"mat-radio-group",exportAs:["matRadioGroup"]},{kind:"component",type:Je.MatRadioButton,selector:"mat-radio-button",inputs:["disableRipple","tabIndex"],exportAs:["matRadioButton"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:on,selector:"tb-kv-map-config-old",inputs:["disabled","uniqueKeyValuePairValidator","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:pr,decorators:[{type:n,args:[{selector:"tb-transformation-node-rename-keys-config",template:'
\n
{{ \'tb.rulenode.rename-keys-in\' | translate }}
\n \n \n {{\'tb.rulenode.data\' | translate}}\n \n \n {{\'tb.rulenode.metadata\' | translate}}\n \n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class dr extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.jsonPathConfigForm}onConfigurationSet(e){this.jsonPathConfigForm=this.fb.group({jsonPath:[e?e.jsonPath:null,[D.required]]})}}e("NodeJsonPathConfigComponent",dr),dr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:dr,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),dr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:dr,selector:"tb-transformation-node-json-path-config",usesInheritance:!0,ngImport:t,template:"
\n \n {{ 'tb.rulenode.json-path-expression' | translate }}\n \n {{ 'tb.rulenode.json-path-expression-hint' | translate }}\n {{ 'tb.rulenode.json-path-expression-required' | translate }}\n \n
\n\n",dependencies:[{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:dr,decorators:[{type:n,args:[{selector:"tb-transformation-node-json-path-config",template:"
\n \n {{ 'tb.rulenode.json-path-expression' | translate }}\n \n {{ 'tb.rulenode.json-path-expression-hint' | translate }}\n {{ 'tb.rulenode.json-path-expression-required' | translate }}\n \n
\n\n"}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class cr extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.separatorKeysCodes=[ue,pe,de]}onConfigurationSet(e){this.deleteKeysConfigForm=this.fb.group({fromMetadata:[e?e.fromMetadata:null,[D.required]],keys:[e?e.keys:null,[D.required]]})}configForm(){return this.deleteKeysConfigForm}removeKey(e){const t=this.deleteKeysConfigForm.get("keys").value,n=t.indexOf(e);n>=0&&(t.splice(n,1),this.deleteKeysConfigForm.get("keys").patchValue(t,{emitEvent:!0}))}addKey(e){const t=e.input;let n=e.value;if((n||"").trim()){n=n.trim();let e=this.deleteKeysConfigForm.get("keys").value;e&&-1!==e.indexOf(n)||(e||(e=[]),e.push(n),this.deleteKeysConfigForm.get("keys").patchValue(e,{emitEvent:!0}))}t&&(t.value="")}}e("DeleteKeysConfigComponent",cr),cr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:cr,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),cr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:cr,selector:"tb-transformation-node-delete-keys-config",usesInheritance:!0,ngImport:t,template:'
\n
{{\'tb.rulenode.delete-from\' | translate}}
\n \n \n {{\'tb.rulenode.data\' | translate}}\n \n \n {{\'tb.rulenode.metadata\' | translate}}\n \n \n \n \n \n {{key}}\n close\n \n \n \n {{ \'tb.rulenode.keys-required\' | translate }}\n tb.rulenode.use-regular-expression-delete-hint\n \n
\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Je.MatRadioGroup,selector:"mat-radio-group",exportAs:["matRadioGroup"]},{kind:"component",type:Je.MatRadioButton,selector:"mat-radio-button",inputs:["disableRipple","tabIndex"],exportAs:["matRadioButton"]},{kind:"component",type:ce.MatChipGrid,selector:"mat-chip-grid",inputs:["tabIndex","disabled","placeholder","required","value","errorStateMatcher"],outputs:["change","valueChange"]},{kind:"directive",type:ce.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputFor","matChipInputAddOnBlur","matChipInputSeparatorKeyCodes","placeholder","id","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]},{kind:"directive",type:ce.MatChipRemove,selector:"[matChipRemove]"},{kind:"component",type:ce.MatChipRow,selector:"mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]",inputs:["color","disabled","disableRipple","tabIndex","editable"],outputs:["edited"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:cr,decorators:[{type:n,args:[{selector:"tb-transformation-node-delete-keys-config",template:'
\n
{{\'tb.rulenode.delete-from\' | translate}}
\n \n \n {{\'tb.rulenode.data\' | translate}}\n \n \n {{\'tb.rulenode.metadata\' | translate}}\n \n \n \n \n \n {{key}}\n close\n \n \n \n {{ \'tb.rulenode.keys-required\' | translate }}\n tb.rulenode.use-regular-expression-delete-hint\n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class fr{}e("RulenodeCoreConfigTransformModule",fr),fr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:fr,deps:[],target:t.ɵɵFactoryTarget.NgModule}),fr.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.9",ngImport:t,type:fr,declarations:[lr,sr,mr,ur,pr,dr,cr],imports:[K,I,In],exports:[lr,sr,mr,ur,pr,dr,cr]}),fr.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:fr,imports:[K,I,In]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:fr,decorators:[{type:l,args:[{declarations:[lr,sr,mr,ur,pr,dr,cr],imports:[K,I,In],exports:[lr,sr,mr,ur,pr,dr,cr]}]}]});class gr extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.entityType=x}configForm(){return this.ruleChainInputConfigForm}onConfigurationSet(e){this.ruleChainInputConfigForm=this.fb.group({ruleChainId:[e?e.ruleChainId:null,[D.required]]})}}e("RuleChainInputComponent",gr),gr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:gr,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),gr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:gr,selector:"tb-flow-node-rule-chain-input-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n
\n',dependencies:[{kind:"component",type:We.EntityAutocompleteComponent,selector:"tb-entity-autocomplete",inputs:["entityType","entitySubtype","excludeEntityIds","labelText","requiredText","useFullEntityId","appearance","required","disabled"],outputs:["entityChanged"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:gr,decorators:[{type:n,args:[{selector:"tb-flow-node-rule-chain-input-config",template:'
\n \n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class yr extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.ruleChainOutputConfigForm}onConfigurationSet(e){this.ruleChainOutputConfigForm=this.fb.group({})}}e("RuleChainOutputComponent",yr),yr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:yr,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),yr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:yr,selector:"tb-flow-node-rule-chain-output-config",usesInheritance:!0,ngImport:t,template:'
\n
\n
\n',dependencies:[{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:yr,decorators:[{type:n,args:[{selector:"tb-flow-node-rule-chain-output-config",template:'
\n
\n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class xr{}e("RuleNodeCoreConfigFlowModule",xr),xr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:xr,deps:[],target:t.ɵɵFactoryTarget.NgModule}),xr.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.9",ngImport:t,type:xr,declarations:[gr,yr],imports:[K,I,In],exports:[gr,yr]}),xr.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:xr,imports:[K,I,In]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:xr,decorators:[{type:l,args:[{declarations:[gr,yr],imports:[K,I,In],exports:[gr,yr]}]}]});class br{constructor(e){!function(e){e.setTranslation("en_US",{tb:{rulenode:{id:"Id","additional-info":"Additional Info","create-entity-if-not-exists":"Create new entity if not exists","create-entity-if-not-exists-hint":"Create a new entity set above if it does not exist.","entity-name-pattern":"Name pattern","entity-name-pattern-required":"Name pattern is required","entity-type-pattern":"Type pattern","entity-type-pattern-required":"Type pattern is required","output-message-type":"Output message type","output-message-type-required":"Output message type is required","output-message-type-max-length":"Output message type should be less than 256","entity-cache-expiration":"Entities cache expiration time (sec)","entity-cache-expiration-hint":"Specifies maximum time interval allowed to store found entity records. 0 value means that records will never expire.","entity-cache-expiration-required":"Entities cache expiration time is required.","entity-cache-expiration-range":"Entities cache expiration time should be greater than or equal to 0.","customer-name-pattern":"Customer name pattern","customer-name-pattern-required":"Customer name pattern is required","create-customer-if-not-exists":"Create new customer if not exists","customer-cache-expiration":"Customers cache expiration time (sec)","customer-cache-expiration-hint":"Specifies maximum time interval allowed to store found customer records. 0 value means that records will never expire.","customer-cache-expiration-required":"Customers cache expiration time is required.","customer-cache-expiration-range":"Customers cache expiration time should be greater than or equal to 0.","interval-start":"Interval start","interval-end":"Interval end","time-unit":"Time unit","fetch-mode":"Fetch mode","order-by-timestamp":"Order by timestamp",limit:"Limit","limit-hint":"Min limit value is 2, max - 1000. If you want to fetch a single entry, select fetch mode 'First' or 'Last'.","limit-required":"Limit is required.","limit-range":"Limit should be in a range from 2 to 1000.","time-unit-milliseconds":"Milliseconds","time-unit-seconds":"Seconds","time-unit-minutes":"Minutes","time-unit-hours":"Hours","time-unit-days":"Days","time-value-range":"Allowing range from 1 to 2147483647.","start-interval-value-required":"Interval start is required.","end-interval-value-required":"Interval end is required.",filter:"Filter",switch:"Switch","math-templatization-tooltip":"This field support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","add-message-type":"Add message type","select-message-types-required":"At least one message type should be selected.","select-message-types":"Select message types","no-message-types-found":"No message types found","no-message-type-matching":"'{{messageType}}' not found.","create-new-message-type":"Create a new one.","message-types-required":"Message types are required.","client-attributes":"Client attributes","shared-attributes":"Shared attributes","server-attributes":"Server attributes","attributes-keys":"Attributes keys","attributes-keys-required":"Attributes keys are required","notify-device":"Notify device","send-attributes-updated-notification":"Send attributes updated notification","send-attributes-updated-notification-hint":"Send notification about updated attributes as a separate message to the rule engine queue.","send-attributes-deleted-notification":"Send attributes deleted notification","send-attributes-deleted-notification-hint":"Send notification about deleted attributes as a separate message to the rule engine queue.","update-attributes-only-on-value-change":"Save attributes only if the value changes","update-attributes-only-on-value-change-hint":"Updates the attributes on every incoming message disregarding if their value has changed. Increases API usage and reduces performance.","update-attributes-only-on-value-change-hint-enabled":"Updates the attributes only if their value has changed. If the value is not changed, no update to the attribute timestamp nor attribute change notification will be sent.","fetch-credentials-to-metadata":"Fetch credentials to metadata","notify-device-hint":"If the message arrives from the device, we will push it back to the device by default.","notify-device-delete-hint":"Send notification about deleted attributes to device.","latest-timeseries":"Latest time-series data keys","timeseries-keys":"Timeseries keys","timeseries-keys-required":"At least one timeseries key should be selected.","add-timeseries-key":"Add timeseries key","add-message-field":"Add message field","relation-search-parameters":"Relation search parameters","add-metadata-field":"Add metadata field","data-keys":"Message field names","copy-from":"Copy from","data-to-metadata":"Data to metadata","metadata-to-data":"Metadata to data","use-regular-expression-hint":"Use regular expression to copy keys by pattern",interval:"Interval","interval-required":"Interval is required","interval-hint":"Deduplication interval in seconds.","interval-min-error":"Min allowed value is 1","max-pending-msgs":"Max pending messages","max-pending-msgs-hint":"Maximum number of messages that are stored in memory for each unique deduplication id.","max-pending-msgs-required":"Max pending messages is required","max-pending-msgs-max-error":"Max allowed value is 1000","max-pending-msgs-min-error":"Min allowed value is 1","max-retries":"Max retries","max-retries-required":"Max retries is required","max-retries-hint":"Maximum number of retries to push the deduplicated messages into the queue. 10 seconds delay is used between retries","max-retries-max-error":"Max allowed value is 100","max-retries-min-error":"Min allowed value is 0",strategy:"Strategy","strategy-required":"Strategy is required","strategy-all-hint":"Return all messages that arrived during deduplication period as a single JSON array message. Where each element represents object with msg and metadata inner properties.","strategy-first-hint":"Return first message that arrived during deduplication period.","strategy-last-hint":"Return last message that arrived during deduplication period.",first:"First",last:"Last",all:"All","output-msg-type-hint":"The message type of the deduplication result.","queue-name-hint":"The queue name where the deduplication result will be published.",keys:"Keys","keys-required":"Keys are required","rename-keys-in":"Rename keys in",data:"Data",message:"Message",metadata:"Metadata","key-name":"Key name","key-name-required":"Key name is required","new-key-name":"New key name","new-key-name-required":"New key name is required","metadata-keys":"Metadata field names","json-path-expression":"JSON path expression","json-path-expression-required":"JSON path expression is required","json-path-expression-hint":"JSONPath specifies a path to an element or a set of elements in a JSON structure. '$' represents the root object or array.","relations-query":"Relations query","device-relations-query":"Device relations query","max-relation-level":"Max relation level","max-relation-level-error":"Value should be greater than 0 or unspecified.","relation-type":"Relation type","relation-type-pattern":"Relation type pattern","relation-type-pattern-required":"Relation type pattern is required","relation-types-list":"Relation types to propagate","relation-types-list-hint":"If Propagate relation types are not selected, alarms will be propagated without filtering by relation type.","unlimited-level":"Unlimited level","latest-telemetry":"Latest telemetry","add-telemetry-key":"Add telemetry key","delete-from":"Delete from","use-regular-expression-delete-hint":"Use regular expression to delete keys by pattern","fetch-into":"Fetch into","attr-mapping":"Attributes mapping:","source-attribute":"Source attribute key","source-attribute-required":"Source attribute key is required.","source-telemetry":"Source telemetry key","source-telemetry-required":"Source telemetry key is required.","target-key":"Target key","target-key-required":"Target key is required.","attr-mapping-required":"At least one mapping entry should be specified.","fields-mapping":"Fields mapping","relations-query-config-direction-suffix":"originator","add-device-profile":"Add device profile","profile-name":"Profile name","fetch-circle-parameter-info-from-metadata-hint":'Metadata field \'{{perimeterKeyName}}\' should be defined in next format: {"latitude":48.196, "longitude":24.6532, "radius":100.0, "radiusUnit":"METER"}',"fetch-poligon-parameter-info-from-metadata-hint":"Metadata field '{{perimeterKeyName}}' should be defined in next format: [[48.19736,24.65235],[48.19800,24.65060],...,[48.19849,24.65420]]","short-templatization-tooltip":"Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","fields-mapping-required":"At least one field mapping should be specified.","at-least-one-field-required":"At least one input field must have a value(s) provided.","originator-fields-sv-map-hint":"Target key fields support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","sv-map-hint":"Only target key fields support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","source-field":"Source field","source-field-required":"Source field is required.","originator-source":"Originator source","originator-customer":"Customer","originator-tenant":"Tenant","originator-related":"Related","originator-alarm-originator":"Alarm Originator","originator-entity":"Entity","clone-message":"Clone message",transform:"Transform","default-ttl":"Default TTL in seconds","default-ttl-required":"Default TTL is required.","min-default-ttl-message":"Only 0 minimum TTL is allowed.","message-count":"Message count (0 - unlimited)","message-count-required":"Message count is required.","min-message-count-message":"Only 0 minimum message count is allowed.","period-seconds":"Period in seconds","period-seconds-required":"Period is required.","use-metadata-period-in-seconds-patterns":"Use period in seconds pattern","use-metadata-period-in-seconds-patterns-hint":"If selected, rule node use period in seconds interval pattern from message metadata or data assuming that intervals are in the seconds.","period-in-seconds-pattern":"Period in seconds pattern","period-in-seconds-pattern-required":"Period in seconds pattern is required","min-period-seconds-message":"Only 1 second minimum period is allowed.",originator:"Originator","message-body":"Message body","message-metadata":"Message metadata",generate:"Generate","test-generator-function":"Test generator function",generator:"Generator","test-filter-function":"Test filter function","test-switch-function":"Test switch function","test-transformer-function":"Test transformer function",transformer:"Transformer","alarm-create-condition":"Alarm create condition","test-condition-function":"Test condition function","alarm-clear-condition":"Alarm clear condition","alarm-details-builder":"Alarm details builder","test-details-function":"Test details function","alarm-type":"Alarm type","select-entity-types":"Select entity types","alarm-type-required":"Alarm type is required.","alarm-severity":"Alarm severity","alarm-severity-required":"Alarm severity is required","alarm-severity-pattern":"Alarm severity pattern","alarm-status-filter":"Alarm status filter","alarm-status-list-empty":"Alarm status list is empty","no-alarm-status-matching":"No alarm status matching were found.",propagate:"Propagate alarm to related entities","propagate-to-owner":"Propagate alarm to entity owner (Customer or Tenant)","propagate-to-tenant":"Propagate alarm to Tenant",condition:"Condition",details:"Details","to-string":"To string","test-to-string-function":"Test to string function","from-template":"From Template","from-template-required":"From Template is required","to-template":"To Template","to-template-required":"To Template is required","mail-address-list-template-hint":'Comma separated address list, use ${metadataKey} for value from metadata, $[messageKey] for value from message body',"cc-template":"Cc Template","bcc-template":"Bcc Template","subject-template":"Subject Template","subject-template-required":"Subject Template is required","body-template":"Body Template","body-template-required":"Body Template is required","dynamic-mail-body-type":"Dynamic mail body type","mail-body-type":"Mail body type","request-id-metadata-attribute":"Request Id Metadata attribute name","timeout-sec":"Timeout in seconds","timeout-required":"Timeout is required","min-timeout-message":"Only 0 minimum timeout value is allowed.","endpoint-url-pattern":"Endpoint URL pattern","endpoint-url-pattern-required":"Endpoint URL pattern is required","request-method":"Request method","use-simple-client-http-factory":"Use simple client HTTP factory","ignore-request-body":"Without request body","parse-to-plain-text":"Parse to plain text","parse-to-plain-text-hint":'If selected, request body message payload will be transformed from JSON string to plain text, e.g. msg = "Hello,\\t\\"world\\"" will be parsed to Hello, "world"',"read-timeout":"Read timeout in millis","read-timeout-hint":"The value of 0 means an infinite timeout","max-parallel-requests-count":"Max number of parallel requests","max-parallel-requests-count-hint":"The value of 0 specifies no limit in parallel processing",headers:"Headers","headers-hint":'Use ${metadataKey} for value from metadata, $[messageKey] for value from message body in header/value fields',header:"Header","header-required":"Header is required",value:"Value","value-required":"Value is required","topic-pattern":"Topic pattern","key-pattern":"Key pattern","key-pattern-hint":"Optional. If a valid partition number is specified, it will be used when sending the record. If no partition is specified, the key will be used instead. If neither is specified, a partition will be assigned in a round-robin fashion.","topic-pattern-required":"Topic pattern is required",topic:"Topic","topic-required":"Topic is required","bootstrap-servers":"Bootstrap servers","bootstrap-servers-required":"Bootstrap servers value is required","other-properties":"Other properties",key:"Key","key-required":"Key is required",retries:"Automatically retry times if fails","min-retries-message":"Only 0 minimum retries is allowed.","batch-size-bytes":"Produces batch size in bytes","min-batch-size-bytes-message":"Only 0 minimum batch size is allowed.","linger-ms":"Time to buffer locally (ms)","min-linger-ms-message":"Only 0 ms minimum value is allowed.","buffer-memory-bytes":"Client buffer max size in bytes","min-buffer-memory-message":"Only 0 minimum buffer size is allowed.",acks:"Number of acknowledgments","key-serializer":"Key serializer","key-serializer-required":"Key serializer is required","value-serializer":"Value serializer","value-serializer-required":"Value serializer is required","topic-arn-pattern":"Topic ARN pattern","topic-arn-pattern-required":"Topic ARN pattern is required","aws-access-key-id":"AWS Access Key ID","aws-access-key-id-required":"AWS Access Key ID is required","aws-secret-access-key":"AWS Secret Access Key","aws-secret-access-key-required":"AWS Secret Access Key is required","aws-region":"AWS Region","aws-region-required":"AWS Region is required","exchange-name-pattern":"Exchange name pattern","routing-key-pattern":"Routing key pattern","message-properties":"Message properties",host:"Host","host-required":"Host is required",port:"Port","port-required":"Port is required","port-range":"Port should be in a range from 1 to 65535.","virtual-host":"Virtual host",username:"Username",password:"Password","automatic-recovery":"Automatic recovery","connection-timeout-ms":"Connection timeout (ms)","min-connection-timeout-ms-message":"Only 0 ms minimum value is allowed.","handshake-timeout-ms":"Handshake timeout (ms)","min-handshake-timeout-ms-message":"Only 0 ms minimum value is allowed.","client-properties":"Client properties","queue-url-pattern":"Queue URL pattern","queue-url-pattern-required":"Queue URL pattern is required","delay-seconds":"Delay (seconds)","min-delay-seconds-message":"Only 0 seconds minimum value is allowed.","max-delay-seconds-message":"Only 900 seconds maximum value is allowed.",name:"Name","name-required":"Name is required","queue-type":"Queue type","sqs-queue-standard":"Standard","sqs-queue-fifo":"FIFO","gcp-project-id":"GCP project ID","gcp-project-id-required":"GCP project ID is required","gcp-service-account-key":"GCP service account key file","gcp-service-account-key-required":"GCP service account key file is required","pubsub-topic-name":"Topic name","pubsub-topic-name-required":"Topic name is required","message-attributes":"Message attributes","message-attributes-hint":'Use ${metadataKey} for value from metadata, $[messageKey] for value from message body in name/value fields',"connect-timeout":"Connection timeout (sec)","connect-timeout-required":"Connection timeout is required.","connect-timeout-range":"Connection timeout should be in a range from 1 to 200.","client-id":"Client ID","client-id-hint":'Optional. Leave empty for auto-generated Client ID. Be careful when specifying the Client ID. Majority of the MQTT brokers will not allow multiple connections with the same Client ID. To connect to such brokers, your mqtt Client ID must be unique. When platform is running in a micro-services mode, the copy of rule node is launched in each micro-service. This will automatically lead to multiple mqtt clients with the same ID and may cause failures of the rule node. To avoid such failures enable "Add Service ID as suffix to Client ID" option below.',"append-client-id-suffix":"Add Service ID as suffix to Client ID","client-id-suffix-hint":'Optional. Applied when "Client ID" specified explicitly. If selected then Service ID will be added to Client ID as a suffix. Helps to avoid failures when platform is running in a micro-services mode.',"device-id":"Device ID","device-id-required":"Device ID is required.","clean-session":"Clean session","enable-ssl":"Enable SSL",credentials:"Credentials","credentials-type":"Credentials type","credentials-type-required":"Credentials type is required.","credentials-anonymous":"Anonymous","credentials-basic":"Basic","credentials-pem":"PEM","credentials-pem-hint":"At least Server CA certificate file or a pair of Client certificate and Client private key files are required","credentials-sas":"Shared Access Signature","sas-key":"SAS Key","sas-key-required":"SAS Key is required.",hostname:"Hostname","hostname-required":"Hostname is required.","azure-ca-cert":"CA certificate file","username-required":"Username is required.","password-required":"Password is required.","ca-cert":"Server CA certificate file","private-key":"Client private key file",cert:"Client certificate file","no-file":"No file selected.","drop-file":"Drop a file or click to select a file to upload.","private-key-password":"Private key password","use-system-smtp-settings":"Use system SMTP settings","use-metadata-dynamic-interval":"Use dynamic interval","metadata-dynamic-interval-hint":"Interval start and end input fields support templatization. Note that the substituted template value should be set in milliseconds. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","use-metadata-interval-patterns-hint":"If selected, rule node use start and end interval patterns from message metadata or data assuming that intervals are in the milliseconds.","use-message-alarm-data":"Use message alarm data","overwrite-alarm-details":"Overwrite alarm details","use-alarm-severity-pattern":"Use alarm severity pattern","check-all-keys":"Check that all specified fields are present","check-all-keys-hint":"If selected, checks that all specified keys are present in the message data and metadata.","check-relation-to-specific-entity":"Check relation to specific entity","check-relation-to-specific-entity-tooltip":"If enabled, checks the presence of relation with a specific entity otherwise, checks the presence of relation with any entity. In both cases, relation lookup is based on configured direction and type.","check-relation-hint":"Checks existence of relation to specific entity or to any entity based on direction and relation type.","delete-relation-to-specific-entity":"Delete relation to specific entity","delete-relation-hint":"Deletes relation from the originator of the incoming message to the specified entity or list of entities based on direction and type.","remove-current-relations":"Remove current relations","remove-current-relations-hint":"Removes current relations from the originator of the incoming message based on direction and type.","change-originator-to-related-entity":"Change originator to related entity","change-originator-to-related-entity-hint":"Used to process submitted message as a message from another entity.","start-interval":"Interval start","end-interval":"Interval end","start-interval-required":"Interval start is required.","end-interval-required":"Interval end is required.","smtp-protocol":"Protocol","smtp-host":"SMTP host","smtp-host-required":"SMTP host is required.","smtp-port":"SMTP port","smtp-port-required":"You must supply a smtp port.","smtp-port-range":"SMTP port should be in a range from 1 to 65535.","timeout-msec":"Timeout ms","min-timeout-msec-message":"Only 0 ms minimum value is allowed.","enter-username":"Enter username","enter-password":"Enter password","enable-tls":"Enable TLS","tls-version":"TLS version","enable-proxy":"Enable proxy","use-system-proxy-properties":"Use system proxy properties","proxy-host":"Proxy host","proxy-host-required":"Proxy host is required.","proxy-port":"Proxy port","proxy-port-required":"Proxy port is required.","proxy-port-range":"Proxy port should be in a range from 1 to 65535.","proxy-user":"Proxy user","proxy-password":"Proxy password","proxy-scheme":"Proxy scheme","numbers-to-template":"Phone Numbers To Template","numbers-to-template-required":"Phone Numbers To Template is required","numbers-to-template-hint":'Comma separated Phone Numbers, use ${metadataKey} for value from metadata, $[messageKey] for value from message body',"sms-message-template":"SMS message Template","sms-message-template-required":"SMS message Template is required","use-system-sms-settings":"Use system SMS provider settings","min-period-0-seconds-message":"Only 0 second minimum period is allowed.","max-pending-messages":"Maximum pending messages","max-pending-messages-required":"Maximum pending messages is required.","max-pending-messages-range":"Maximum pending messages should be in a range from 1 to 100000.","originator-types-filter":"Originator types filter","interval-seconds":"Interval in seconds","interval-seconds-required":"Interval is required.","min-interval-seconds-message":"Only 1 second minimum interval is allowed.","output-timeseries-key-prefix":"Output timeseries key prefix","output-timeseries-key-prefix-required":"Output timeseries key prefix required.","separator-hint":'Press "Enter" to complete field input.',"select-details":"Select details","entity-details-id":"Id","entity-details-title":"Title","entity-details-country":"Country","entity-details-state":"State","entity-details-city":"City","entity-details-zip":"Zip","entity-details-address":"Address","entity-details-address2":"Address2","entity-details-additional_info":"Additional Info","entity-details-phone":"Phone","entity-details-email":"Email","fields-to-check":"Fields to check","add-detail":"Add detail","check-all-keys-tooltip":"If enabled, checks the presence of all fields listed in the message and metadata field names within the incoming message and its metadata.","fields-to-check-hint":'Press "Enter" to complete field name input. Multiple field names supported.',"entity-details-list-empty":"At least one detail should be selected.","alarm-status":"Alarm status","alarm-required":"At least one alarm status should be selected.","no-entity-details-matching":"No entity details matching were found.","custom-table-name":"Custom table name","custom-table-name-required":"Table Name is required","custom-table-hint":"Enter the table name without prefix 'cs_tb_'.","message-field":"Message field","message-field-required":"Message field is required.","table-col":"Table column","table-col-required":"Table column is required.","latitude-field-name":"Latitude field name","longitude-field-name":"Longitude field name","latitude-field-name-required":"Latitude field name is required.","longitude-field-name-required":"Longitude field name is required.","fetch-perimeter-info-from-metadata":"Fetch perimeter information from metadata","fetch-perimeter-info-from-metadata-tooltip":"If perimeter type is set to 'Polygon' the value of metadata field '{{perimeterKeyName}}' will be set as perimeter definition without additional parsing of the value. Otherwise, if perimeter type is set to 'Circle' the value of '{{perimeterKeyName}}' metadata field will be parsed to extract 'latitude', 'longitude', 'radius', 'radiusUnit' fields that uses for circle perimeter definition.","perimeter-key-name":"Perimeter key name","perimeter-key-name-hint":"Metadata field name that includes perimeter information.","perimeter-key-name-required":"Perimeter key name is required.","perimeter-circle":"Circle","perimeter-polygon":"Polygon","perimeter-type":"Perimeter type","circle-center-latitude":"Center latitude","circle-center-latitude-required":"Center latitude is required.","circle-center-longitude":"Center longitude","circle-center-longitude-required":"Center longitude is required.","range-unit-meter":"Meter","range-unit-kilometer":"Kilometer","range-unit-foot":"Foot","range-unit-mile":"Mile","range-unit-nautical-mile":"Nautical mile","range-units":"Range units","range-units-required":"Range units is required.",range:"Range","range-required":"Range is required.","polygon-definition":"Polygon definition","polygon-definition-required":"Polygon definition is required.","polygon-definition-hint":"Use the following format for manual definition of polygon: [[lat1,lon1],[lat2,lon2], ... ,[latN,lonN]].","min-inside-duration":"Minimal inside duration","min-inside-duration-value-required":"Minimal inside duration is required","min-inside-duration-time-unit":"Minimal inside duration time unit","min-outside-duration":"Minimal outside duration","min-outside-duration-value-required":"Minimal outside duration is required","min-outside-duration-time-unit":"Minimal outside duration time unit","tell-failure-if-absent":"Tell Failure","tell-failure-if-absent-hint":'If at least one selected key doesn\'t exist the outbound message will report "Failure".',"get-latest-value-with-ts":"Fetch timestamp for the latest telemetry values","get-latest-value-with-ts-hint":'If selected, the latest telemetry values will also include timestamp, e.g: "temp": "{"ts":1574329385897, "value":42}"',"use-redis-queue":"Use redis queue for message persistence","ignore-null-strings":"Ignore null strings","ignore-null-strings-hint":"If selected rule node will ignore entity fields with empty value.","trim-redis-queue":"Trim redis queue","redis-queue-max-size":"Redis queue max size","add-metadata-key-values-as-kafka-headers":"Add Message metadata key-value pairs to Kafka record headers","add-metadata-key-values-as-kafka-headers-hint":"If selected, key-value pairs from message metadata will be added to the outgoing records headers as byte arrays with predefined charset encoding.","charset-encoding":"Charset encoding","charset-encoding-required":"Charset encoding is required.","charset-us-ascii":"US-ASCII","charset-iso-8859-1":"ISO-8859-1","charset-utf-8":"UTF-8","charset-utf-16be":"UTF-16BE","charset-utf-16le":"UTF-16LE","charset-utf-16":"UTF-16","select-queue-hint":"The queue name can be selected from a drop-down list or add a custom name.","persist-alarm-rules":"Persist state of alarm rules","fetch-alarm-rules":"Fetch state of alarm rules","input-value-key":"Input value key","input-value-key-required":"Input value key is required.","output-value-key":"Output value key","output-value-key-required":"Output value key is required.","number-of-digits-after-floating-point":"Number of digits after floating point","number-of-digits-after-floating-point-range":"Number of digits after floating point should be in a range from 0 to 15.","failure-if-delta-negative":"Tell Failure if delta is negative","failure-if-delta-negative-tooltip":"Rule node forces failure of message processing if delta value is negative.","use-caching":"Use caching","use-caching-tooltip":'Rule node will cache the value of "{{inputValueKey}}" that arrives from the incoming message to improve performance. Note that the cache will not be updated if you modify the "{{inputValueKey}}" value elsewhere.',"add-time-difference-between-readings":'Add the time difference between "{{inputValueKey}}" readings',"add-time-difference-between-readings-tooltip":'If enabled, the rule node will add the "{{periodValueKey}}" to the outbound message.',"period-value-key":"Period value key","period-value-key-required":"Period value key is required.","general-pattern-hint":"Use ${metadataKey} for value from metadata, $[messageKey] for value from message body.","alarm-severity-pattern-hint":'Use ${metadataKey} for value from metadata, $[messageKey] for value from message body. Alarm severity should be system (CRITICAL, MAJOR etc.)',"output-node-name-hint":"The rule node name corresponds to the relation type of the output message, and it is used to forward messages to other rule nodes in the caller rule chain.","skip-latest-persistence":"Skip latest persistence","use-server-ts":"Use server ts","use-server-ts-hint":"Enable this setting to use the timestamp of the message processing instead of the timestamp from the message. Useful for all sorts of sequential processing if you merge messages from multiple sources (devices, assets, etc).","kv-map-pattern-hint":"All input fields support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","shared-scope":"Shared scope","server-scope":"Server scope","client-scope":"Client scope","attribute-type":"Attribute","constant-type":"Constant","time-series-type":"Time-series","message-body-type":"Message","message-metadata-type":"Metadata","argument-tile":"Arguments","no-arguments-prompt":"No arguments configured","result-title":"Result","functions-field-input":"Functions","no-option-found":"No option found","argument-source-field-input":"Source","argument-source-field-input-required":"Argument source is required.","argument-key-field-input":"Key","argument-key-field-input-required":"Argument key is required.","constant-value-field-input":"Constant value","constant-value-field-input-required":"Constant value is required.","attribute-scope-field-input":"Attribute scope","attribute-scope-field-input-required":"Attribute scope os required.","default-value-field-input":"Default value","type-field-input":"Type","type-field-input-required":"Type is required.","key-field-input":"Key","add-entity-type":"Add entity type","key-field-input-required":"Key is required.","number-floating-point-field-input":"Number of digits after floating point","number-floating-point-field-input-hint":"Use 0 to convert result to integer","add-to-message-field-input":"Add to message","add-to-metadata-field-input":"Add to metadata","custom-expression-field-input":"Mathematical Expression","custom-expression-field-input-required":"Mathematical expression is required","custom-expression-field-input-hint":"Specify a mathematical expression to evaluate. Default expression demonstrates how to transform Fahrenheit to Celsius","retained-message":"Retained","attributes-mapping":"Attributes mapping","latest-telemetry-mapping":"Latest telemetry mapping","add-mapped-attribute-to":"Add mapped attributes to","add-mapped-latest-telemetry-to":"Add mapped latest telemetry to","add-mapped-fields-to":"Add mapped fields to","add-selected-details-to":"Add selected details to","clear-selected-types":"Clear selected types","clear-selected-details":"Clear selected details","clear-selected-fields":"Clear selected fields","clear-selected-keys":"Clear selected keys","geofence-configuration":"Geofence configuration","coordinate-field-names":"Coordinate field names","coordinate-field-hint":"Rule node tries to retrieve the specified fields from the message. If they are not present, it will look them up in the metadata.","fetch-credentials-to":"Fetch credentials to","add-originator-attributes-to":"Add originator attributes to","originator-attributes":"Originator attributes","fetch-latest-telemetry-with-timestamp":"Fetch latest telemetry with timestamp","fetch-latest-telemetry-with-timestamp-tooltip":'If selected, latest telemetry values will be added to the outbound metadata with timestamp, e.g: "{{latestTsKeyName}}": "{"ts":1574329385897, "value":42}"',"tell-failure":"Tell failure if any of the attributes are missing","tell-failure-tooltip":'If at least one selected key doesn\'t exist the outbound message will report "Failure".',"created-time":"Created time","chip-help":"Press 'Enter' to complete {{inputName}} input. \nPress 'Backspace' to delete {{inputName}}. \nMultiple values supported.",detail:"detail","field-name":"field name","device-profile":"device profile","entity-type":"entity type","message-type":"message type","timeseries-key":"timeseries key",type:"Type","first-name":"First name","last-name":"Last name",label:"Label","originator-fields-mapping":"Originator fields mapping","add-mapped-originator-fields-to":"Add mapped originator fields to",fields:"Fields","skip-empty-fields":"Skip empty fields","skip-empty-fields-tooltip":"Fields with empty values will not be added to the output message/output metadata.","fetch-interval":"Fetch interval","fetch-strategy":"Fetch strategy","fetch-timeseries-from-to":"Fetch timeseries from {{startInterval}} {{startIntervalTimeUnit}} ago to {{endInterval}} {{endIntervalTimeUnit}} ago.","fetch-timeseries-from-to-invalid":'Fetch timeseries invalid ("Interval start" should be less than "Interval end").',"use-metadata-dynamic-interval-tooltip":"If selected, the rule node will use dynamic interval start and end based on the message and metadata patterns.","all-mode-hint":'If selected fetch mode "All" rule node will retrieve telemetry from the fetch interval with configurable query parameters.',"first-mode-hint":'If selected fetch mode "First" rule node will retrieve the closest telemetry to the fetch interval\'s start.',"last-mode-hint":'If selected fetch mode "Last" rule node will retrieve the closest telemetry to the fetch interval\'s end.',ascending:"Ascending",descending:"Descending",min:"Min",max:"Max",average:"Average",sum:"Sum",count:"Count",none:"None","last-level-relation-tooltip":"If selected, the rule node will search related entities only on the level set in the max relation level.","last-level-device-relation-tooltip":"If selected, the rule node will search related devices only on the level set in the max relation level.","data-to-fetch":"Data to fetch","mapping-of-customers":"Mapping of customer's","map-fields-required":"All mapping fields are required.",attributes:"Attributes","related-device-attributes":"Related device attributes","add-selected-attributes-to":"Add selected attributes to","device-profiles":"Device profiles","mapping-of-tenant":"Mapping of tenant's","add-attribute-key":"Add attribute key","message-template":"Message template","message-template-required":"Message template is required","use-system-slack-settings":"Use system slack settings","slack-api-token":"Slack API token","slack-api-token-required":"Slack API token is required"},"key-val":{key:"Key",value:"Value","see-examples":"See examples.","remove-entry":"Remove entry","remove-mapping-entry":"Remove mapping entry","add-mapping-entry":"Add mapping","add-entry":"Add entry","unique-key-value-pair-error":"'{{valText}}' must be different from the current '{{keyText}}'"},"mail-body-type":{"plain-text":"Plain Text",html:"HTML",dynamic:"Dynamic"}}},!0)}(e)}}e("RuleNodeCoreConfigModule",br),br.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:br,deps:[{token:j.TranslateService}],target:t.ɵɵFactoryTarget.NgModule}),br.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.9",ngImport:t,type:br,declarations:[Ze],imports:[K,I],exports:[Nn,ir,Rn,Wn,fr,xr,Ze]}),br.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:br,imports:[K,I,Nn,ir,Rn,Wn,fr,xr]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:br,decorators:[{type:l,args:[{declarations:[Ze],imports:[K,I],exports:[Nn,ir,Rn,Wn,fr,xr,Ze]}]}],ctorParameters:function(){return[{type:j.TranslateService}]}})}}}));//# sourceMappingURL=rulenode-core-config.js.map +System.register(["@angular/core","@shared/public-api","@ngrx/store","@angular/forms","@angular/common","@angular/material/checkbox","@angular/material/input","@angular/material/form-field","@angular/flex-layout/flex","@ngx-translate/core","@angular/material/select","@angular/material/core","@angular/material/slide-toggle","@shared/components/hint-tooltip-icon.component","@shared/components/queue/queue-autocomplete.component","@core/public-api","@shared/components/js-func.component","@angular/material/button","@angular/material/icon","@angular/material/tooltip","@shared/components/script-lang.component","@angular/cdk/keycodes","@angular/material/chips","@shared/pipe/safe.pipe","@shared/components/entity/entity-type-select.component","@shared/components/entity/entity-select.component","@angular/cdk/coercion","@shared/components/tb-error.component","@angular/flex-layout/extended","@angular/material/list","@angular/cdk/drag-drop","rxjs/operators","@angular/material/autocomplete","@shared/pipe/highlight.pipe","@angular/material/expansion","tslib","rxjs","@home/components/public-api","@shared/components/help-popup.component","@shared/components/entity/entity-subtype-list.component","@shared/components/relation/relation-type-autocomplete.component","@home/components/relation/relation-filters.component","@shared/components/file-input.component","@shared/components/button/toggle-password.component","@shared/components/string-items-list.component","@shared/components/toggle-header.component","@shared/components/toggle-select.component","@shared/components/entity/entity-list.component","@shared/components/notification/template-autocomplete.component","@shared/components/tb-checkbox.component","@home/components/sms/sms-provider-configuration.component","@angular/material/radio","@shared/components/slack-conversation-autocomplete.component","@shared/components/entity/entity-autocomplete.component","@shared/components/entity/entity-type-list.component"],(function(e){"use strict";var t,n,r,o,a,i,l,s,m,u,p,d,c,f,g,y,x,b,h,v,C,F,k,L,T,I,N,S,q,A,M,G,E,D,V,P,w,R,O,B,K,U,_,z,H,j,Q,$,J,Y,W,X,Z,ee,te,ne,re,oe,ae,ie,le,se,me,ue,pe,de,ce,fe,ge,ye,xe,be,he,ve,Ce,Fe,ke,Le,Te,Ie,Ne,Se,qe,Ae,Me,Ge,Ee,De,Ve,Pe,we,Re,Oe,Be,Ke,Ue,_e,ze,He,je,Qe,$e,Je,Ye,We,Xe;return{setters:[function(e){t=e,n=e.Component,r=e.EventEmitter,o=e.ViewChild,a=e.forwardRef,i=e.Input,l=e.NgModule},function(e){s=e.RuleNodeConfigurationComponent,m=e.AttributeScope,u=e.telemetryTypeTranslations,p=e.ServiceType,d=e.ScriptLanguage,c=e.AlarmSeverity,f=e.alarmSeverityTranslations,g=e.EntitySearchDirection,y=e.entitySearchDirectionTranslations,x=e.EntityType,b=e.entityFields,h=e.PageComponent,v=e.coerceBoolean,C=e.MessageType,F=e.messageTypeNames,k=e,L=e.AlarmStatus,T=e.alarmStatusTranslations,I=e.SharedModule,N=e.AggregationType,S=e.aggregationTranslations,q=e.NotificationType,A=e.SlackChanelType,M=e.SlackChanelTypesTranslateMap},function(e){G=e},function(e){E=e,D=e.Validators,V=e.NgControl,P=e.NG_VALUE_ACCESSOR,w=e.NG_VALIDATORS,R=e.FormArray,O=e.FormGroup},function(e){B=e,K=e.CommonModule},function(e){U=e},function(e){_=e},function(e){z=e},function(e){H=e},function(e){j=e},function(e){Q=e},function(e){$=e},function(e){J=e},function(e){Y=e},function(e){W=e},function(e){X=e.getCurrentAuthState,Z=e,ee=e.isDefinedAndNotNull,te=e.isEqual,ne=e.deepTrim,re=e.isObject,oe=e.isNotEmptyStr},function(e){ae=e},function(e){ie=e},function(e){le=e},function(e){se=e},function(e){me=e},function(e){ue=e.ENTER,pe=e.COMMA,de=e.SEMICOLON},function(e){ce=e},function(e){fe=e},function(e){ge=e},function(e){ye=e},function(e){xe=e.coerceBooleanProperty},function(e){be=e},function(e){he=e},function(e){ve=e},function(e){Ce=e},function(e){Fe=e.tap,ke=e.map,Le=e.mergeMap,Te=e.startWith,Ie=e.share,Ne=e.takeUntil},function(e){Se=e},function(e){qe=e},function(e){Ae=e},function(e){Me=e.__decorate},function(e){Ge=e.of,Ee=e.Subject,De=e.takeUntil},function(e){Ve=e.HomeComponentsModule},function(e){Pe=e},function(e){we=e},function(e){Re=e},function(e){Oe=e},function(e){Be=e},function(e){Ke=e},function(e){Ue=e},function(e){_e=e},function(e){ze=e},function(e){He=e},function(e){je=e},function(e){Qe=e},function(e){$e=e},function(e){Je=e},function(e){Ye=e},function(e){We=e},function(e){Xe=e}],execute:function(){class Ze extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.emptyConfigForm}onConfigurationSet(e){this.emptyConfigForm=this.fb.group({})}}e("EmptyConfigComponent",Ze),Ze.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Ze,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Ze.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Ze,selector:"tb-node-empty-config",usesInheritance:!0,ngImport:t,template:"
",isInline:!0}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Ze,decorators:[{type:n,args:[{selector:"tb-node-empty-config",template:"
"}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class et extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.assignCustomerConfigForm}onConfigurationSet(e){this.assignCustomerConfigForm=this.fb.group({customerNamePattern:[e?e.customerNamePattern:null,[D.required,D.pattern(/.*\S.*/)]],createCustomerIfNotExists:[!!e&&e.createCustomerIfNotExists,[]],customerCacheExpiration:[e?e.customerCacheExpiration:null,[D.required,D.min(0)]]})}prepareOutputConfig(e){return e.customerNamePattern=e.customerNamePattern.trim(),e}}e("AssignCustomerConfigComponent",et),et.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:et,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),et.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:et,selector:"tb-action-node-assign-to-customer-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.customer-name-pattern\n \n \n {{ \'tb.rulenode.customer-name-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.create-customer-if-not-exists\' | translate }}\n \n \n tb.rulenode.customer-cache-expiration\n \n \n {{ \'tb.rulenode.customer-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.customer-cache-expiration-range\' | translate }}\n \n tb.rulenode.customer-cache-expiration-hint\n \n
\n',dependencies:[{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:U.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:et,decorators:[{type:n,args:[{selector:"tb-action-node-assign-to-customer-config",template:'
\n \n tb.rulenode.customer-name-pattern\n \n \n {{ \'tb.rulenode.customer-name-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.create-customer-if-not-exists\' | translate }}\n \n \n tb.rulenode.customer-cache-expiration\n \n \n {{ \'tb.rulenode.customer-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.customer-cache-expiration-range\' | translate }}\n \n tb.rulenode.customer-cache-expiration-hint\n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class tt extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.attributeScopeMap=m,this.attributeScopes=Object.keys(m),this.telemetryTypeTranslationsMap=u}configForm(){return this.attributesConfigForm}onConfigurationSet(e){this.attributesConfigForm=this.fb.group({scope:[e?e.scope:null,[D.required]],notifyDevice:[!e||e.notifyDevice,[]],sendAttributesUpdatedNotification:[!!e&&e.sendAttributesUpdatedNotification,[]],updateAttributesOnlyOnValueChange:[!!e&&e.updateAttributesOnlyOnValueChange,[]]}),this.attributesConfigForm.get("scope").valueChanges.subscribe((e=>{e!==m.SHARED_SCOPE&&this.attributesConfigForm.get("notifyDevice").patchValue(!1,{emitEvent:!1}),e===m.CLIENT_SCOPE&&this.attributesConfigForm.get("sendAttributesUpdatedNotification").patchValue(!1,{emitEvent:!1}),this.attributesConfigForm.get("updateAttributesOnlyOnValueChange").patchValue(!1,{emitEvent:!1})}))}}e("AttributesConfigComponent",tt),tt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:tt,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),tt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:tt,selector:"tb-action-node-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n \n attribute.attributes-scope\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n
\n
\n \n {{ \'tb.rulenode.update-attributes-only-on-value-change\' | translate }}\n \n
\n
\n \n {{ \'tb.rulenode.send-attributes-updated-notification\' | translate }}\n \n
\n
\n \n {{ \'tb.rulenode.notify-device\' | translate }}\n \n
\n
\n
\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:J.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Y.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:tt,decorators:[{type:n,args:[{selector:"tb-action-node-attributes-config",template:'
\n \n attribute.attributes-scope\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n
\n
\n \n {{ \'tb.rulenode.update-attributes-only-on-value-change\' | translate }}\n \n
\n
\n \n {{ \'tb.rulenode.send-attributes-updated-notification\' | translate }}\n \n
\n
\n \n {{ \'tb.rulenode.notify-device\' | translate }}\n \n
\n
\n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class nt extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.serviceType=p.TB_RULE_ENGINE}configForm(){return this.checkPointConfigForm}onConfigurationSet(e){this.checkPointConfigForm=this.fb.group({queueName:[e?e.queueName:null,[D.required]]})}}e("CheckPointConfigComponent",nt),nt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:nt,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),nt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:nt,selector:"tb-action-node-check-point-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n
\n',dependencies:[{kind:"component",type:W.QueueAutocompleteComponent,selector:"tb-queue-autocomplete",inputs:["labelText","requiredText","autocompleteHint","subscriptSizing","required","queueType","disabled"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:nt,decorators:[{type:n,args:[{selector:"tb-action-node-check-point-config",template:'
\n \n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class rt extends s{constructor(e,t,n,o){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=n,this.translate=o,this.tbelEnabled=X(this.store).tbelEnabled,this.scriptLanguage=d,this.changeScript=new r,this.hasScript=!0,this.testScriptLabel="tb.rulenode.test-details-function"}configForm(){return this.clearAlarmConfigForm}onConfigurationSet(e){this.clearAlarmConfigForm=this.fb.group({scriptLang:[e?e.scriptLang:d.JS,[D.required]],alarmDetailsBuildJs:[e?e.alarmDetailsBuildJs:null,[]],alarmDetailsBuildTbel:[e?e.alarmDetailsBuildTbel:null,[]],alarmType:[e?e.alarmType:null,[D.required]]})}validatorTriggers(){return["scriptLang"]}updateValidators(e){let t=this.clearAlarmConfigForm.get("scriptLang").value;t!==d.TBEL||this.tbelEnabled||(t=d.JS,this.clearAlarmConfigForm.get("scriptLang").patchValue(t,{emitEvent:!1}),setTimeout((()=>{this.clearAlarmConfigForm.updateValueAndValidity({emitEvent:!0})}))),this.clearAlarmConfigForm.get("alarmDetailsBuildJs").setValidators(t===d.JS?[D.required]:[]),this.clearAlarmConfigForm.get("alarmDetailsBuildJs").updateValueAndValidity({emitEvent:e}),this.clearAlarmConfigForm.get("alarmDetailsBuildTbel").setValidators(t===d.TBEL?[D.required]:[]),this.clearAlarmConfigForm.get("alarmDetailsBuildTbel").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=d.JS)),e}testScript(e){const t=this.clearAlarmConfigForm.get("scriptLang").value,n=t===d.JS?"alarmDetailsBuildJs":"alarmDetailsBuildTbel",r=t===d.JS?"rulenode/clear_alarm_node_script_fn":"rulenode/tbel/clear_alarm_node_script_fn",o=this.clearAlarmConfigForm.get(n).value;this.nodeScriptTestService.testNodeScript(o,"json",this.translate.instant("tb.rulenode.details"),"Details",["msg","metadata","msgType"],this.ruleNodeId,r,t,e).subscribe((e=>{e&&(this.clearAlarmConfigForm.get(n).setValue(e),this.changeScript.emit())}))}onValidate(){this.clearAlarmConfigForm.get("scriptLang").value===d.JS&&this.jsFuncComponent.validateOnSubmit()}}e("ClearAlarmConfigComponent",rt),rt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:rt,deps:[{token:G.Store},{token:E.UntypedFormBuilder},{token:Z.NodeScriptTestService},{token:j.TranslateService}],target:t.ɵɵFactoryTarget.Component}),rt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:rt,selector:"tb-action-node-clear-alarm-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0},{propertyName:"tbelFuncComponent",first:!0,predicate:["tbelFuncComponent"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n \n \n \n
\n \n
\n \n tb.rulenode.alarm-type\n \n \n {{ \'tb.rulenode.alarm-type-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n',dependencies:[{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ae.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","scriptLanguage","noValidate","required"]},{kind:"component",type:ie.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ie.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:se.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:me.TbScriptLangComponent,selector:"tb-script-lang",inputs:["disabled"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:rt,decorators:[{type:n,args:[{selector:"tb-action-node-clear-alarm-config",template:'
\n \n \n \n \n \n \n \n
\n \n
\n \n tb.rulenode.alarm-type\n \n \n {{ \'tb.rulenode.alarm-type-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder},{type:Z.NodeScriptTestService},{type:j.TranslateService}]},propDecorators:{jsFuncComponent:[{type:o,args:["jsFuncComponent",{static:!1}]}],tbelFuncComponent:[{type:o,args:["tbelFuncComponent",{static:!1}]}]}});class ot extends s{constructor(e,t,n,o){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=n,this.translate=o,this.alarmSeverities=Object.keys(c),this.alarmSeverityTranslationMap=f,this.separatorKeysCodes=[ue,pe,de],this.tbelEnabled=X(this.store).tbelEnabled,this.scriptLanguage=d,this.changeScript=new r,this.hasScript=!0,this.testScriptLabel="tb.rulenode.test-details-function"}configForm(){return this.createAlarmConfigForm}onConfigurationSet(e){this.createAlarmConfigForm=this.fb.group({scriptLang:[e?e.scriptLang:d.JS,[D.required]],alarmDetailsBuildJs:[e?e.alarmDetailsBuildJs:null,[]],alarmDetailsBuildTbel:[e?e.alarmDetailsBuildTbel:null,[]],useMessageAlarmData:[!!e&&e.useMessageAlarmData,[]],overwriteAlarmDetails:[!!e&&e.overwriteAlarmDetails,[]],alarmType:[e?e.alarmType:null,[]],severity:[e?e.severity:null,[]],propagate:[!!e&&e.propagate,[]],relationTypes:[e?e.relationTypes:null,[]],propagateToOwner:[!!e&&e.propagateToOwner,[]],propagateToTenant:[!!e&&e.propagateToTenant,[]],dynamicSeverity:!1}),this.createAlarmConfigForm.get("dynamicSeverity").valueChanges.subscribe((e=>{e?this.createAlarmConfigForm.get("severity").patchValue("",{emitEvent:!1}):this.createAlarmConfigForm.get("severity").patchValue(this.alarmSeverities[0],{emitEvent:!1})}))}validatorTriggers(){return["useMessageAlarmData","overwriteAlarmDetails","scriptLang"]}updateValidators(e){const t=this.createAlarmConfigForm.get("useMessageAlarmData").value,n=this.createAlarmConfigForm.get("overwriteAlarmDetails").value;t?(this.createAlarmConfigForm.get("alarmType").setValidators([]),this.createAlarmConfigForm.get("severity").setValidators([])):(this.createAlarmConfigForm.get("alarmType").setValidators([D.required]),this.createAlarmConfigForm.get("severity").setValidators([D.required])),this.createAlarmConfigForm.get("alarmType").updateValueAndValidity({emitEvent:e}),this.createAlarmConfigForm.get("severity").updateValueAndValidity({emitEvent:e});let r=this.createAlarmConfigForm.get("scriptLang").value;r!==d.TBEL||this.tbelEnabled||(r=d.JS,this.createAlarmConfigForm.get("scriptLang").patchValue(r,{emitEvent:!1}),setTimeout((()=>{this.createAlarmConfigForm.updateValueAndValidity({emitEvent:!0})})));const o=!1===t||!0===n;this.createAlarmConfigForm.get("alarmDetailsBuildJs").setValidators(o&&r===d.JS?[D.required]:[]),this.createAlarmConfigForm.get("alarmDetailsBuildTbel").setValidators(o&&r===d.TBEL?[D.required]:[]),this.createAlarmConfigForm.get("alarmDetailsBuildJs").updateValueAndValidity({emitEvent:e}),this.createAlarmConfigForm.get("alarmDetailsBuildTbel").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=d.JS)),e}testScript(e){const t=this.createAlarmConfigForm.get("scriptLang").value,n=t===d.JS?"alarmDetailsBuildJs":"alarmDetailsBuildTbel",r=t===d.JS?"rulenode/create_alarm_node_script_fn":"rulenode/tbel/create_alarm_node_script_fn",o=this.createAlarmConfigForm.get(n).value;this.nodeScriptTestService.testNodeScript(o,"json",this.translate.instant("tb.rulenode.details"),"Details",["msg","metadata","msgType"],this.ruleNodeId,r,t,e).subscribe((e=>{e&&(this.createAlarmConfigForm.get(n).setValue(e),this.changeScript.emit())}))}removeKey(e,t){const n=this.createAlarmConfigForm.get(t).value,r=n.indexOf(e);r>=0&&(n.splice(r,1),this.createAlarmConfigForm.get(t).setValue(n,{emitEvent:!0}))}addKey(e,t){const n=e.input;let r=e.value;if((r||"").trim()){r=r.trim();let e=this.createAlarmConfigForm.get(t).value;e&&-1!==e.indexOf(r)||(e||(e=[]),e.push(r),this.createAlarmConfigForm.get(t).setValue(e,{emitEvent:!0}))}n&&(n.value="")}onValidate(){const e=this.createAlarmConfigForm.get("useMessageAlarmData").value,t=this.createAlarmConfigForm.get("overwriteAlarmDetails").value;if(!e||t){this.createAlarmConfigForm.get("scriptLang").value===d.JS&&this.jsFuncComponent.validateOnSubmit()}}}e("CreateAlarmConfigComponent",ot),ot.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ot,deps:[{token:G.Store},{token:E.UntypedFormBuilder},{token:Z.NodeScriptTestService},{token:j.TranslateService}],target:t.ɵɵFactoryTarget.Component}),ot.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:ot,selector:"tb-action-node-create-alarm-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0},{propertyName:"tbelFuncComponent",first:!0,predicate:["tbelFuncComponent"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.use-message-alarm-data\' | translate }}\n \n \n {{ \'tb.rulenode.overwrite-alarm-details\' | translate }}\n \n
\n \n \n \n \n \n \n \n
\n \n
\n
\n
\n \n tb.rulenode.alarm-type\n \n \n {{ \'tb.rulenode.alarm-type-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.use-alarm-severity-pattern\' | translate }}\n \n \n tb.rulenode.alarm-severity\n \n \n {{ alarmSeverityTranslationMap.get(severity) | translate }}\n \n \n \n {{ \'tb.rulenode.alarm-severity-required\' | translate }}\n \n \n \n tb.rulenode.alarm-severity-pattern\n \n \n {{ \'tb.rulenode.alarm-severity-required\' | translate }}\n \n \n \n \n {{ \'tb.rulenode.propagate\' | translate }}\n \n
\n \n tb.rulenode.relation-types-list\n \n \n {{key}}\n close\n \n \n \n tb.rulenode.relation-types-list-hint\n \n
\n \n {{ \'tb.rulenode.propagate-to-owner\' | translate }}\n \n \n {{ \'tb.rulenode.propagate-to-tenant\' | translate }}\n \n
\n
\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ae.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","scriptLanguage","noValidate","required"]},{kind:"component",type:ie.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ie.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:U.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:se.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"component",type:ce.MatChipGrid,selector:"mat-chip-grid",inputs:["tabIndex","disabled","placeholder","required","value","errorStateMatcher"],outputs:["change","valueChange"]},{kind:"directive",type:ce.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputFor","matChipInputAddOnBlur","matChipInputSeparatorKeyCodes","placeholder","id","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]},{kind:"directive",type:ce.MatChipRemove,selector:"[matChipRemove]"},{kind:"component",type:ce.MatChipRow,selector:"mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]",inputs:["color","disabled","disableRipple","tabIndex","editable"],outputs:["edited"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:me.TbScriptLangComponent,selector:"tb-script-lang",inputs:["disabled"]},{kind:"pipe",type:fe.SafePipe,name:"safe"},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ot,decorators:[{type:n,args:[{selector:"tb-action-node-create-alarm-config",template:'
\n \n {{ \'tb.rulenode.use-message-alarm-data\' | translate }}\n \n \n {{ \'tb.rulenode.overwrite-alarm-details\' | translate }}\n \n
\n \n \n \n \n \n \n \n
\n \n
\n
\n
\n \n tb.rulenode.alarm-type\n \n \n {{ \'tb.rulenode.alarm-type-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.use-alarm-severity-pattern\' | translate }}\n \n \n tb.rulenode.alarm-severity\n \n \n {{ alarmSeverityTranslationMap.get(severity) | translate }}\n \n \n \n {{ \'tb.rulenode.alarm-severity-required\' | translate }}\n \n \n \n tb.rulenode.alarm-severity-pattern\n \n \n {{ \'tb.rulenode.alarm-severity-required\' | translate }}\n \n \n \n \n {{ \'tb.rulenode.propagate\' | translate }}\n \n
\n \n tb.rulenode.relation-types-list\n \n \n {{key}}\n close\n \n \n \n tb.rulenode.relation-types-list-hint\n \n
\n \n {{ \'tb.rulenode.propagate-to-owner\' | translate }}\n \n \n {{ \'tb.rulenode.propagate-to-tenant\' | translate }}\n \n
\n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder},{type:Z.NodeScriptTestService},{type:j.TranslateService}]},propDecorators:{jsFuncComponent:[{type:o,args:["jsFuncComponent",{static:!1}]}],tbelFuncComponent:[{type:o,args:["tbelFuncComponent",{static:!1}]}]}});class at extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.directionTypes=Object.keys(g),this.directionTypeTranslations=y,this.entityType=x}configForm(){return this.createRelationConfigForm}onConfigurationSet(e){this.createRelationConfigForm=this.fb.group({direction:[e?e.direction:null,[D.required]],entityType:[e?e.entityType:null,[D.required]],entityNamePattern:[e?e.entityNamePattern:null,[]],entityTypePattern:[e?e.entityTypePattern:null,[]],relationType:[e?e.relationType:null,[D.required]],createEntityIfNotExists:[!!e&&e.createEntityIfNotExists,[]],removeCurrentRelations:[!!e&&e.removeCurrentRelations,[]],changeOriginatorToRelatedEntity:[!!e&&e.changeOriginatorToRelatedEntity,[]],entityCacheExpiration:[e?e.entityCacheExpiration:null,[D.required,D.min(0)]]})}validatorTriggers(){return["entityType"]}updateValidators(e){const t=this.createRelationConfigForm.get("entityType").value;t?this.createRelationConfigForm.get("entityNamePattern").setValidators([D.required,D.pattern(/.*\S.*/)]):this.createRelationConfigForm.get("entityNamePattern").setValidators([]),!t||t!==x.DEVICE&&t!==x.ASSET?this.createRelationConfigForm.get("entityTypePattern").setValidators([]):this.createRelationConfigForm.get("entityTypePattern").setValidators([D.required,D.pattern(/.*\S.*/)]),this.createRelationConfigForm.get("entityNamePattern").updateValueAndValidity({emitEvent:e}),this.createRelationConfigForm.get("entityTypePattern").updateValueAndValidity({emitEvent:e})}prepareOutputConfig(e){return e.entityNamePattern=e.entityNamePattern?e.entityNamePattern.trim():null,e.entityTypePattern=e.entityTypePattern?e.entityTypePattern.trim():null,e}}e("CreateRelationConfigComponent",at),at.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:at,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),at.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:at,selector:"tb-action-node-create-relation-config",usesInheritance:!0,ngImport:t,template:'
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.entity-name-pattern\n \n \n {{ \'tb.rulenode.entity-name-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.entity-type-pattern\n \n \n {{ \'tb.rulenode.entity-type-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n \n tb.rulenode.relation-type-pattern\n \n \n {{ \'tb.rulenode.relation-type-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n \n {{ \'tb.rulenode.create-entity-if-not-exists\' | translate }}\n \n
tb.rulenode.create-entity-if-not-exists-hint
\n
\n \n {{ \'tb.rulenode.remove-current-relations\' | translate }}\n \n
tb.rulenode.remove-current-relations-hint
\n \n {{ \'tb.rulenode.change-originator-to-related-entity\' | translate }}\n \n
tb.rulenode.change-originator-to-related-entity-hint
\n \n tb.rulenode.entity-cache-expiration\n \n \n {{ \'tb.rulenode.entity-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.entity-cache-expiration-range\' | translate }}\n \n tb.rulenode.entity-cache-expiration-hint\n \n
\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ge.EntityTypeSelectComponent,selector:"tb-entity-type-select",inputs:["allowedEntityTypes","useAliasEntityTypes","filterAllowedEntityTypes","showLabel","required","disabled"]},{kind:"component",type:U.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:at,decorators:[{type:n,args:[{selector:"tb-action-node-create-relation-config",template:'
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.entity-name-pattern\n \n \n {{ \'tb.rulenode.entity-name-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.entity-type-pattern\n \n \n {{ \'tb.rulenode.entity-type-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n \n tb.rulenode.relation-type-pattern\n \n \n {{ \'tb.rulenode.relation-type-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n \n {{ \'tb.rulenode.create-entity-if-not-exists\' | translate }}\n \n
tb.rulenode.create-entity-if-not-exists-hint
\n
\n \n {{ \'tb.rulenode.remove-current-relations\' | translate }}\n \n
tb.rulenode.remove-current-relations-hint
\n \n {{ \'tb.rulenode.change-originator-to-related-entity\' | translate }}\n \n
tb.rulenode.change-originator-to-related-entity-hint
\n \n tb.rulenode.entity-cache-expiration\n \n \n {{ \'tb.rulenode.entity-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.entity-cache-expiration-range\' | translate }}\n \n tb.rulenode.entity-cache-expiration-hint\n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class it extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.directionTypes=Object.keys(g),this.directionTypeTranslations=y,this.entityType=x}configForm(){return this.deleteRelationConfigForm}onConfigurationSet(e){this.deleteRelationConfigForm=this.fb.group({deleteForSingleEntity:[!!e&&e.deleteForSingleEntity,[]],direction:[e?e.direction:null,[D.required]],entityType:[e?e.entityType:null,[]],entityNamePattern:[e?e.entityNamePattern:null,[]],relationType:[e?e.relationType:null,[D.required]],entityCacheExpiration:[e?e.entityCacheExpiration:null,[D.required,D.min(0)]]})}validatorTriggers(){return["deleteForSingleEntity","entityType"]}updateValidators(e){const t=this.deleteRelationConfigForm.get("deleteForSingleEntity").value,n=this.deleteRelationConfigForm.get("entityType").value;t?this.deleteRelationConfigForm.get("entityType").setValidators([D.required]):this.deleteRelationConfigForm.get("entityType").setValidators([]),t&&n?this.deleteRelationConfigForm.get("entityNamePattern").setValidators([D.required,D.pattern(/.*\S.*/)]):this.deleteRelationConfigForm.get("entityNamePattern").setValidators([]),this.deleteRelationConfigForm.get("entityType").updateValueAndValidity({emitEvent:!1}),this.deleteRelationConfigForm.get("entityNamePattern").updateValueAndValidity({emitEvent:e})}prepareOutputConfig(e){return e.entityNamePattern=e.entityNamePattern?e.entityNamePattern.trim():null,e}}e("DeleteRelationConfigComponent",it),it.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:it,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),it.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:it,selector:"tb-action-node-delete-relation-config",usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.delete-relation-to-specific-entity\' | translate }}\n \n
tb.rulenode.delete-relation-hint
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.entity-name-pattern\n \n \n {{ \'tb.rulenode.entity-name-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n \n tb.rulenode.relation-type-pattern\n \n \n {{ \'tb.rulenode.relation-type-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.entity-cache-expiration\n \n \n {{ \'tb.rulenode.entity-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.entity-cache-expiration-range\' | translate }}\n \n tb.rulenode.entity-cache-expiration-hint\n \n
\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ge.EntityTypeSelectComponent,selector:"tb-entity-type-select",inputs:["allowedEntityTypes","useAliasEntityTypes","filterAllowedEntityTypes","showLabel","required","disabled"]},{kind:"component",type:U.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:it,decorators:[{type:n,args:[{selector:"tb-action-node-delete-relation-config",template:'
\n \n {{ \'tb.rulenode.delete-relation-to-specific-entity\' | translate }}\n \n
tb.rulenode.delete-relation-hint
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.entity-name-pattern\n \n \n {{ \'tb.rulenode.entity-name-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n \n tb.rulenode.relation-type-pattern\n \n \n {{ \'tb.rulenode.relation-type-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.entity-cache-expiration\n \n \n {{ \'tb.rulenode.entity-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.entity-cache-expiration-range\' | translate }}\n \n tb.rulenode.entity-cache-expiration-hint\n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class lt extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.deviceProfile}onConfigurationSet(e){this.deviceProfile=this.fb.group({persistAlarmRulesState:[!!e&&e.persistAlarmRulesState,D.required],fetchAlarmRulesStateOnStart:[!!e&&e.fetchAlarmRulesStateOnStart,D.required]})}}e("DeviceProfileConfigComponent",lt),lt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:lt,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),lt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:lt,selector:"tb-device-profile-config",usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.persist-alarm-rules\' | translate }}\n \n \n {{ \'tb.rulenode.fetch-alarm-rules\' | translate }}\n \n
\n',dependencies:[{kind:"component",type:U.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:lt,decorators:[{type:n,args:[{selector:"tb-device-profile-config",template:'
\n \n {{ \'tb.rulenode.persist-alarm-rules\' | translate }}\n \n \n {{ \'tb.rulenode.fetch-alarm-rules\' | translate }}\n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class st extends s{constructor(e,t,n,o){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=n,this.translate=o,this.tbelEnabled=X(this.store).tbelEnabled,this.scriptLanguage=d,this.changeScript=new r,this.hasScript=!0,this.testScriptLabel="tb.rulenode.test-generator-function",this.serviceType=p.TB_RULE_ENGINE}configForm(){return this.generatorConfigForm}onConfigurationSet(e){this.generatorConfigForm=this.fb.group({msgCount:[e?e.msgCount:null,[D.required,D.min(0)]],periodInSeconds:[e?e.periodInSeconds:null,[D.required,D.min(1)]],originator:[e?e.originator:null,[]],scriptLang:[e?e.scriptLang:d.JS,[D.required]],jsScript:[e?e.jsScript:null,[]],tbelScript:[e?e.tbelScript:null,[]],queueName:[e?e.queueName:null,[]]})}validatorTriggers(){return["scriptLang"]}updateValidators(e){let t=this.generatorConfigForm.get("scriptLang").value;t!==d.TBEL||this.tbelEnabled||(t=d.JS,this.generatorConfigForm.get("scriptLang").patchValue(t,{emitEvent:!1}),setTimeout((()=>{this.generatorConfigForm.updateValueAndValidity({emitEvent:!0})}))),this.generatorConfigForm.get("jsScript").setValidators(t===d.JS?[D.required]:[]),this.generatorConfigForm.get("jsScript").updateValueAndValidity({emitEvent:e}),this.generatorConfigForm.get("tbelScript").setValidators(t===d.TBEL?[D.required]:[]),this.generatorConfigForm.get("tbelScript").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=d.JS),e.originatorId&&e.originatorType?e.originator={id:e.originatorId,entityType:e.originatorType}:e.originator=null,delete e.originatorId,delete e.originatorType),e}prepareOutputConfig(e){return e.originator?(e.originatorId=e.originator.id,e.originatorType=e.originator.entityType):(e.originatorId=null,e.originatorType=null),delete e.originator,e}testScript(e){const t=this.generatorConfigForm.get("scriptLang").value,n=t===d.JS?"jsScript":"tbelScript",r=t===d.JS?"rulenode/generator_node_script_fn":"rulenode/tbel/generator_node_script_fn",o=this.generatorConfigForm.get(n).value;this.nodeScriptTestService.testNodeScript(o,"generate",this.translate.instant("tb.rulenode.generator"),"Generate",["prevMsg","prevMetadata","prevMsgType"],this.ruleNodeId,r,t,e).subscribe((e=>{e&&(this.generatorConfigForm.get(n).setValue(e),this.changeScript.emit())}))}onValidate(){this.generatorConfigForm.get("scriptLang").value===d.JS&&this.jsFuncComponent.validateOnSubmit()}}var mt;e("GeneratorConfigComponent",st),st.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:st,deps:[{token:G.Store},{token:E.UntypedFormBuilder},{token:Z.NodeScriptTestService},{token:j.TranslateService}],target:t.ɵɵFactoryTarget.Component}),st.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:st,selector:"tb-action-node-generator-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0},{propertyName:"tbelFuncComponent",first:!0,predicate:["tbelFuncComponent"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.message-count\n \n \n {{ \'tb.rulenode.message-count-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-message-count-message\' | translate }}\n \n \n \n tb.rulenode.period-seconds\n \n \n {{ \'tb.rulenode.period-seconds-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-period-seconds-message\' | translate }}\n \n \n
\n \n \n \n
\n\n \n \n\n \n \n \n \n \n \n \n
\n \n
\n
\n',dependencies:[{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ye.EntitySelectComponent,selector:"tb-entity-select",inputs:["allowedEntityTypes","useAliasEntityTypes","required","disabled"]},{kind:"component",type:W.QueueAutocompleteComponent,selector:"tb-queue-autocomplete",inputs:["labelText","requiredText","autocompleteHint","subscriptSizing","required","queueType","disabled"]},{kind:"component",type:ae.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","scriptLanguage","noValidate","required"]},{kind:"component",type:ie.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ie.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:se.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:me.TbScriptLangComponent,selector:"tb-script-lang",inputs:["disabled"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:st,decorators:[{type:n,args:[{selector:"tb-action-node-generator-config",template:'
\n \n tb.rulenode.message-count\n \n \n {{ \'tb.rulenode.message-count-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-message-count-message\' | translate }}\n \n \n \n tb.rulenode.period-seconds\n \n \n {{ \'tb.rulenode.period-seconds-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-period-seconds-message\' | translate }}\n \n \n
\n \n \n \n
\n\n \n \n\n \n \n \n \n \n \n \n
\n \n
\n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder},{type:Z.NodeScriptTestService},{type:j.TranslateService}]},propDecorators:{jsFuncComponent:[{type:o,args:["jsFuncComponent",{static:!1}]}],tbelFuncComponent:[{type:o,args:["tbelFuncComponent",{static:!1}]}]}}),function(e){e.CUSTOMER="CUSTOMER",e.TENANT="TENANT",e.RELATED="RELATED",e.ALARM_ORIGINATOR="ALARM_ORIGINATOR",e.ENTITY="ENTITY"}(mt||(mt={}));const ut=new Map([[mt.CUSTOMER,"tb.rulenode.originator-customer"],[mt.TENANT,"tb.rulenode.originator-tenant"],[mt.RELATED,"tb.rulenode.originator-related"],[mt.ALARM_ORIGINATOR,"tb.rulenode.originator-alarm-originator"],[mt.ENTITY,"tb.rulenode.originator-entity"]]),pt=[b.createdTime,b.name,{value:"type",name:"tb.rulenode.profile-name",keyName:"originatorProfileName"},b.firstName,b.lastName,b.email,b.title,b.country,b.state,b.city,b.address,b.address2,b.zip,b.phone,b.label,{value:"id",name:"tb.rulenode.id",keyName:"id"},{value:"additionalInfo",name:"tb.rulenode.additional-info",keyName:"additionalInfo"}],dt=new Map([["type","profileName"],["createdTime","createdTime"],["name","name"],["firstName","firstName"],["lastName","lastName"],["email","email"],["title","title"],["country","country"],["state","state"],["city","city"],["address","address"],["address2","address2"],["zip","zip"],["phone","phone"],["label","label"],["id","id"],["additionalInfo","additionalInfo"]]);var ct;!function(e){e.CIRCLE="CIRCLE",e.POLYGON="POLYGON"}(ct||(ct={}));const ft=new Map([[ct.CIRCLE,"tb.rulenode.perimeter-circle"],[ct.POLYGON,"tb.rulenode.perimeter-polygon"]]);var gt;!function(e){e.MILLISECONDS="MILLISECONDS",e.SECONDS="SECONDS",e.MINUTES="MINUTES",e.HOURS="HOURS",e.DAYS="DAYS"}(gt||(gt={}));const yt=new Map([[gt.MILLISECONDS,"tb.rulenode.time-unit-milliseconds"],[gt.SECONDS,"tb.rulenode.time-unit-seconds"],[gt.MINUTES,"tb.rulenode.time-unit-minutes"],[gt.HOURS,"tb.rulenode.time-unit-hours"],[gt.DAYS,"tb.rulenode.time-unit-days"]]);var xt;!function(e){e.METER="METER",e.KILOMETER="KILOMETER",e.FOOT="FOOT",e.MILE="MILE",e.NAUTICAL_MILE="NAUTICAL_MILE"}(xt||(xt={}));const bt=new Map([[xt.METER,"tb.rulenode.range-unit-meter"],[xt.KILOMETER,"tb.rulenode.range-unit-kilometer"],[xt.FOOT,"tb.rulenode.range-unit-foot"],[xt.MILE,"tb.rulenode.range-unit-mile"],[xt.NAUTICAL_MILE,"tb.rulenode.range-unit-nautical-mile"]]);var ht;!function(e){e.ID="ID",e.TITLE="TITLE",e.COUNTRY="COUNTRY",e.STATE="STATE",e.CITY="CITY",e.ZIP="ZIP",e.ADDRESS="ADDRESS",e.ADDRESS2="ADDRESS2",e.PHONE="PHONE",e.EMAIL="EMAIL",e.ADDITIONAL_INFO="ADDITIONAL_INFO"}(ht||(ht={}));const vt=new Map([[ht.ID,"tb.rulenode.entity-details-id"],[ht.TITLE,"tb.rulenode.entity-details-title"],[ht.COUNTRY,"tb.rulenode.entity-details-country"],[ht.STATE,"tb.rulenode.entity-details-state"],[ht.CITY,"tb.rulenode.entity-details-city"],[ht.ZIP,"tb.rulenode.entity-details-zip"],[ht.ADDRESS,"tb.rulenode.entity-details-address"],[ht.ADDRESS2,"tb.rulenode.entity-details-address2"],[ht.PHONE,"tb.rulenode.entity-details-phone"],[ht.EMAIL,"tb.rulenode.entity-details-email"],[ht.ADDITIONAL_INFO,"tb.rulenode.entity-details-additional_info"]]);var Ct;!function(e){e.FIRST="FIRST",e.LAST="LAST",e.ALL="ALL"}(Ct||(Ct={}));const Ft=new Map([[Ct.FIRST,"tb.rulenode.first"],[Ct.LAST,"tb.rulenode.last"],[Ct.ALL,"tb.rulenode.all"]]),kt=new Map([[Ct.FIRST,"tb.rulenode.first-mode-hint"],[Ct.LAST,"tb.rulenode.last-mode-hint"],[Ct.ALL,"tb.rulenode.all-mode-hint"]]);var Lt,Tt;!function(e){e.ASC="ASC",e.DESC="DESC"}(Lt||(Lt={})),function(e){e.ATTRIBUTES="ATTRIBUTES",e.LATEST_TELEMETRY="LATEST_TELEMETRY",e.FIELDS="FIELDS"}(Tt||(Tt={}));const It=new Map([[Tt.ATTRIBUTES,"tb.rulenode.attributes"],[Tt.LATEST_TELEMETRY,"tb.rulenode.latest-telemetry"],[Tt.FIELDS,"tb.rulenode.fields"]]),Nt=new Map([[Tt.ATTRIBUTES,"tb.rulenode.add-mapped-attribute-to"],[Tt.LATEST_TELEMETRY,"tb.rulenode.add-mapped-latest-telemetry-to"],[Tt.FIELDS,"tb.rulenode.add-mapped-fields-to"]]),St=new Map([[Lt.ASC,"tb.rulenode.ascending"],[Lt.DESC,"tb.rulenode.descending"]]);var qt;!function(e){e.STANDARD="STANDARD",e.FIFO="FIFO"}(qt||(qt={}));const At=new Map([[qt.STANDARD,"tb.rulenode.sqs-queue-standard"],[qt.FIFO,"tb.rulenode.sqs-queue-fifo"]]),Mt=["anonymous","basic","cert.PEM"],Gt=new Map([["anonymous","tb.rulenode.credentials-anonymous"],["basic","tb.rulenode.credentials-basic"],["cert.PEM","tb.rulenode.credentials-pem"]]),Et=["sas","cert.PEM"],Dt=new Map([["sas","tb.rulenode.credentials-sas"],["cert.PEM","tb.rulenode.credentials-pem"]]);var Vt;!function(e){e.GET="GET",e.POST="POST",e.PUT="PUT",e.DELETE="DELETE"}(Vt||(Vt={}));const Pt=["US-ASCII","ISO-8859-1","UTF-8","UTF-16BE","UTF-16LE","UTF-16"],wt=new Map([["US-ASCII","tb.rulenode.charset-us-ascii"],["ISO-8859-1","tb.rulenode.charset-iso-8859-1"],["UTF-8","tb.rulenode.charset-utf-8"],["UTF-16BE","tb.rulenode.charset-utf-16be"],["UTF-16LE","tb.rulenode.charset-utf-16le"],["UTF-16","tb.rulenode.charset-utf-16"]]);var Rt;!function(e){e.CUSTOM="CUSTOM",e.ADD="ADD",e.SUB="SUB",e.MULT="MULT",e.DIV="DIV",e.SIN="SIN",e.SINH="SINH",e.COS="COS",e.COSH="COSH",e.TAN="TAN",e.TANH="TANH",e.ACOS="ACOS",e.ASIN="ASIN",e.ATAN="ATAN",e.ATAN2="ATAN2",e.EXP="EXP",e.EXPM1="EXPM1",e.SQRT="SQRT",e.CBRT="CBRT",e.GET_EXP="GET_EXP",e.HYPOT="HYPOT",e.LOG="LOG",e.LOG10="LOG10",e.LOG1P="LOG1P",e.CEIL="CEIL",e.FLOOR="FLOOR",e.FLOOR_DIV="FLOOR_DIV",e.FLOOR_MOD="FLOOR_MOD",e.ABS="ABS",e.MIN="MIN",e.MAX="MAX",e.POW="POW",e.SIGNUM="SIGNUM",e.RAD="RAD",e.DEG="DEG"}(Rt||(Rt={}));const Ot=new Map([[Rt.CUSTOM,{value:Rt.CUSTOM,name:"Custom Function",description:"Use this function to specify complex mathematical expression.",minArgs:1,maxArgs:16}],[Rt.ADD,{value:Rt.ADD,name:"Addition",description:"x + y",minArgs:2,maxArgs:2}],[Rt.SUB,{value:Rt.SUB,name:"Subtraction",description:"x - y",minArgs:2,maxArgs:2}],[Rt.MULT,{value:Rt.MULT,name:"Multiplication",description:"x * y",minArgs:2,maxArgs:2}],[Rt.DIV,{value:Rt.DIV,name:"Division",description:"x / y",minArgs:2,maxArgs:2}],[Rt.SIN,{value:Rt.SIN,name:"Sine",description:"Returns the trigonometric sine of an angle in radians.",minArgs:1,maxArgs:1}],[Rt.SINH,{value:Rt.SINH,name:"Hyperbolic sine",description:"Returns the hyperbolic sine of an argument.",minArgs:1,maxArgs:1}],[Rt.COS,{value:Rt.COS,name:"Cosine",description:"Returns the trigonometric cosine of an angle in radians.",minArgs:1,maxArgs:1}],[Rt.COSH,{value:Rt.COSH,name:"Hyperbolic cosine",description:"Returns the hyperbolic cosine of an argument.",minArgs:1,maxArgs:1}],[Rt.TAN,{value:Rt.TAN,name:"Tangent",description:"Returns the trigonometric tangent of an angle in radians",minArgs:1,maxArgs:1}],[Rt.TANH,{value:Rt.TANH,name:"Hyperbolic tangent",description:"Returns the hyperbolic tangent of an argument",minArgs:1,maxArgs:1}],[Rt.ACOS,{value:Rt.ACOS,name:"Arc cosine",description:"Returns the arc cosine of an argument",minArgs:1,maxArgs:1}],[Rt.ASIN,{value:Rt.ASIN,name:"Arc sine",description:"Returns the arc sine of an argument",minArgs:1,maxArgs:1}],[Rt.ATAN,{value:Rt.ATAN,name:"Arc tangent",description:"Returns the arc tangent of an argument",minArgs:1,maxArgs:1}],[Rt.ATAN2,{value:Rt.ATAN2,name:"2-argument arc tangent",description:"Returns the angle theta from the conversion of rectangular coordinates (x, y) to polar coordinates (r, theta)",minArgs:2,maxArgs:2}],[Rt.EXP,{value:Rt.EXP,name:"Exponential",description:"Returns Euler's number e raised to the power of an argument",minArgs:1,maxArgs:1}],[Rt.EXPM1,{value:Rt.EXPM1,name:"Exponential minus one",description:"Returns Euler's number e raised to the power of an argument minus one",minArgs:1,maxArgs:1}],[Rt.SQRT,{value:Rt.SQRT,name:"Square",description:"Returns the correctly rounded positive square root of an argument",minArgs:1,maxArgs:1}],[Rt.CBRT,{value:Rt.CBRT,name:"Cube root",description:"Returns the cube root of an argument",minArgs:1,maxArgs:1}],[Rt.GET_EXP,{value:Rt.GET_EXP,name:"Get exponent",description:"Returns the unbiased exponent used in the representation of an argument",minArgs:1,maxArgs:1}],[Rt.HYPOT,{value:Rt.HYPOT,name:"Square root",description:"Returns the square root of the squares of the arguments",minArgs:2,maxArgs:2}],[Rt.LOG,{value:Rt.LOG,name:"Logarithm",description:"Returns the natural logarithm of an argument",minArgs:1,maxArgs:1}],[Rt.LOG10,{value:Rt.LOG10,name:"Base 10 logarithm",description:"Returns the base 10 logarithm of an argument",minArgs:1,maxArgs:1}],[Rt.LOG1P,{value:Rt.LOG1P,name:"Logarithm of the sum",description:"Returns the natural logarithm of the sum of an argument",minArgs:1,maxArgs:1}],[Rt.CEIL,{value:Rt.CEIL,name:"Ceiling",description:"Returns the smallest (closest to negative infinity) of an argument",minArgs:1,maxArgs:1}],[Rt.FLOOR,{value:Rt.FLOOR,name:"Floor",description:"Returns the largest (closest to positive infinity) of an argument",minArgs:1,maxArgs:1}],[Rt.FLOOR_DIV,{value:Rt.FLOOR_DIV,name:"Floor division",description:"Returns the largest (closest to positive infinity) of the arguments",minArgs:2,maxArgs:2}],[Rt.FLOOR_MOD,{value:Rt.FLOOR_MOD,name:"Floor modulus",description:"Returns the floor modulus of the arguments",minArgs:2,maxArgs:2}],[Rt.ABS,{value:Rt.ABS,name:"Absolute",description:"Returns the absolute value of an argument",minArgs:1,maxArgs:1}],[Rt.MIN,{value:Rt.MIN,name:"Min",description:"Returns the smaller of the arguments",minArgs:2,maxArgs:2}],[Rt.MAX,{value:Rt.MAX,name:"Max",description:"Returns the greater of the arguments",minArgs:2,maxArgs:2}],[Rt.POW,{value:Rt.POW,name:"Raise to a power",description:"Returns the value of the first argument raised to the power of the second argument",minArgs:2,maxArgs:2}],[Rt.SIGNUM,{value:Rt.SIGNUM,name:"Sign of a real number",description:"Returns the signum function of the argument",minArgs:1,maxArgs:1}],[Rt.RAD,{value:Rt.RAD,name:"Radian",description:"Converts an angle measured in degrees to an approximately equivalent angle measured in radians",minArgs:1,maxArgs:1}],[Rt.DEG,{value:Rt.DEG,name:"Degrees",description:"Converts an angle measured in radians to an approximately equivalent angle measured in degrees.",minArgs:1,maxArgs:1}]]);var Bt,Kt,Ut;!function(e){e.MESSAGE_BODY="MESSAGE_BODY",e.MESSAGE_METADATA="MESSAGE_METADATA",e.ATTRIBUTE="ATTRIBUTE",e.TIME_SERIES="TIME_SERIES",e.CONSTANT="CONSTANT"}(Bt||(Bt={})),function(e){e.MESSAGE_BODY="MESSAGE_BODY",e.MESSAGE_METADATA="MESSAGE_METADATA",e.ATTRIBUTE="ATTRIBUTE",e.TIME_SERIES="TIME_SERIES"}(Kt||(Kt={})),function(e){e.DATA="DATA",e.METADATA="METADATA"}(Ut||(Ut={}));const _t=new Map([[Ut.DATA,"tb.rulenode.message"],[Ut.METADATA,"tb.rulenode.metadata"]]),zt=new Map([[Bt.MESSAGE_BODY,{name:"tb.rulenode.message-body-type",description:"Fetch argument value from incoming message"}],[Bt.MESSAGE_METADATA,{name:"tb.rulenode.message-metadata-type",description:"Fetch argument value from incoming message metadata"}],[Bt.ATTRIBUTE,{name:"tb.rulenode.attribute-type",description:"Fetch attribute value from database"}],[Bt.TIME_SERIES,{name:"tb.rulenode.time-series-type",description:"Fetch latest time-series value from database"}],[Bt.CONSTANT,{name:"tb.rulenode.constant-type",description:"Define constant value"}]]),Ht=new Map([[Kt.MESSAGE_BODY,{name:"tb.rulenode.message-body-type",description:"Add result to the outgoing message"}],[Kt.MESSAGE_METADATA,{name:"tb.rulenode.message-metadata-type",description:"Add result to the outgoing message metadata"}],[Kt.ATTRIBUTE,{name:"tb.rulenode.attribute-type",description:"Store result as an entity attribute in the database"}],[Kt.TIME_SERIES,{name:"tb.rulenode.time-series-type",description:"Store result as an entity time-series in the database"}]]),jt=["x","y","z","a","b","c","d","k","l","m","n","o","p","r","s","t"];var Qt,$t;!function(e){e.SHARED_SCOPE="SHARED_SCOPE",e.SERVER_SCOPE="SERVER_SCOPE",e.CLIENT_SCOPE="CLIENT_SCOPE"}(Qt||(Qt={})),function(e){e.SHARED_SCOPE="SHARED_SCOPE",e.SERVER_SCOPE="SERVER_SCOPE"}($t||($t={}));const Jt=new Map([[Qt.SHARED_SCOPE,"tb.rulenode.shared-scope"],[Qt.SERVER_SCOPE,"tb.rulenode.server-scope"],[Qt.CLIENT_SCOPE,"tb.rulenode.client-scope"]]);class Yt extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.perimeterType=ct,this.perimeterTypes=Object.keys(ct),this.perimeterTypeTranslationMap=ft,this.rangeUnits=Object.keys(xt),this.rangeUnitTranslationMap=bt,this.timeUnits=Object.keys(gt),this.timeUnitsTranslationMap=yt}configForm(){return this.geoActionConfigForm}onConfigurationSet(e){this.geoActionConfigForm=this.fb.group({latitudeKeyName:[e?e.latitudeKeyName:null,[D.required]],longitudeKeyName:[e?e.longitudeKeyName:null,[D.required]],perimeterType:[e?e.perimeterType:null,[D.required]],fetchPerimeterInfoFromMessageMetadata:[!!e&&e.fetchPerimeterInfoFromMessageMetadata,[]],perimeterKeyName:[e?e.perimeterKeyName:null,[]],centerLatitude:[e?e.centerLatitude:null,[]],centerLongitude:[e?e.centerLatitude:null,[]],range:[e?e.range:null,[]],rangeUnit:[e?e.rangeUnit:null,[]],polygonsDefinition:[e?e.polygonsDefinition:null,[]],minInsideDuration:[e?e.minInsideDuration:null,[D.required,D.min(1),D.max(2147483647)]],minInsideDurationTimeUnit:[e?e.minInsideDurationTimeUnit:null,[D.required]],minOutsideDuration:[e?e.minOutsideDuration:null,[D.required,D.min(1),D.max(2147483647)]],minOutsideDurationTimeUnit:[e?e.minOutsideDurationTimeUnit:null,[D.required]]})}validatorTriggers(){return["fetchPerimeterInfoFromMessageMetadata","perimeterType"]}updateValidators(e){const t=this.geoActionConfigForm.get("fetchPerimeterInfoFromMessageMetadata").value,n=this.geoActionConfigForm.get("perimeterType").value;t?this.geoActionConfigForm.get("perimeterKeyName").setValidators([D.required]):this.geoActionConfigForm.get("perimeterKeyName").setValidators([]),t||n!==ct.CIRCLE?(this.geoActionConfigForm.get("centerLatitude").setValidators([]),this.geoActionConfigForm.get("centerLongitude").setValidators([]),this.geoActionConfigForm.get("range").setValidators([]),this.geoActionConfigForm.get("rangeUnit").setValidators([])):(this.geoActionConfigForm.get("centerLatitude").setValidators([D.required,D.min(-90),D.max(90)]),this.geoActionConfigForm.get("centerLongitude").setValidators([D.required,D.min(-180),D.max(180)]),this.geoActionConfigForm.get("range").setValidators([D.required,D.min(0)]),this.geoActionConfigForm.get("rangeUnit").setValidators([D.required])),t||n!==ct.POLYGON?this.geoActionConfigForm.get("polygonsDefinition").setValidators([]):this.geoActionConfigForm.get("polygonsDefinition").setValidators([D.required]),this.geoActionConfigForm.get("perimeterKeyName").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("centerLatitude").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("centerLongitude").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("range").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("rangeUnit").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("polygonsDefinition").updateValueAndValidity({emitEvent:e})}}e("GpsGeoActionConfigComponent",Yt),Yt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Yt,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Yt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Yt,selector:"tb-action-node-gps-geofencing-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.latitude-key-name\n \n \n {{ \'tb.rulenode.latitude-key-name-required\' | translate }}\n \n \n \n tb.rulenode.longitude-key-name\n \n \n {{ \'tb.rulenode.longitude-key-name-required\' | translate }}\n \n \n \n tb.rulenode.perimeter-type\n \n \n {{ perimeterTypeTranslationMap.get(type) | translate }}\n \n \n \n \n {{ \'tb.rulenode.fetch-perimeter-info-from-metadata\' | translate }}\n \n \n tb.rulenode.perimeter-key-name\n \n \n {{ \'tb.rulenode.perimeter-key-name-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.circle-center-latitude\n \n \n {{ \'tb.rulenode.circle-center-latitude-required\' | translate }}\n \n \n \n tb.rulenode.circle-center-longitude\n \n \n {{ \'tb.rulenode.circle-center-longitude-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.range\n \n \n {{ \'tb.rulenode.range-required\' | translate }}\n \n \n \n tb.rulenode.range-units\n \n \n {{ rangeUnitTranslationMap.get(type) | translate }}\n \n \n \n
\n
\n
\n \n tb.rulenode.polygon-definition\n \n \n {{ \'tb.rulenode.polygon-definition-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.min-inside-duration\n \n \n {{ \'tb.rulenode.min-inside-duration-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n tb.rulenode.min-inside-duration-time-unit\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n \n tb.rulenode.min-outside-duration\n \n \n {{ \'tb.rulenode.min-outside-duration-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n tb.rulenode.min-outside-duration-time-unit\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:U.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Yt,decorators:[{type:n,args:[{selector:"tb-action-node-gps-geofencing-config",template:'
\n \n tb.rulenode.latitude-key-name\n \n \n {{ \'tb.rulenode.latitude-key-name-required\' | translate }}\n \n \n \n tb.rulenode.longitude-key-name\n \n \n {{ \'tb.rulenode.longitude-key-name-required\' | translate }}\n \n \n \n tb.rulenode.perimeter-type\n \n \n {{ perimeterTypeTranslationMap.get(type) | translate }}\n \n \n \n \n {{ \'tb.rulenode.fetch-perimeter-info-from-metadata\' | translate }}\n \n \n tb.rulenode.perimeter-key-name\n \n \n {{ \'tb.rulenode.perimeter-key-name-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.circle-center-latitude\n \n \n {{ \'tb.rulenode.circle-center-latitude-required\' | translate }}\n \n \n \n tb.rulenode.circle-center-longitude\n \n \n {{ \'tb.rulenode.circle-center-longitude-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.range\n \n \n {{ \'tb.rulenode.range-required\' | translate }}\n \n \n \n tb.rulenode.range-units\n \n \n {{ rangeUnitTranslationMap.get(type) | translate }}\n \n \n \n
\n
\n
\n \n tb.rulenode.polygon-definition\n \n \n {{ \'tb.rulenode.polygon-definition-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.min-inside-duration\n \n \n {{ \'tb.rulenode.min-inside-duration-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n tb.rulenode.min-inside-duration-time-unit\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n \n tb.rulenode.min-outside-duration\n \n \n {{ \'tb.rulenode.min-outside-duration-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n tb.rulenode.min-outside-duration-time-unit\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class Wt extends s{constructor(e,t,n,o){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=n,this.translate=o,this.tbelEnabled=X(this.store).tbelEnabled,this.scriptLanguage=d,this.changeScript=new r,this.hasScript=!0,this.testScriptLabel="tb.rulenode.test-to-string-function"}configForm(){return this.logConfigForm}onConfigurationSet(e){this.logConfigForm=this.fb.group({scriptLang:[e?e.scriptLang:d.JS,[D.required]],jsScript:[e?e.jsScript:null,[]],tbelScript:[e?e.tbelScript:null,[]]})}validatorTriggers(){return["scriptLang"]}updateValidators(e){let t=this.logConfigForm.get("scriptLang").value;t!==d.TBEL||this.tbelEnabled||(t=d.JS,this.logConfigForm.get("scriptLang").patchValue(t,{emitEvent:!1}),setTimeout((()=>{this.logConfigForm.updateValueAndValidity({emitEvent:!0})}))),this.logConfigForm.get("jsScript").setValidators(t===d.JS?[D.required]:[]),this.logConfigForm.get("jsScript").updateValueAndValidity({emitEvent:e}),this.logConfigForm.get("tbelScript").setValidators(t===d.TBEL?[D.required]:[]),this.logConfigForm.get("tbelScript").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=d.JS)),e}testScript(e){const t=this.logConfigForm.get("scriptLang").value,n=t===d.JS?"jsScript":"tbelScript",r=t===d.JS?"rulenode/log_node_script_fn":"rulenode/tbel/log_node_script_fn",o=this.logConfigForm.get(n).value;this.nodeScriptTestService.testNodeScript(o,"string",this.translate.instant("tb.rulenode.to-string"),"ToString",["msg","metadata","msgType"],this.ruleNodeId,r,t,e).subscribe((e=>{e&&(this.logConfigForm.get(n).setValue(e),this.changeScript.emit())}))}onValidate(){this.logConfigForm.get("scriptLang").value===d.JS&&this.jsFuncComponent.validateOnSubmit()}}e("LogConfigComponent",Wt),Wt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Wt,deps:[{token:G.Store},{token:E.UntypedFormBuilder},{token:Z.NodeScriptTestService},{token:j.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Wt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Wt,selector:"tb-action-node-log-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0},{propertyName:"tbelFuncComponent",first:!0,predicate:["tbelFuncComponent"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n \n \n \n
\n \n
\n
\n',dependencies:[{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ae.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","scriptLanguage","noValidate","required"]},{kind:"component",type:ie.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ie.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:se.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:me.TbScriptLangComponent,selector:"tb-script-lang",inputs:["disabled"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Wt,decorators:[{type:n,args:[{selector:"tb-action-node-log-config",template:'
\n \n \n \n \n \n \n \n
\n \n
\n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder},{type:Z.NodeScriptTestService},{type:j.TranslateService}]},propDecorators:{jsFuncComponent:[{type:o,args:["jsFuncComponent",{static:!1}]}],tbelFuncComponent:[{type:o,args:["tbelFuncComponent",{static:!1}]}]}});class Xt extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.msgCountConfigForm}onConfigurationSet(e){this.msgCountConfigForm=this.fb.group({interval:[e?e.interval:null,[D.required,D.min(1)]],telemetryPrefix:[e?e.telemetryPrefix:null,[D.required]]})}}e("MsgCountConfigComponent",Xt),Xt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Xt,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Xt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Xt,selector:"tb-action-node-msg-count-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.interval-seconds\n \n \n {{ \'tb.rulenode.interval-seconds-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-interval-seconds-message\' | translate }}\n \n \n \n tb.rulenode.output-timeseries-key-prefix\n \n \n {{ \'tb.rulenode.output-timeseries-key-prefix-required\' | translate }}\n \n \n
\n',dependencies:[{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Xt,decorators:[{type:n,args:[{selector:"tb-action-node-msg-count-config",template:'
\n \n tb.rulenode.interval-seconds\n \n \n {{ \'tb.rulenode.interval-seconds-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-interval-seconds-message\' | translate }}\n \n \n \n tb.rulenode.output-timeseries-key-prefix\n \n \n {{ \'tb.rulenode.output-timeseries-key-prefix-required\' | translate }}\n \n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class Zt extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.msgDelayConfigForm}onConfigurationSet(e){this.msgDelayConfigForm=this.fb.group({useMetadataPeriodInSecondsPatterns:[!!e&&e.useMetadataPeriodInSecondsPatterns,[]],periodInSeconds:[e?e.periodInSeconds:null,[]],periodInSecondsPattern:[e?e.periodInSecondsPattern:null,[]],maxPendingMsgs:[e?e.maxPendingMsgs:null,[D.required,D.min(1),D.max(1e5)]]})}validatorTriggers(){return["useMetadataPeriodInSecondsPatterns"]}updateValidators(e){this.msgDelayConfigForm.get("useMetadataPeriodInSecondsPatterns").value?(this.msgDelayConfigForm.get("periodInSecondsPattern").setValidators([D.required]),this.msgDelayConfigForm.get("periodInSeconds").setValidators([])):(this.msgDelayConfigForm.get("periodInSecondsPattern").setValidators([]),this.msgDelayConfigForm.get("periodInSeconds").setValidators([D.required,D.min(0)])),this.msgDelayConfigForm.get("periodInSecondsPattern").updateValueAndValidity({emitEvent:e}),this.msgDelayConfigForm.get("periodInSeconds").updateValueAndValidity({emitEvent:e})}}e("MsgDelayConfigComponent",Zt),Zt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Zt,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Zt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Zt,selector:"tb-action-node-msg-delay-config",usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.use-metadata-period-in-seconds-patterns\' | translate }}\n \n
tb.rulenode.use-metadata-period-in-seconds-patterns-hint
\n \n tb.rulenode.period-seconds\n \n \n {{ \'tb.rulenode.period-seconds-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-period-0-seconds-message\' | translate }}\n \n \n \n \n tb.rulenode.period-in-seconds-pattern\n \n \n {{ \'tb.rulenode.period-in-seconds-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n \n tb.rulenode.max-pending-messages\n \n \n {{ \'tb.rulenode.max-pending-messages-required\' | translate }}\n \n \n {{ \'tb.rulenode.max-pending-messages-range\' | translate }}\n \n \n {{ \'tb.rulenode.max-pending-messages-range\' | translate }}\n \n \n
\n',dependencies:[{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:U.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Zt,decorators:[{type:n,args:[{selector:"tb-action-node-msg-delay-config",template:'
\n \n {{ \'tb.rulenode.use-metadata-period-in-seconds-patterns\' | translate }}\n \n
tb.rulenode.use-metadata-period-in-seconds-patterns-hint
\n \n tb.rulenode.period-seconds\n \n \n {{ \'tb.rulenode.period-seconds-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-period-0-seconds-message\' | translate }}\n \n \n \n \n tb.rulenode.period-in-seconds-pattern\n \n \n {{ \'tb.rulenode.period-in-seconds-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n \n tb.rulenode.max-pending-messages\n \n \n {{ \'tb.rulenode.max-pending-messages-required\' | translate }}\n \n \n {{ \'tb.rulenode.max-pending-messages-range\' | translate }}\n \n \n {{ \'tb.rulenode.max-pending-messages-range\' | translate }}\n \n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class en extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.attributeScopes=Object.keys(m),this.telemetryTypeTranslationsMap=u}configForm(){return this.pushToCloudConfigForm}onConfigurationSet(e){this.pushToCloudConfigForm=this.fb.group({scope:[e?e.scope:null,[D.required]]})}}e("PushToCloudConfigComponent",en),en.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:en,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),en.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:en,selector:"tb-action-node-push-to-cloud-config",usesInheritance:!0,ngImport:t,template:'
\n \n attribute.attributes-scope\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n
\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:en,decorators:[{type:n,args:[{selector:"tb-action-node-push-to-cloud-config",template:'
\n \n attribute.attributes-scope\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class tn extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.attributeScopes=Object.keys(m),this.telemetryTypeTranslationsMap=u}configForm(){return this.pushToEdgeConfigForm}onConfigurationSet(e){this.pushToEdgeConfigForm=this.fb.group({scope:[e?e.scope:null,[D.required]]})}}e("PushToEdgeConfigComponent",tn),tn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:tn,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),tn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:tn,selector:"tb-action-node-push-to-edge-config",usesInheritance:!0,ngImport:t,template:'
\n \n attribute.attributes-scope\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n
\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:tn,decorators:[{type:n,args:[{selector:"tb-action-node-push-to-edge-config",template:'
\n \n attribute.attributes-scope\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class nn extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.rpcReplyConfigForm}onConfigurationSet(e){this.rpcReplyConfigForm=this.fb.group({requestIdMetaDataAttribute:[e?e.requestIdMetaDataAttribute:null,[]]})}}e("RpcReplyConfigComponent",nn),nn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:nn,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),nn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:nn,selector:"tb-action-node-rpc-reply-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.request-id-metadata-attribute\n \n \n
\n',dependencies:[{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:nn,decorators:[{type:n,args:[{selector:"tb-action-node-rpc-reply-config",template:'
\n \n tb.rulenode.request-id-metadata-attribute\n \n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class rn extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.rpcRequestConfigForm}onConfigurationSet(e){this.rpcRequestConfigForm=this.fb.group({timeoutInSeconds:[e?e.timeoutInSeconds:null,[D.required,D.min(0)]]})}}e("RpcRequestConfigComponent",rn),rn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:rn,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),rn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:rn,selector:"tb-action-node-rpc-request-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.timeout-sec\n \n \n {{ \'tb.rulenode.timeout-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-timeout-message\' | translate }}\n \n \n
\n',dependencies:[{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:rn,decorators:[{type:n,args:[{selector:"tb-action-node-rpc-request-config",template:'
\n \n tb.rulenode.timeout-sec\n \n \n {{ \'tb.rulenode.timeout-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-timeout-message\' | translate }}\n \n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class on extends h{get required(){return this.requiredValue}set required(e){this.requiredValue=xe(e)}constructor(e,t,n,r){super(e),this.store=e,this.translate=t,this.injector=n,this.fb=r,this.propagateChange=null,this.valueChangeSubscription=null}ngOnInit(){this.ngControl=this.injector.get(V),null!=this.ngControl&&(this.ngControl.valueAccessor=this),this.kvListFormGroup=this.fb.group({}),this.kvListFormGroup.addControl("keyVals",this.fb.array([]))}keyValsFormArray(){return this.kvListFormGroup.get("keyVals")}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.kvListFormGroup.disable({emitEvent:!1}):this.kvListFormGroup.enable({emitEvent:!1})}writeValue(e){this.valueChangeSubscription&&this.valueChangeSubscription.unsubscribe();const t=[];if(e)for(const n of Object.keys(e))Object.prototype.hasOwnProperty.call(e,n)&&t.push(this.fb.group({key:[n,[D.required]],value:[e[n],[D.required]]}));this.kvListFormGroup.setControl("keyVals",this.fb.array(t)),this.valueChangeSubscription=this.kvListFormGroup.valueChanges.subscribe((()=>{this.updateModel()}))}removeKeyVal(e){this.kvListFormGroup.get("keyVals").removeAt(e)}addKeyVal(){this.kvListFormGroup.get("keyVals").push(this.fb.group({key:["",[D.required]],value:["",[D.required]]}))}validate(e){const t=this.kvListFormGroup.get("keyVals").value;if(!t.length&&this.required)return{kvMapRequired:!0};if(!this.kvListFormGroup.valid)return{kvFieldsRequired:!0};if(this.uniqueKeyValuePairValidator)for(const e of t)if(e.key===e.value)return{uniqueKeyValuePair:!0};return null}updateModel(){const e=this.kvListFormGroup.get("keyVals").value;if(this.required&&!e.length||!this.kvListFormGroup.valid)this.propagateChange(null);else{const t={};e.forEach((e=>{t[e.key]=e.value})),this.propagateChange(t)}}}e("KvMapConfigOldComponent",on),on.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:on,deps:[{token:G.Store},{token:j.TranslateService},{token:t.Injector},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),on.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:on,selector:"tb-kv-map-config-old",inputs:{disabled:"disabled",uniqueKeyValuePairValidator:"uniqueKeyValuePairValidator",requiredText:"requiredText",keyText:"keyText",keyRequiredText:"keyRequiredText",valText:"valText",valRequiredText:"valRequiredText",hintText:"hintText",required:"required"},providers:[{provide:P,useExisting:a((()=>on)),multi:!0},{provide:w,useExisting:a((()=>on)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n
\n {{ keyText | translate }}\n {{ valText | translate }}\n \n
\n
\n
\n \n \n \n {{ keyRequiredText | translate }}\n \n \n \n \n \n {{ valRequiredText | translate }}\n \n \n \n
\n
\n
\n \n
\n \n
\n
\n',styles:[":host .tb-kv-map-config{margin-bottom:16px}:host .tb-kv-map-config .header{padding-left:5px;padding-right:5px;padding-bottom:5px}:host .tb-kv-map-config .header .cell{padding-left:5px;padding-right:5px;color:#757575;font-size:12px;font-weight:700;white-space:nowrap}:host .tb-kv-map-config .body{padding-left:5px;padding-right:5px;padding-bottom:0;max-height:300px;overflow:auto}:host .tb-kv-map-config .body .cell{padding-left:5px;padding-right:5px}:host .tb-kv-map-config tb-error{display:block;margin-top:-12px}\n"],dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:be.TbErrorComponent,selector:"tb-error",inputs:["noMargin","error"]},{kind:"component",type:ie.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ie.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:se.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:H.DefaultLayoutAlignDirective,selector:" [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg]",inputs:["fxLayoutAlign","fxLayoutAlign.xs","fxLayoutAlign.sm","fxLayoutAlign.md","fxLayoutAlign.lg","fxLayoutAlign.xl","fxLayoutAlign.lt-sm","fxLayoutAlign.lt-md","fxLayoutAlign.lt-lg","fxLayoutAlign.lt-xl","fxLayoutAlign.gt-xs","fxLayoutAlign.gt-sm","fxLayoutAlign.gt-md","fxLayoutAlign.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:he.DefaultShowHideDirective,selector:" [fxShow], [fxShow.print], [fxShow.xs], [fxShow.sm], [fxShow.md], [fxShow.lg], [fxShow.xl], [fxShow.lt-sm], [fxShow.lt-md], [fxShow.lt-lg], [fxShow.lt-xl], [fxShow.gt-xs], [fxShow.gt-sm], [fxShow.gt-md], [fxShow.gt-lg], [fxHide], [fxHide.print], [fxHide.xs], [fxHide.sm], [fxHide.md], [fxHide.lg], [fxHide.xl], [fxHide.lt-sm], [fxHide.lt-md], [fxHide.lt-lg], [fxHide.lt-xl], [fxHide.gt-xs], [fxHide.gt-sm], [fxHide.gt-md], [fxHide.gt-lg]",inputs:["fxShow","fxShow.print","fxShow.xs","fxShow.sm","fxShow.md","fxShow.lg","fxShow.xl","fxShow.lt-sm","fxShow.lt-md","fxShow.lt-lg","fxShow.lt-xl","fxShow.gt-xs","fxShow.gt-sm","fxShow.gt-md","fxShow.gt-lg","fxHide","fxHide.print","fxHide.xs","fxHide.sm","fxHide.md","fxHide.lg","fxHide.xl","fxHide.lt-sm","fxHide.lt-md","fxHide.lt-lg","fxHide.lt-xl","fxHide.gt-xs","fxHide.gt-sm","fxHide.gt-md","fxHide.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormControlDirective,selector:"[formControl]",inputs:["formControl","disabled","ngModel"],outputs:["ngModelChange"],exportAs:["ngForm"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormArrayName,selector:"[formArrayName]",inputs:["formArrayName"]},{kind:"pipe",type:B.AsyncPipe,name:"async"},{kind:"pipe",type:fe.SafePipe,name:"safe"},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:on,decorators:[{type:n,args:[{selector:"tb-kv-map-config-old",providers:[{provide:P,useExisting:a((()=>on)),multi:!0},{provide:w,useExisting:a((()=>on)),multi:!0}],template:'
\n
\n {{ keyText | translate }}\n {{ valText | translate }}\n \n
\n
\n
\n \n \n \n {{ keyRequiredText | translate }}\n \n \n \n \n \n {{ valRequiredText | translate }}\n \n \n \n
\n
\n
\n \n
\n \n
\n
\n',styles:[":host .tb-kv-map-config{margin-bottom:16px}:host .tb-kv-map-config .header{padding-left:5px;padding-right:5px;padding-bottom:5px}:host .tb-kv-map-config .header .cell{padding-left:5px;padding-right:5px;color:#757575;font-size:12px;font-weight:700;white-space:nowrap}:host .tb-kv-map-config .body{padding-left:5px;padding-right:5px;padding-bottom:0;max-height:300px;overflow:auto}:host .tb-kv-map-config .body .cell{padding-left:5px;padding-right:5px}:host .tb-kv-map-config tb-error{display:block;margin-top:-12px}\n"]}]}],ctorParameters:function(){return[{type:G.Store},{type:j.TranslateService},{type:t.Injector},{type:E.FormBuilder}]},propDecorators:{disabled:[{type:i}],uniqueKeyValuePairValidator:[{type:i}],requiredText:[{type:i}],keyText:[{type:i}],keyRequiredText:[{type:i}],valText:[{type:i}],valRequiredText:[{type:i}],hintText:[{type:i}],required:[{type:i}]}});class an extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.saveToCustomTableConfigForm}onConfigurationSet(e){this.saveToCustomTableConfigForm=this.fb.group({tableName:[e?e.tableName:null,[D.required,D.pattern(/.*\S.*/)]],fieldsMapping:[e?e.fieldsMapping:null,[D.required]]})}prepareOutputConfig(e){return e.tableName=e.tableName.trim(),e}}e("SaveToCustomTableConfigComponent",an),an.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:an,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),an.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:an,selector:"tb-action-node-custom-table-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.custom-table-name\n \n \n {{ \'tb.rulenode.custom-table-name-required\' | translate }}\n \n tb.rulenode.custom-table-hint\n \n \n \n \n
\n',dependencies:[{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:on,selector:"tb-kv-map-config-old",inputs:["disabled","uniqueKeyValuePairValidator","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:an,decorators:[{type:n,args:[{selector:"tb-action-node-custom-table-config",template:'
\n \n tb.rulenode.custom-table-name\n \n \n {{ \'tb.rulenode.custom-table-name-required\' | translate }}\n \n tb.rulenode.custom-table-hint\n \n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class ln extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.timeseriesConfigForm}onConfigurationSet(e){this.timeseriesConfigForm=this.fb.group({defaultTTL:[e?e.defaultTTL:null,[D.required,D.min(0)]],skipLatestPersistence:[!!e&&e.skipLatestPersistence,[]],useServerTs:[!!e&&e.useServerTs,[]]})}}e("TimeseriesConfigComponent",ln),ln.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ln,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),ln.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:ln,selector:"tb-action-node-timeseries-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.default-ttl\n \n \n {{ \'tb.rulenode.default-ttl-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-default-ttl-message\' | translate }}\n \n \n \n {{ \'tb.rulenode.skip-latest-persistence\' | translate }}\n \n \n {{ \'tb.rulenode.use-server-ts\' | translate }}\n \n
tb.rulenode.use-server-ts-hint
\n
\n',dependencies:[{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:U.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ln,decorators:[{type:n,args:[{selector:"tb-action-node-timeseries-config",template:'
\n \n tb.rulenode.default-ttl\n \n \n {{ \'tb.rulenode.default-ttl-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-default-ttl-message\' | translate }}\n \n \n \n {{ \'tb.rulenode.skip-latest-persistence\' | translate }}\n \n \n {{ \'tb.rulenode.use-server-ts\' | translate }}\n \n
tb.rulenode.use-server-ts-hint
\n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class sn extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.unassignCustomerConfigForm}onConfigurationSet(e){this.unassignCustomerConfigForm=this.fb.group({customerNamePattern:[e?e.customerNamePattern:null,[D.required,D.pattern(/.*\S.*/)]],customerCacheExpiration:[e?e.customerCacheExpiration:null,[D.required,D.min(0)]]})}prepareOutputConfig(e){return e.customerNamePattern=e.customerNamePattern.trim(),e}}e("UnassignCustomerConfigComponent",sn),sn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:sn,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),sn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:sn,selector:"tb-action-node-un-assign-to-customer-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.customer-name-pattern\n \n \n {{ \'tb.rulenode.customer-name-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.customer-cache-expiration\n \n \n {{ \'tb.rulenode.customer-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.customer-cache-expiration-range\' | translate }}\n \n tb.rulenode.customer-cache-expiration-hint\n \n
\n',dependencies:[{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:sn,decorators:[{type:n,args:[{selector:"tb-action-node-un-assign-to-customer-config",template:'
\n \n tb.rulenode.customer-name-pattern\n \n \n {{ \'tb.rulenode.customer-name-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.customer-cache-expiration\n \n \n {{ \'tb.rulenode.customer-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.customer-cache-expiration-range\' | translate }}\n \n tb.rulenode.customer-cache-expiration-hint\n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class mn extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.attributeScopeMap=m,this.attributeScopes=Object.keys(m),this.telemetryTypeTranslationsMap=u,this.separatorKeysCodes=[ue,pe,de]}configForm(){return this.deleteAttributesConfigForm}onConfigurationSet(e){this.deleteAttributesConfigForm=this.fb.group({scope:[e?e.scope:null,[D.required]],keys:[e?e.keys:null,[D.required]],sendAttributesDeletedNotification:[!!e&&e.sendAttributesDeletedNotification,[]],notifyDevice:[!!e&&e.notifyDevice,[]]}),this.deleteAttributesConfigForm.get("scope").valueChanges.subscribe((e=>{e!==m.SHARED_SCOPE&&this.deleteAttributesConfigForm.get("notifyDevice").patchValue(!1,{emitEvent:!1})}))}removeKey(e){const t=this.deleteAttributesConfigForm.get("keys").value,n=t.indexOf(e);n>=0&&(t.splice(n,1),this.deleteAttributesConfigForm.get("keys").patchValue(t,{emitEvent:!0}))}addKey(e){const t=e.input;let n=e.value;if((n||"").trim()){n=n.trim();let e=this.deleteAttributesConfigForm.get("keys").value;e&&-1!==e.indexOf(n)||(e||(e=[]),e.push(n),this.deleteAttributesConfigForm.get("keys").patchValue(e,{emitEvent:!0}))}t&&(t.value="")}}e("DeleteAttributesConfigComponent",mn),mn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:mn,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),mn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:mn,selector:"tb-action-node-delete-attributes-config",viewQueries:[{propertyName:"attributeChipList",first:!0,predicate:["attributeChipList"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'attribute.attributes-scope\' | translate }}\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n \n \n \n {{key}}\n close\n \n \n \n {{ \'tb.rulenode.attributes-keys-required\' | translate }}\n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.send-attributes-deleted-notification\' | translate }}\n \n
tb.rulenode.send-attributes-deleted-notification-hint
\n
\n \n {{ \'tb.rulenode.notify-device\' | translate }}\n \n
tb.rulenode.notify-device-delete-hint
\n
\n
\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:U.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:ce.MatChipGrid,selector:"mat-chip-grid",inputs:["tabIndex","disabled","placeholder","required","value","errorStateMatcher"],outputs:["change","valueChange"]},{kind:"directive",type:ce.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputFor","matChipInputAddOnBlur","matChipInputSeparatorKeyCodes","placeholder","id","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]},{kind:"directive",type:ce.MatChipRemove,selector:"[matChipRemove]"},{kind:"component",type:ce.MatChipRow,selector:"mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]",inputs:["color","disabled","disableRipple","tabIndex","editable"],outputs:["edited"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:mn,decorators:[{type:n,args:[{selector:"tb-action-node-delete-attributes-config",template:'
\n \n {{ \'attribute.attributes-scope\' | translate }}\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n \n \n \n {{key}}\n close\n \n \n \n {{ \'tb.rulenode.attributes-keys-required\' | translate }}\n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.send-attributes-deleted-notification\' | translate }}\n \n
tb.rulenode.send-attributes-deleted-notification-hint
\n
\n \n {{ \'tb.rulenode.notify-device\' | translate }}\n \n
tb.rulenode.notify-device-delete-hint
\n
\n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]},propDecorators:{attributeChipList:[{type:o,args:["attributeChipList"]}]}});class un extends h{get function(){return this.functionValue}set function(e){e&&this.functionValue!==e&&(this.functionValue=e,this.setupArgumentsFormGroup(!0))}constructor(e,t,n,r){super(e),this.store=e,this.translate=t,this.injector=n,this.fb=r,this.maxArgs=16,this.minArgs=1,this.displayArgumentName=!1,this.mathFunctionMap=Ot,this.ArgumentType=Bt,this.attributeScopeMap=Jt,this.argumentTypeMap=zt,this.arguments=Object.values(Bt),this.attributeScope=Object.values(Qt),this.propagateChange=null,this.valueChangeSubscription=[]}ngOnInit(){this.ngControl=this.injector.get(V),null!=this.ngControl&&(this.ngControl.valueAccessor=this),this.argumentsFormGroup=this.fb.group({arguments:this.fb.array([])}),this.valueChangeSubscription.push(this.argumentsFormGroup.valueChanges.subscribe((()=>{this.updateModel()}))),this.setupArgumentsFormGroup()}onDrop(e){const t=this.argumentsFormArray(),n=t.at(e.previousIndex);t.removeAt(e.previousIndex),t.insert(e.currentIndex,n),this.updateArgumentNames()}argumentsFormArray(){return this.argumentsFormGroup.get("arguments")}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.argumentsFormGroup.disable({emitEvent:!1}):(this.argumentsFormGroup.enable({emitEvent:!1}),this.argumentsFormGroup.get("arguments").controls.forEach((e=>this.updateArgumentControlValidators(e))))}ngOnDestroy(){this.valueChangeSubscription.length&&this.valueChangeSubscription.forEach((e=>e.unsubscribe()))}writeValue(e){const t=[];e&&e.forEach(((e,n)=>{t.push(this.createArgumentControl(e,n))})),this.argumentsFormGroup.setControl("arguments",this.fb.array(t),{emitEvent:!1}),this.setupArgumentsFormGroup()}removeArgument(e){this.argumentsFormGroup.get("arguments").removeAt(e),this.updateArgumentNames()}addArgument(e=!0){const t=this.argumentsFormGroup.get("arguments"),n=this.createArgumentControl(null,t.length);t.push(n,{emitEvent:e})}validate(e){return this.argumentsFormGroup.valid?null:{argumentsRequired:!0}}setupArgumentsFormGroup(e=!1){if(this.function&&(this.maxArgs=this.mathFunctionMap.get(this.function).maxArgs,this.minArgs=this.mathFunctionMap.get(this.function).minArgs,this.displayArgumentName=this.function===Rt.CUSTOM),this.argumentsFormGroup){for(this.argumentsFormGroup.get("arguments").setValidators([D.minLength(this.minArgs),D.maxLength(this.maxArgs)]),this.argumentsFormGroup.get("arguments").value.length>this.maxArgs&&(this.argumentsFormGroup.get("arguments").controls.length=this.maxArgs);this.argumentsFormGroup.get("arguments").value.length{this.updateArgumentControlValidators(n),n.get("attributeScope").updateValueAndValidity({emitEvent:!1}),n.get("defaultValue").updateValueAndValidity({emitEvent:!1})}))),n}updateArgumentControlValidators(e){const t=e.get("type").value;t===Bt.ATTRIBUTE?e.get("attributeScope").enable({emitEvent:!1}):e.get("attributeScope").disable({emitEvent:!1}),t&&t!==Bt.CONSTANT?e.get("defaultValue").enable({emitEvent:!1}):e.get("defaultValue").disable({emitEvent:!1})}updateArgumentNames(){this.argumentsFormGroup.get("arguments").controls.forEach(((e,t)=>{e.get("name").setValue(jt[t])}))}updateModel(){const e=this.argumentsFormGroup.get("arguments").value;e.length&&this.argumentsFormGroup.valid?this.propagateChange(e):this.propagateChange(null)}}e("ArgumentsMapConfigComponent",un),un.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:un,deps:[{token:G.Store},{token:j.TranslateService},{token:t.Injector},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),un.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:un,selector:"tb-arguments-map-config",inputs:{disabled:"disabled",function:"function"},providers:[{provide:P,useExisting:a((()=>un)),multi:!0},{provide:w,useExisting:a((()=>un)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n\n
\n \n \n
\n \n
\n {{argumentControl.get(\'name\').value}}.\n
\n \n tb.rulenode.argument-source-field-input\n \n \n {{ argumentTypeMap.get(argumentControl.get(\'type\').value)?.name | translate }}\n \n \n {{ argumentTypeMap.get(argument).name | translate }}\n \n {{ argumentTypeMap.get(argument).description }}\n \n \n \n \n tb.rulenode.argument-source-field-input-required\n \n \n
\n \n tb.rulenode.argument-key-field-input\n \n \n help\n \n \n tb.rulenode.argument-key-field-input-required\n \n \n \n tb.rulenode.constant-value-field-input\n \n \n tb.rulenode.constant-value-field-input-required\n \n \n \n tb.rulenode.default-value-field-input\n \n \n
\n \n tb.rulenode.attribute-scope-field-input\n \n \n {{ attributeScopeMap.get(scope) | translate }}\n \n \n \n tb.rulenode.attribute-scope-field-input-required\n \n \n
\n \n
\n
\n
\n
\n
\n
\n tb.rulenode.no-arguments-prompt\n
\n \n
\n',styles:[":host .mat-mdc-list-item.tb-argument{border:solid rgba(0,0,0,.25) 1px;border-radius:4px;padding:10px 0;margin-bottom:16px}:host .arguments-list{padding:0}\n"],dependencies:[{kind:"directive",type:B.NgClass,selector:"[ngClass]",inputs:["class","ngClass"]},{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ie.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ie.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:z.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"directive",type:Q.MatSelectTrigger,selector:"mat-select-trigger"},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:se.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"component",type:ve.MatList,selector:"mat-list",exportAs:["matList"]},{kind:"component",type:ve.MatListItem,selector:"mat-list-item, a[mat-list-item], button[mat-list-item]",inputs:["activated"],exportAs:["matListItem"]},{kind:"directive",type:Ce.CdkDropList,selector:"[cdkDropList], cdk-drop-list",inputs:["cdkDropListConnectedTo","cdkDropListData","cdkDropListOrientation","id","cdkDropListLockAxis","cdkDropListDisabled","cdkDropListSortingDisabled","cdkDropListEnterPredicate","cdkDropListSortPredicate","cdkDropListAutoScrollDisabled","cdkDropListAutoScrollStep"],outputs:["cdkDropListDropped","cdkDropListEntered","cdkDropListExited","cdkDropListSorted"],exportAs:["cdkDropList"]},{kind:"directive",type:Ce.CdkDrag,selector:"[cdkDrag]",inputs:["cdkDragData","cdkDragLockAxis","cdkDragRootElement","cdkDragBoundary","cdkDragStartDelay","cdkDragFreeDragPosition","cdkDragDisabled","cdkDragConstrainPosition","cdkDragPreviewClass","cdkDragPreviewContainer"],outputs:["cdkDragStarted","cdkDragReleased","cdkDragEnded","cdkDragEntered","cdkDragExited","cdkDragDropped","cdkDragMoved"],exportAs:["cdkDrag"]},{kind:"directive",type:Ce.CdkDragHandle,selector:"[cdkDragHandle]",inputs:["cdkDragHandleDisabled"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:H.DefaultLayoutAlignDirective,selector:" [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg]",inputs:["fxLayoutAlign","fxLayoutAlign.xs","fxLayoutAlign.sm","fxLayoutAlign.md","fxLayoutAlign.lg","fxLayoutAlign.xl","fxLayoutAlign.lt-sm","fxLayoutAlign.lt-md","fxLayoutAlign.lt-lg","fxLayoutAlign.lt-xl","fxLayoutAlign.gt-xs","fxLayoutAlign.gt-sm","fxLayoutAlign.gt-md","fxLayoutAlign.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:he.DefaultClassDirective,selector:" [ngClass], [ngClass.xs], [ngClass.sm], [ngClass.md], [ngClass.lg], [ngClass.xl], [ngClass.lt-sm], [ngClass.lt-md], [ngClass.lt-lg], [ngClass.lt-xl], [ngClass.gt-xs], [ngClass.gt-sm], [ngClass.gt-md], [ngClass.gt-lg]",inputs:["ngClass","ngClass.xs","ngClass.sm","ngClass.md","ngClass.lg","ngClass.xl","ngClass.lt-sm","ngClass.lt-md","ngClass.lt-lg","ngClass.lt-xl","ngClass.gt-xs","ngClass.gt-sm","ngClass.gt-md","ngClass.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.FormControlDirective,selector:"[formControl]",inputs:["formControl","disabled","ngModel"],outputs:["ngModelChange"],exportAs:["ngForm"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormArrayName,selector:"[formArrayName]",inputs:["formArrayName"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:un,decorators:[{type:n,args:[{selector:"tb-arguments-map-config",providers:[{provide:P,useExisting:a((()=>un)),multi:!0},{provide:w,useExisting:a((()=>un)),multi:!0}],template:'
\n\n
\n \n \n
\n \n
\n {{argumentControl.get(\'name\').value}}.\n
\n \n tb.rulenode.argument-source-field-input\n \n \n {{ argumentTypeMap.get(argumentControl.get(\'type\').value)?.name | translate }}\n \n \n {{ argumentTypeMap.get(argument).name | translate }}\n \n {{ argumentTypeMap.get(argument).description }}\n \n \n \n \n tb.rulenode.argument-source-field-input-required\n \n \n
\n \n tb.rulenode.argument-key-field-input\n \n \n help\n \n \n tb.rulenode.argument-key-field-input-required\n \n \n \n tb.rulenode.constant-value-field-input\n \n \n tb.rulenode.constant-value-field-input-required\n \n \n \n tb.rulenode.default-value-field-input\n \n \n
\n \n tb.rulenode.attribute-scope-field-input\n \n \n {{ attributeScopeMap.get(scope) | translate }}\n \n \n \n tb.rulenode.attribute-scope-field-input-required\n \n \n
\n \n
\n
\n
\n
\n
\n
\n tb.rulenode.no-arguments-prompt\n
\n \n
\n',styles:[":host .mat-mdc-list-item.tb-argument{border:solid rgba(0,0,0,.25) 1px;border-radius:4px;padding:10px 0;margin-bottom:16px}:host .arguments-list{padding:0}\n"]}]}],ctorParameters:function(){return[{type:G.Store},{type:j.TranslateService},{type:t.Injector},{type:E.FormBuilder}]},propDecorators:{disabled:[{type:i}],function:[{type:i}]}});class pn extends h{get required(){return this.requiredValue}set required(e){this.requiredValue=xe(e)}constructor(e,t,n,r){super(e),this.store=e,this.translate=t,this.injector=n,this.fb=r,this.searchText="",this.dirty=!1,this.mathOperation=[...Ot.values()],this.propagateChange=null}ngOnInit(){this.mathFunctionForm=this.fb.group({operation:[""]}),this.filteredOptions=this.mathFunctionForm.get("operation").valueChanges.pipe(Fe((e=>{let t;t="string"==typeof e&&Rt[e]?Rt[e]:null,this.updateView(t)})),ke((e=>(this.searchText=e||"",e?this._filter(e):this.mathOperation.slice()))))}_filter(e){const t=e.toLowerCase();return this.mathOperation.filter((e=>e.name.toLowerCase().includes(t)||e.value.toLowerCase().includes(t)))}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.mathFunctionForm.disable({emitEvent:!1}):this.mathFunctionForm.enable({emitEvent:!1})}mathFunctionDisplayFn(e){if(e){const t=Ot.get(e);return t.value+" | "+t.name}return""}writeValue(e){this.modelValue=e,this.mathFunctionForm.get("operation").setValue(e,{emitEvent:!1}),this.dirty=!0}updateView(e){this.modelValue!==e&&(this.modelValue=e,this.propagateChange(this.modelValue))}onFocus(){this.dirty&&(this.mathFunctionForm.get("operation").updateValueAndValidity({onlySelf:!0}),this.dirty=!1)}clear(){this.mathFunctionForm.get("operation").patchValue(""),setTimeout((()=>{this.operationInput.nativeElement.blur(),this.operationInput.nativeElement.focus()}),0)}}e("MathFunctionAutocompleteComponent",pn),pn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:pn,deps:[{token:G.Store},{token:j.TranslateService},{token:t.Injector},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),pn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:pn,selector:"tb-math-function-autocomplete",inputs:{required:"required",disabled:"disabled"},providers:[{provide:P,useExisting:a((()=>pn)),multi:!0}],viewQueries:[{propertyName:"operationInput",first:!0,predicate:["operationInput"],descendants:!0,static:!0}],usesInheritance:!0,ngImport:t,template:'\n tb.rulenode.functions-field-input\n \n \n \n \n \n \n {{ option.description }}\n \n \n \n tb.rulenode.no-option-found\n \n \n\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ie.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:Se.MatAutocomplete,selector:"mat-autocomplete",inputs:["disableRipple","hideSingleSelectionIndicator"],exportAs:["matAutocomplete"]},{kind:"directive",type:Se.MatAutocompleteTrigger,selector:"input[matAutocomplete], textarea[matAutocomplete]",exportAs:["matAutocompleteTrigger"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:B.AsyncPipe,name:"async"},{kind:"pipe",type:qe.HighlightPipe,name:"highlight"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:pn,decorators:[{type:n,args:[{selector:"tb-math-function-autocomplete",providers:[{provide:P,useExisting:a((()=>pn)),multi:!0}],template:'\n tb.rulenode.functions-field-input\n \n \n \n \n \n \n {{ option.description }}\n \n \n \n tb.rulenode.no-option-found\n \n \n\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:j.TranslateService},{type:t.Injector},{type:E.UntypedFormBuilder}]},propDecorators:{required:[{type:i}],disabled:[{type:i}],operationInput:[{type:o,args:["operationInput",{static:!0}]}]}});class dn extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.MathFunction=Rt,this.ArgumentTypeResult=Kt,this.argumentTypeResultMap=Ht,this.attributeScopeMap=Jt,this.argumentsResult=Object.values(Kt),this.attributeScopeResult=Object.values($t)}configForm(){return this.mathFunctionConfigForm}onConfigurationSet(e){this.mathFunctionConfigForm=this.fb.group({operation:[e?e.operation:null,[D.required]],arguments:[e?e.arguments:null,[D.required]],customFunction:[e?e.customFunction:"",[D.required]],result:this.fb.group({type:[e?e.result.type:null,[D.required]],attributeScope:[e?e.result.attributeScope:null,[D.required]],key:[e?e.result.key:"",[D.required]],resultValuePrecision:[e?e.result.resultValuePrecision:0],addToBody:[!!e&&e.result.addToBody],addToMetadata:[!!e&&e.result.addToMetadata]})})}updateValidators(e){const t=this.mathFunctionConfigForm.get("operation").value,n=this.mathFunctionConfigForm.get("result.type").value;t===Rt.CUSTOM?this.mathFunctionConfigForm.get("customFunction").enable({emitEvent:!1}):this.mathFunctionConfigForm.get("customFunction").disable({emitEvent:!1}),n===Kt.ATTRIBUTE?this.mathFunctionConfigForm.get("result.attributeScope").enable({emitEvent:!1}):this.mathFunctionConfigForm.get("result.attributeScope").disable({emitEvent:!1}),this.mathFunctionConfigForm.get("customFunction").updateValueAndValidity({emitEvent:e}),this.mathFunctionConfigForm.get("result.attributeScope").updateValueAndValidity({emitEvent:e})}validatorTriggers(){return["operation","result.type"]}}e("MathFunctionConfigComponent",dn),dn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:dn,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),dn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:dn,selector:"tb-action-node-math-function-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n
\n tb.rulenode.argument-tile\n \n \n
\n
\n {{\'tb.rulenode.custom-expression-field-input\' | translate }} *\n \n \n \n tb.rulenode.custom-expression-field-input-required\n \n tb.rulenode.custom-expression-field-input-hint\n \n
\n
\n tb.rulenode.result-title\n
\n \n tb.rulenode.type-field-input\n \n \n {{ argumentTypeResultMap.get(mathFunctionConfigForm.get(\'result.type\').value)?.name | translate }}\n \n \n {{ argumentTypeResultMap.get(argument).name | translate }}\n \n {{ argumentTypeResultMap.get(argument).description }}\n \n \n \n \n tb.rulenode.type-field-input-required\n \n \n
\n \n tb.rulenode.attribute-scope-field-input\n \n \n {{ attributeScopeMap.get(scope) | translate }}\n \n \n \n \n tb.rulenode.key-field-input\n \n help\n \n tb.rulenode.key-field-input-required\n \n \n
\n
\n \n tb.rulenode.number-floating-point-field-input\n \n \n \n
\n
\n \n {{\'tb.rulenode.add-to-message-field-input\' | translate }}\n \n \n {{\'tb.rulenode.add-to-metadata-field-input\' | translate}}\n \n
\n
\n
\n
\n',styles:[":host ::ng-deep .fields-group{padding:0 16px 8px;margin:10px 0;border:1px groove rgba(0,0,0,.25);border-radius:4px}:host ::ng-deep .fields-group .mat-mdc-form-field .mat-mdc-form-field-infix{width:100%}:host ::ng-deep .fields-group legend{color:#000000b3;width:-moz-fit-content;width:fit-content}:host ::ng-deep .fields-group legend+*{display:block}:host ::ng-deep .fields-group legend+*.no-margin-top{margin-top:0}\n"],dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:U.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:z.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"directive",type:Q.MatSelectTrigger,selector:"mat-select-trigger"},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:se.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:H.DefaultLayoutAlignDirective,selector:" [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg]",inputs:["fxLayoutAlign","fxLayoutAlign.xs","fxLayoutAlign.sm","fxLayoutAlign.md","fxLayoutAlign.lg","fxLayoutAlign.xl","fxLayoutAlign.lt-sm","fxLayoutAlign.lt-md","fxLayoutAlign.lt-lg","fxLayoutAlign.lt-xl","fxLayoutAlign.gt-xs","fxLayoutAlign.gt-sm","fxLayoutAlign.gt-md","fxLayoutAlign.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:E.FormGroupName,selector:"[formGroupName]",inputs:["formGroupName"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:un,selector:"tb-arguments-map-config",inputs:["disabled","function"]},{kind:"component",type:pn,selector:"tb-math-function-autocomplete",inputs:["required","disabled"]},{kind:"pipe",type:fe.SafePipe,name:"safe"},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:dn,decorators:[{type:n,args:[{selector:"tb-action-node-math-function-config",template:'
\n \n \n
\n tb.rulenode.argument-tile\n \n \n
\n
\n {{\'tb.rulenode.custom-expression-field-input\' | translate }} *\n \n \n \n tb.rulenode.custom-expression-field-input-required\n \n tb.rulenode.custom-expression-field-input-hint\n \n
\n
\n tb.rulenode.result-title\n
\n \n tb.rulenode.type-field-input\n \n \n {{ argumentTypeResultMap.get(mathFunctionConfigForm.get(\'result.type\').value)?.name | translate }}\n \n \n {{ argumentTypeResultMap.get(argument).name | translate }}\n \n {{ argumentTypeResultMap.get(argument).description }}\n \n \n \n \n tb.rulenode.type-field-input-required\n \n \n
\n \n tb.rulenode.attribute-scope-field-input\n \n \n {{ attributeScopeMap.get(scope) | translate }}\n \n \n \n \n tb.rulenode.key-field-input\n \n help\n \n tb.rulenode.key-field-input-required\n \n \n
\n
\n \n tb.rulenode.number-floating-point-field-input\n \n \n \n
\n
\n \n {{\'tb.rulenode.add-to-message-field-input\' | translate }}\n \n \n {{\'tb.rulenode.add-to-metadata-field-input\' | translate}}\n \n
\n
\n
\n
\n',styles:[":host ::ng-deep .fields-group{padding:0 16px 8px;margin:10px 0;border:1px groove rgba(0,0,0,.25);border-radius:4px}:host ::ng-deep .fields-group .mat-mdc-form-field .mat-mdc-form-field-infix{width:100%}:host ::ng-deep .fields-group legend{color:#000000b3;width:-moz-fit-content;width:fit-content}:host ::ng-deep .fields-group legend+*{display:block}:host ::ng-deep .fields-group legend+*.no-margin-top{margin-top:0}\n"]}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class cn extends h{set required(e){this.requiredValue!==e&&(this.requiredValue=e,this.updateValidators())}get required(){return this.requiredValue}constructor(e,t){super(e),this.store=e,this.fb=t,this.subscriptSizing="fixed",this.searchText="",this.dirty=!1,this.messageTypes=["POST_ATTRIBUTES_REQUEST","POST_TELEMETRY_REQUEST"],this.propagateChange=e=>{},this.messageTypeFormGroup=this.fb.group({messageType:[null,[D.maxLength(255)]]})}registerOnTouched(e){}registerOnChange(e){this.propagateChange=e}ngOnInit(){this.outputMessageTypes=this.messageTypeFormGroup.get("messageType").valueChanges.pipe(Fe((e=>{this.updateView(e)})),ke((e=>e||"")),Le((e=>this.fetchMessageTypes(e))))}writeValue(e){this.searchText="",this.modelValue=e,this.messageTypeFormGroup.get("messageType").patchValue(e,{emitEvent:!1}),this.dirty=!0}onFocus(){this.dirty&&(this.messageTypeFormGroup.get("messageType").updateValueAndValidity({onlySelf:!0,emitEvent:!0}),this.dirty=!1)}updateView(e){this.modelValue!==e&&(this.modelValue=e,this.propagateChange(this.modelValue))}displayMessageTypeFn(e){return e||void 0}fetchMessageTypes(e,t=!1){return this.searchText=e,Ge(this.messageTypes).pipe(ke((n=>n.filter((n=>t?!!e&&n===e:!e||n.toUpperCase().startsWith(e.toUpperCase()))))))}clear(){this.messageTypeFormGroup.get("messageType").patchValue(null,{emitEvent:!0}),setTimeout((()=>{this.messageTypeInput.nativeElement.blur(),this.messageTypeInput.nativeElement.focus()}),0)}setDisabledState(e){e?this.messageTypeFormGroup.disable({emitEvent:!1}):this.messageTypeFormGroup.enable({emitEvent:!1})}updateValidators(){this.messageTypeFormGroup.get("messageType").setValidators(this.required?[D.required,D.maxLength(255)]:[D.maxLength(255)]),this.messageTypeFormGroup.get("messageType").updateValueAndValidity({emitEvent:!1})}}e("OutputMessageTypeAutocompleteComponent",cn),cn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:cn,deps:[{token:G.Store},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),cn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:cn,selector:"tb-output-message-type-autocomplete",inputs:{autocompleteHint:"autocompleteHint",subscriptSizing:"subscriptSizing",required:"required"},providers:[{provide:P,useExisting:a((()=>cn)),multi:!0}],viewQueries:[{propertyName:"messageTypeInput",first:!0,predicate:["messageTypeInput"],descendants:!0,static:!0}],usesInheritance:!0,ngImport:t,template:'\n {{\'tb.rulenode.output-message-type\' | translate}}\n \n \n \n \n \n \n \n {{autocompleteHint | translate}}\n \n {{ \'tb.rulenode.output-message-type-required\' | translate }}\n \n \n {{ \'tb.rulenode.output-message-type-max-length\' | translate }}\n \n\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ie.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:z.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:Se.MatAutocomplete,selector:"mat-autocomplete",inputs:["disableRipple","hideSingleSelectionIndicator"],exportAs:["matAutocomplete"]},{kind:"directive",type:Se.MatAutocompleteTrigger,selector:"input[matAutocomplete], textarea[matAutocomplete]",exportAs:["matAutocompleteTrigger"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"pipe",type:B.AsyncPipe,name:"async"},{kind:"pipe",type:qe.HighlightPipe,name:"highlight"},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),Me([v()],cn.prototype,"required",null),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:cn,decorators:[{type:n,args:[{selector:"tb-output-message-type-autocomplete",providers:[{provide:P,useExisting:a((()=>cn)),multi:!0}],template:'\n {{\'tb.rulenode.output-message-type\' | translate}}\n \n \n \n \n \n \n \n {{autocompleteHint | translate}}\n \n {{ \'tb.rulenode.output-message-type-required\' | translate }}\n \n \n {{ \'tb.rulenode.output-message-type-max-length\' | translate }}\n \n\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.FormBuilder}]},propDecorators:{messageTypeInput:[{type:o,args:["messageTypeInput",{static:!0}]}],autocompleteHint:[{type:i}],subscriptSizing:[{type:i}],required:[{type:i}]}});class fn extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.serviceType=p.TB_RULE_ENGINE,this.deduplicationStrategie=Ct,this.deduplicationStrategies=Object.keys(this.deduplicationStrategie),this.deduplicationStrategiesTranslations=Ft}configForm(){return this.deduplicationConfigForm}onConfigurationSet(e){this.deduplicationConfigForm=this.fb.group({interval:[ee(e?.interval)?e.interval:null,[D.required,D.min(1)]],strategy:[ee(e?.strategy)?e.strategy:null,[D.required]],outMsgType:[ee(e?.outMsgType)?e.outMsgType:null,[D.required]],queueName:[ee(e?.queueName)?e.queueName:null,[D.required]],maxPendingMsgs:[ee(e?.maxPendingMsgs)?e.maxPendingMsgs:null,[D.required,D.min(1),D.max(1e3)]],maxRetries:[ee(e?.maxRetries)?e.maxRetries:null,[D.required,D.min(0),D.max(100)]]})}updateValidators(e){this.deduplicationConfigForm.get("strategy").value===this.deduplicationStrategie.ALL?(this.deduplicationConfigForm.get("outMsgType").enable({emitEvent:!1}),this.deduplicationConfigForm.get("queueName").enable({emitEvent:!1})):(this.deduplicationConfigForm.get("outMsgType").disable({emitEvent:!1}),this.deduplicationConfigForm.get("queueName").disable({emitEvent:!1})),this.deduplicationConfigForm.get("outMsgType").updateValueAndValidity({emitEvent:e}),this.deduplicationConfigForm.get("queueName").updateValueAndValidity({emitEvent:e})}validatorTriggers(){return["strategy"]}}e("DeduplicationConfigComponent",fn),fn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:fn,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),fn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:fn,selector:"tb-action-node-msg-deduplication-config",usesInheritance:!0,ngImport:t,template:"
\n \n {{'tb.rulenode.interval' | translate}}\n \n {{'tb.rulenode.interval-hint' | translate}}\n \n {{'tb.rulenode.interval-required' | translate}}\n \n \n {{'tb.rulenode.interval-min-error' | translate}}\n \n \n \n {{'tb.rulenode.strategy' | translate}}\n \n \n {{ deduplicationStrategiesTranslations.get(strategy) | translate }}\n \n \n \n {{'tb.rulenode.strategy-first-hint' | translate}}\n {{'tb.rulenode.strategy-last-hint' | translate}}\n \n {{'tb.rulenode.strategy-required' | translate}}\n \n \n
\n \n \n \n \n
\n \n \n \n
\n
Advanced settings
\n
\n
\n
\n \n \n {{'tb.rulenode.max-pending-msgs' | translate}}\n \n {{'tb.rulenode.max-pending-msgs-hint' | translate}}\n \n {{'tb.rulenode.max-pending-msgs-required' | translate}}\n \n \n {{'tb.rulenode.max-pending-msgs-max-error' | translate}}\n \n \n {{'tb.rulenode.max-pending-msgs-min-error' | translate}}\n \n \n \n {{'tb.rulenode.max-retries' | translate}}\n \n {{'tb.rulenode.max-retries-hint' | translate}}\n \n {{'tb.rulenode.max-retries-required' | translate}}\n \n \n {{'tb.rulenode.max-retries-max-error' | translate}}\n \n \n {{'tb.rulenode.max-retries-min-error' | translate}}\n \n \n \n
\n
\n",styles:[":host ::ng-deep .mat-expansion-panel.advanced-settings{border:none;box-shadow:none;padding:0}:host ::ng-deep .mat-expansion-panel.advanced-settings .mat-expansion-panel-body{padding:0}:host ::ng-deep .mat-expansion-panel:not(.mat-expanded) .mat-expansion-panel-header:not([aria-disabled=true]):hover{background:white}\n"],dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:W.QueueAutocompleteComponent,selector:"tb-queue-autocomplete",inputs:["labelText","requiredText","autocompleteHint","subscriptSizing","required","queueType","disabled"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:Ae.MatExpansionPanel,selector:"mat-expansion-panel",inputs:["disabled","expanded","hideToggle","togglePosition"],outputs:["opened","closed","expandedChange","afterExpand","afterCollapse"],exportAs:["matExpansionPanel"]},{kind:"component",type:Ae.MatExpansionPanelHeader,selector:"mat-expansion-panel-header",inputs:["tabIndex","expandedHeight","collapsedHeight"]},{kind:"directive",type:Ae.MatExpansionPanelTitle,selector:"mat-panel-title"},{kind:"directive",type:Ae.MatExpansionPanelContent,selector:"ng-template[matExpansionPanelContent]"},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutAlignDirective,selector:" [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg]",inputs:["fxLayoutAlign","fxLayoutAlign.xs","fxLayoutAlign.sm","fxLayoutAlign.md","fxLayoutAlign.lg","fxLayoutAlign.xl","fxLayoutAlign.lt-sm","fxLayoutAlign.lt-md","fxLayoutAlign.lt-lg","fxLayoutAlign.lt-xl","fxLayoutAlign.gt-xs","fxLayoutAlign.gt-sm","fxLayoutAlign.gt-md","fxLayoutAlign.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:cn,selector:"tb-output-message-type-autocomplete",inputs:["autocompleteHint","subscriptSizing","required"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:fn,decorators:[{type:n,args:[{selector:"tb-action-node-msg-deduplication-config",template:"
\n \n {{'tb.rulenode.interval' | translate}}\n \n {{'tb.rulenode.interval-hint' | translate}}\n \n {{'tb.rulenode.interval-required' | translate}}\n \n \n {{'tb.rulenode.interval-min-error' | translate}}\n \n \n \n {{'tb.rulenode.strategy' | translate}}\n \n \n {{ deduplicationStrategiesTranslations.get(strategy) | translate }}\n \n \n \n {{'tb.rulenode.strategy-first-hint' | translate}}\n {{'tb.rulenode.strategy-last-hint' | translate}}\n \n {{'tb.rulenode.strategy-required' | translate}}\n \n \n
\n \n \n \n \n
\n \n \n \n
\n
Advanced settings
\n
\n
\n
\n \n \n {{'tb.rulenode.max-pending-msgs' | translate}}\n \n {{'tb.rulenode.max-pending-msgs-hint' | translate}}\n \n {{'tb.rulenode.max-pending-msgs-required' | translate}}\n \n \n {{'tb.rulenode.max-pending-msgs-max-error' | translate}}\n \n \n {{'tb.rulenode.max-pending-msgs-min-error' | translate}}\n \n \n \n {{'tb.rulenode.max-retries' | translate}}\n \n {{'tb.rulenode.max-retries-hint' | translate}}\n \n {{'tb.rulenode.max-retries-required' | translate}}\n \n \n {{'tb.rulenode.max-retries-max-error' | translate}}\n \n \n {{'tb.rulenode.max-retries-min-error' | translate}}\n \n \n \n
\n
\n",styles:[":host ::ng-deep .mat-expansion-panel.advanced-settings{border:none;box-shadow:none;padding:0}:host ::ng-deep .mat-expansion-panel.advanced-settings .mat-expansion-panel-body{padding:0}:host ::ng-deep .mat-expansion-panel:not(.mat-expanded) .mat-expansion-panel-header:not([aria-disabled=true]):hover{background:white}\n"]}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class gn{}e("ExampleHintComponent",gn),gn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:gn,deps:[],target:t.ɵɵFactoryTarget.Component}),gn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:gn,selector:"tb-example-hint",inputs:{hintText:"hintText",popupHelpLink:"popupHelpLink"},ngImport:t,template:'
\n
{{ hintText }}
\n
\n
\n
\n',styles:[":host .space-between{display:flex;justify-content:space-between;gap:20px}:host .space-between .see-example{display:flex;flex-shrink:0}\n"],dependencies:[{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:Pe.HelpPopupComponent,selector:"[tb-help-popup], [tb-help-popup-content]",inputs:["tb-help-popup","tb-help-popup-content","trigger-text","trigger-style","tb-help-popup-placement","tb-help-popup-style","hintMode"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:gn,decorators:[{type:n,args:[{selector:"tb-example-hint",template:'
\n
{{ hintText }}
\n
\n
\n
\n',styles:[":host .space-between{display:flex;justify-content:space-between;gap:20px}:host .space-between .see-example{display:flex;flex-shrink:0}\n"]}]}],propDecorators:{hintText:[{type:i}],popupHelpLink:[{type:i}]}});class yn extends h{constructor(e,t,n,r){super(e),this.store=e,this.translate=t,this.injector=n,this.fb=r,this.propagateChange=null,this.destroy$=new Ee,this.disabled=!1,this.uniqueKeyValuePairValidator=!1,this.required=!1,this.duplicateValuesValidator=e=>e.controls.key.value===e.controls.value.value?{uniqueKeyValuePair:!0}:null,this.oneMapRequiredValidator=e=>e.get("keyVals").value.length,this.propagateNestedErrors=e=>{if(this.kvListFormGroup&&this.kvListFormGroup.get("keyVals")&&"VALID"===this.kvListFormGroup.get("keyVals")?.status)return null;const t={};if(this.kvListFormGroup&&this.kvListFormGroup.setErrors(null),e instanceof R||e instanceof O){if(e.errors)for(const n of Object.keys(e.errors))t[n]=!0;for(const n of Object.keys(e.controls)){const r=this.propagateNestedErrors(e.controls[n]);if(r&&Object.keys(r).length)for(const e of Object.keys(r))t[e]=!0}return t}if(e.errors)for(const n of Object.keys(e.errors))t[n]=!0;return te(t,{})?null:t}}ngOnInit(){this.ngControl=this.injector.get(V),null!=this.ngControl&&(this.ngControl.valueAccessor=this),this.kvListFormGroup=this.fb.group({keyVals:this.fb.array([])},{validators:[this.propagateNestedErrors,this.oneMapRequiredValidator]}),this.kvListFormGroup.valueChanges.pipe(De(this.destroy$)).subscribe((()=>{this.updateModel()}))}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}keyValsFormArray(){return this.kvListFormGroup.get("keyVals")}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.kvListFormGroup.disable({emitEvent:!1}):this.kvListFormGroup.enable({emitEvent:!1})}writeValue(e){const t=Object.keys(e).map((t=>({key:t,value:e[t]})));if(this.keyValsFormArray().length===t.length)this.keyValsFormArray().patchValue(t,{emitEvent:!1});else{const e=[];t.forEach((t=>{e.push(this.fb.group({key:[t.key,[D.required,D.pattern(/(?:.|\s)*\S(&:.|\s)*/)]],value:[t.value,[D.required,D.pattern(/(?:.|\s)*\S(&:.|\s)*/)]]},{validators:this.uniqueKeyValuePairValidator?[this.duplicateValuesValidator]:[]}))})),this.kvListFormGroup.setControl("keyVals",this.fb.array(e,this.propagateNestedErrors),{emitEvent:!1})}}removeKeyVal(e){this.keyValsFormArray().removeAt(e)}addKeyVal(){this.keyValsFormArray().push(this.fb.group({key:["",[D.required,D.pattern(/(?:.|\s)*\S(&:.|\s)*/)]],value:["",[D.required,D.pattern(/(?:.|\s)*\S(&:.|\s)*/)]]},{validators:this.uniqueKeyValuePairValidator?[this.duplicateValuesValidator]:[]}))}validate(e){const t=this.kvListFormGroup.get("keyVals").value;if(!t.length&&this.required)return{kvMapRequired:!0};if(!this.kvListFormGroup.valid)return{kvFieldsRequired:!0};if(this.uniqueKeyValuePairValidator)for(const e of t)if(e.key===e.value)return{uniqueKeyValuePair:!0};return null}updateModel(){const e=this.kvListFormGroup.get("keyVals").value;if(this.required&&!e.length||!this.kvListFormGroup.valid)this.propagateChange(null);else{const t={};e.forEach((e=>{t[e.key]=e.value})),this.propagateChange(t)}}}e("KvMapConfigComponent",yn),yn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:yn,deps:[{token:G.Store},{token:j.TranslateService},{token:t.Injector},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),yn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:yn,selector:"tb-kv-map-config",inputs:{disabled:"disabled",uniqueKeyValuePairValidator:"uniqueKeyValuePairValidator",labelText:"labelText",requiredText:"requiredText",keyText:"keyText",keyRequiredText:"keyRequiredText",valText:"valText",valRequiredText:"valRequiredText",hintText:"hintText",popupHelpLink:"popupHelpLink",required:"required"},providers:[{provide:P,useExisting:a((()=>yn)),multi:!0},{provide:w,useExisting:a((()=>yn)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n
\n
{{ labelText }}
\n
\n {{ requiredText }}\n
\n
\n tb.rulenode.map-fields-required\n
\n
\n {{ \'tb.key-val.unique-key-value-pair-error\' | translate:\n {\n valText: valText,\n keyText: keyText\n } }}\n
\n
\n
\n
\n
\n
{{ keyText }}
\n
{{ valText }}
\n
\n
\n
\n
\n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n
\n
\n \n
\n \n
\n',styles:[":host .field-space{flex:1 1 50%}:host .actions-header{width:40px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n'],dependencies:[{kind:"directive",type:B.NgClass,selector:"[ngClass]",inputs:["class","ngClass"]},{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ie.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ie.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:se.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:he.DefaultClassDirective,selector:" [ngClass], [ngClass.xs], [ngClass.sm], [ngClass.md], [ngClass.lg], [ngClass.xl], [ngClass.lt-sm], [ngClass.lt-md], [ngClass.lt-lg], [ngClass.lt-xl], [ngClass.gt-xs], [ngClass.gt-sm], [ngClass.gt-md], [ngClass.gt-lg]",inputs:["ngClass","ngClass.xs","ngClass.sm","ngClass.md","ngClass.lg","ngClass.xl","ngClass.lt-sm","ngClass.lt-md","ngClass.lt-lg","ngClass.lt-xl","ngClass.gt-xs","ngClass.gt-sm","ngClass.gt-md","ngClass.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.FormControlDirective,selector:"[formControl]",inputs:["formControl","disabled","ngModel"],outputs:["ngModelChange"],exportAs:["ngForm"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:gn,selector:"tb-example-hint",inputs:["hintText","popupHelpLink"]},{kind:"pipe",type:B.AsyncPipe,name:"async"},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),Me([v()],yn.prototype,"disabled",void 0),Me([v()],yn.prototype,"uniqueKeyValuePairValidator",void 0),Me([v()],yn.prototype,"required",void 0),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:yn,decorators:[{type:n,args:[{selector:"tb-kv-map-config",providers:[{provide:P,useExisting:a((()=>yn)),multi:!0},{provide:w,useExisting:a((()=>yn)),multi:!0}],template:'
\n
\n
{{ labelText }}
\n
\n {{ requiredText }}\n
\n
\n tb.rulenode.map-fields-required\n
\n
\n {{ \'tb.key-val.unique-key-value-pair-error\' | translate:\n {\n valText: valText,\n keyText: keyText\n } }}\n
\n
\n
\n
\n
\n
{{ keyText }}
\n
{{ valText }}
\n
\n
\n
\n
\n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n
\n
\n \n
\n \n
\n',styles:[":host .field-space{flex:1 1 50%}:host .actions-header{width:40px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n']}]}],ctorParameters:function(){return[{type:G.Store},{type:j.TranslateService},{type:t.Injector},{type:E.FormBuilder}]},propDecorators:{disabled:[{type:i}],uniqueKeyValuePairValidator:[{type:i}],labelText:[{type:i}],requiredText:[{type:i}],keyText:[{type:i}],keyRequiredText:[{type:i}],valText:[{type:i}],valRequiredText:[{type:i}],hintText:[{type:i}],popupHelpLink:[{type:i}],required:[{type:i}]}});class xn extends h{get required(){return this.requiredValue}set required(e){this.requiredValue=xe(e)}constructor(e,t){super(e),this.store=e,this.fb=t,this.directionTypes=Object.values(g),this.directionTypeTranslations=y,this.entityType=x,this.propagateChange=null}ngOnInit(){this.deviceRelationsQueryFormGroup=this.fb.group({fetchLastLevelOnly:[!1,[]],direction:[null,[D.required]],maxLevel:[null,[D.min(1)]],relationType:[null],deviceTypes:[null,[D.required]]}),this.deviceRelationsQueryFormGroup.valueChanges.subscribe((e=>{this.deviceRelationsQueryFormGroup.valid?this.propagateChange(e):this.propagateChange(null)}))}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.deviceRelationsQueryFormGroup.disable({emitEvent:!1}):this.deviceRelationsQueryFormGroup.enable({emitEvent:!1})}writeValue(e){this.deviceRelationsQueryFormGroup.reset(e,{emitEvent:!1})}}e("DeviceRelationsQueryConfigComponent",xn),xn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:xn,deps:[{token:G.Store},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),xn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:xn,selector:"tb-device-relations-query-config",inputs:{disabled:"disabled",required:"required"},providers:[{provide:P,useExisting:a((()=>xn)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }} tb.rulenode.relations-query-config-direction-suffix\n \n \n \n \n tb.rulenode.max-relation-level\n \n \n {{ \'tb.rulenode.max-relation-level-error\' | translate }}\n \n \n
\n
\n \n {{ \'alias.last-level-relation\' | translate }}\n \n
\n \n \n \n help\n \n
\n',styles:[":host .last-level-slide-toggle{margin:8px 0 24px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n'],dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:we.EntitySubTypeListComponent,selector:"tb-entity-subtype-list",inputs:["required","floatLabel","label","disabled","entityType","emptyInputPlaceholder","filledInputPlaceholder","appearance","subscriptSizing","additionalClasses"]},{kind:"component",type:Re.RelationTypeAutocompleteComponent,selector:"tb-relation-type-autocomplete",inputs:["showLabel","additionalClasses","appearance","required","disabled","subscriptSizing"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:z.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:se.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"component",type:J.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Y.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:xn,decorators:[{type:n,args:[{selector:"tb-device-relations-query-config",providers:[{provide:P,useExisting:a((()=>xn)),multi:!0}],template:'
\n
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }} tb.rulenode.relations-query-config-direction-suffix\n \n \n \n \n tb.rulenode.max-relation-level\n \n \n {{ \'tb.rulenode.max-relation-level-error\' | translate }}\n \n \n
\n
\n \n {{ \'alias.last-level-relation\' | translate }}\n \n
\n \n \n \n help\n \n
\n',styles:[":host .last-level-slide-toggle{margin:8px 0 24px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n']}]}],ctorParameters:function(){return[{type:G.Store},{type:E.FormBuilder}]},propDecorators:{disabled:[{type:i}],required:[{type:i}]}});class bn extends h{get required(){return this.requiredValue}set required(e){this.requiredValue=xe(e)}constructor(e,t){super(e),this.store=e,this.fb=t,this.directionTypes=Object.values(g),this.directionTypeTranslations=y,this.propagateChange=null}ngOnInit(){this.relationsQueryFormGroup=this.fb.group({fetchLastLevelOnly:[!1,[]],direction:[null,[D.required]],maxLevel:[null,[D.min(1)]],filters:[null]}),this.relationsQueryFormGroup.valueChanges.subscribe((e=>{this.relationsQueryFormGroup.valid?this.propagateChange(e):this.propagateChange(null)}))}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.relationsQueryFormGroup.disable({emitEvent:!1}):this.relationsQueryFormGroup.enable({emitEvent:!1})}writeValue(e){this.relationsQueryFormGroup.reset(e||{},{emitEvent:!1})}}e("RelationsQueryConfigComponent",bn),bn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:bn,deps:[{token:G.Store},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),bn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:bn,selector:"tb-relations-query-config",inputs:{disabled:"disabled",required:"required"},providers:[{provide:P,useExisting:a((()=>bn)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n
tb.rulenode.relations-query
\n
\n
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }} tb.rulenode.relations-query-config-direction-suffix\n \n \n \n \n tb.rulenode.max-relation-level\n \n \n {{ \'tb.rulenode.max-relation-level-error\' | translate }}\n \n \n
\n
\n \n {{ \'alias.last-level-relation\' | translate }}\n \n
\n
\n
\n
relation.relation-filters
\n \n \n
\n
\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:J.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Y.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"component",type:Oe.RelationFiltersComponent,selector:"tb-relation-filters",inputs:["disabled","allowedEntityTypes"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:bn,decorators:[{type:n,args:[{selector:"tb-relations-query-config",providers:[{provide:P,useExisting:a((()=>bn)),multi:!0}],template:'
\n
tb.rulenode.relations-query
\n
\n
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }} tb.rulenode.relations-query-config-direction-suffix\n \n \n \n \n tb.rulenode.max-relation-level\n \n \n {{ \'tb.rulenode.max-relation-level-error\' | translate }}\n \n \n
\n
\n \n {{ \'alias.last-level-relation\' | translate }}\n \n
\n
\n
\n
relation.relation-filters
\n \n \n
\n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.FormBuilder}]},propDecorators:{disabled:[{type:i}],required:[{type:i}]}});class hn extends h{get required(){return this.requiredValue}set required(e){this.requiredValue=xe(e)}constructor(e,t,n,r){super(e),this.store=e,this.translate=t,this.truncate=n,this.fb=r,this.placeholder="tb.rulenode.add-message-type",this.separatorKeysCodes=[ue,pe,de],this.messageTypes=[],this.messageTypesList=[],this.searchText="",this.propagateChange=e=>{},this.messageTypeConfigForm=this.fb.group({messageType:[null]});for(const e of Object.keys(C))this.messageTypesList.push({name:F.get(C[e]),value:e})}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}ngOnInit(){this.filteredMessageTypes=this.messageTypeConfigForm.get("messageType").valueChanges.pipe(Te(""),ke((e=>e||"")),Le((e=>this.fetchMessageTypes(e))),Ie())}setDisabledState(e){this.disabled=e,this.disabled?this.messageTypeConfigForm.disable({emitEvent:!1}):this.messageTypeConfigForm.enable({emitEvent:!1})}writeValue(e){this.searchText="",this.messageTypes.length=0,e&&e.forEach((e=>{const t=this.messageTypesList.find((t=>t.value===e));t?this.messageTypes.push({name:t.name,value:t.value}):this.messageTypes.push({name:e,value:e})}))}displayMessageTypeFn(e){return e?e.name:void 0}textIsNotEmpty(e){return e&&e.length>0}createMessageType(e,t){e.preventDefault(),this.transformMessageType(t)}add(e){this.transformMessageType(e.value)}fetchMessageTypes(e){if(this.searchText=e,this.searchText&&this.searchText.length){const e=this.searchText.toUpperCase();return Ge(this.messageTypesList.filter((t=>t.name.toUpperCase().includes(e))))}return Ge(this.messageTypesList)}transformMessageType(e){if((e||"").trim()){let t;const n=e.trim(),r=this.messageTypesList.find((e=>e.name===n));t=r?{name:r.name,value:r.value}:{name:n,value:n},t&&this.addMessageType(t)}this.clear("")}remove(e){const t=this.messageTypes.indexOf(e);t>=0&&(this.messageTypes.splice(t,1),this.updateModel())}selected(e){this.addMessageType(e.option.value),this.clear("")}addMessageType(e){-1===this.messageTypes.findIndex((t=>t.value===e.value))&&(this.messageTypes.push(e),this.updateModel())}onFocus(){this.messageTypeConfigForm.get("messageType").updateValueAndValidity({onlySelf:!0,emitEvent:!0})}clear(e=""){this.messageTypeInput.nativeElement.value=e,this.messageTypeConfigForm.get("messageType").patchValue(null,{emitEvent:!0}),setTimeout((()=>{this.messageTypeInput.nativeElement.blur(),this.messageTypeInput.nativeElement.focus()}),0)}updateModel(){const e=this.messageTypes.map((e=>e.value));this.required?(this.chipList.errorState=!e.length,this.propagateChange(e.length>0?e:null)):(this.chipList.errorState=!1,this.propagateChange(e))}}e("MessageTypesConfigComponent",hn),hn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:hn,deps:[{token:G.Store},{token:j.TranslateService},{token:k.TruncatePipe},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),hn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:hn,selector:"tb-message-types-config",inputs:{required:"required",label:"label",placeholder:"placeholder",disabled:"disabled"},providers:[{provide:P,useExisting:a((()=>hn)),multi:!0}],viewQueries:[{propertyName:"chipList",first:!0,predicate:["chipList"],descendants:!0},{propertyName:"matAutocomplete",first:!0,predicate:["messageTypeAutocomplete"],descendants:!0},{propertyName:"messageTypeInput",first:!0,predicate:["messageTypeInput"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'\n {{ label }}\n \n \n {{messageType.name}}\n close\n \n \n \n \n \n \n \n \n
\n
\n tb.rulenode.no-message-types-found\n
\n \n \n {{ \'tb.rulenode.no-message-type-matching\' | translate :\n {messageType: truncate.transform(searchText, true, 6, '...')}\n }}\n \n \n \n tb.rulenode.create-new-message-type\n \n
\n
\n
\n help\n \n {{ \'tb.rulenode.select-message-types-required\' | translate }}\n \n
\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:z.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:se.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"component",type:Se.MatAutocomplete,selector:"mat-autocomplete",inputs:["disableRipple","hideSingleSelectionIndicator"],exportAs:["matAutocomplete"]},{kind:"directive",type:Se.MatAutocompleteTrigger,selector:"input[matAutocomplete], textarea[matAutocomplete]",exportAs:["matAutocompleteTrigger"]},{kind:"directive",type:Se.MatAutocompleteOrigin,selector:"[matAutocompleteOrigin]",exportAs:["matAutocompleteOrigin"]},{kind:"component",type:ce.MatChipGrid,selector:"mat-chip-grid",inputs:["tabIndex","disabled","placeholder","required","value","errorStateMatcher"],outputs:["change","valueChange"]},{kind:"directive",type:ce.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputFor","matChipInputAddOnBlur","matChipInputSeparatorKeyCodes","placeholder","id","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]},{kind:"directive",type:ce.MatChipRemove,selector:"[matChipRemove]"},{kind:"component",type:ce.MatChipRow,selector:"mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]",inputs:["color","disabled","disableRipple","tabIndex","editable"],outputs:["edited"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:B.AsyncPipe,name:"async"},{kind:"pipe",type:qe.HighlightPipe,name:"highlight"},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:hn,decorators:[{type:n,args:[{selector:"tb-message-types-config",providers:[{provide:P,useExisting:a((()=>hn)),multi:!0}],template:'\n {{ label }}\n \n \n {{messageType.name}}\n close\n \n \n \n \n \n \n \n \n
\n
\n tb.rulenode.no-message-types-found\n
\n \n \n {{ \'tb.rulenode.no-message-type-matching\' | translate :\n {messageType: truncate.transform(searchText, true, 6, '...')}\n }}\n \n \n \n tb.rulenode.create-new-message-type\n \n
\n
\n
\n help\n \n {{ \'tb.rulenode.select-message-types-required\' | translate }}\n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:j.TranslateService},{type:k.TruncatePipe},{type:E.FormBuilder}]},propDecorators:{required:[{type:i}],label:[{type:i}],placeholder:[{type:i}],disabled:[{type:i}],chipList:[{type:o,args:["chipList",{static:!1}]}],matAutocomplete:[{type:o,args:["messageTypeAutocomplete",{static:!1}]}],messageTypeInput:[{type:o,args:["messageTypeInput",{static:!1}]}]}});class vn extends h{get required(){return this.requiredValue}set required(e){this.requiredValue=xe(e)}constructor(e,t){super(e),this.store=e,this.fb=t,this.subscriptions=[],this.disableCertPemCredentials=!1,this.passwordFieldRequired=!0,this.allCredentialsTypes=Mt,this.credentialsTypeTranslationsMap=Gt,this.propagateChange=e=>{}}ngOnInit(){this.credentialsConfigFormGroup=this.fb.group({type:[null,[D.required]],username:[null,[]],password:[null,[]],caCert:[null,[]],caCertFileName:[null,[]],privateKey:[null,[]],privateKeyFileName:[null,[]],cert:[null,[]],certFileName:[null,[]]}),this.subscriptions.push(this.credentialsConfigFormGroup.valueChanges.subscribe((()=>{this.updateView()}))),this.subscriptions.push(this.credentialsConfigFormGroup.get("type").valueChanges.subscribe((()=>{this.credentialsTypeChanged()})))}ngOnChanges(e){for(const t of Object.keys(e)){const n=e[t];if(!n.firstChange&&n.currentValue!==n.previousValue&&n.currentValue&&"disableCertPemCredentials"===t){"cert.PEM"===this.credentialsConfigFormGroup.get("type").value&&setTimeout((()=>{this.credentialsConfigFormGroup.get("type").patchValue("anonymous",{emitEvent:!0})}))}}}ngOnDestroy(){this.subscriptions.forEach((e=>e.unsubscribe()))}writeValue(e){ee(e)&&(this.credentialsConfigFormGroup.reset(e,{emitEvent:!1}),this.updateValidators())}setDisabledState(e){e?this.credentialsConfigFormGroup.disable({emitEvent:!1}):(this.credentialsConfigFormGroup.enable({emitEvent:!1}),this.updateValidators())}updateView(){let e=this.credentialsConfigFormGroup.value;const t=e.type;switch(t){case"anonymous":e={type:t};break;case"basic":e={type:t,username:e.username,password:e.password};break;case"cert.PEM":delete e.username}this.propagateChange(e)}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}validate(e){return this.credentialsConfigFormGroup.valid?null:{credentialsConfig:{valid:!1}}}credentialsTypeChanged(){this.credentialsConfigFormGroup.patchValue({username:null,password:null,caCert:null,caCertFileName:null,privateKey:null,privateKeyFileName:null,cert:null,certFileName:null}),this.updateValidators()}updateValidators(e=!1){const t=this.credentialsConfigFormGroup.get("type").value;switch(e&&this.credentialsConfigFormGroup.reset({type:t},{emitEvent:!1}),this.credentialsConfigFormGroup.setValidators([]),this.credentialsConfigFormGroup.get("username").setValidators([]),this.credentialsConfigFormGroup.get("password").setValidators([]),t){case"anonymous":break;case"basic":this.credentialsConfigFormGroup.get("username").setValidators([D.required]),this.credentialsConfigFormGroup.get("password").setValidators(this.passwordFieldRequired?[D.required]:[]);break;case"cert.PEM":this.credentialsConfigFormGroup.setValidators([this.requiredFilesSelected(D.required,[["caCert","caCertFileName"],["privateKey","privateKeyFileName","cert","certFileName"]])])}this.credentialsConfigFormGroup.get("username").updateValueAndValidity({emitEvent:e}),this.credentialsConfigFormGroup.get("password").updateValueAndValidity({emitEvent:e}),this.credentialsConfigFormGroup.updateValueAndValidity({emitEvent:e})}requiredFilesSelected(e,t=null){return n=>{t||(t=[Object.keys(n.controls)]);return n?.controls&&t.some((t=>t.every((t=>!e(n.controls[t])))))?null:{notAllRequiredFilesSelected:!0}}}}e("CredentialsConfigComponent",vn),vn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:vn,deps:[{token:G.Store},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),vn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:vn,selector:"tb-credentials-config",inputs:{required:"required",disableCertPemCredentials:"disableCertPemCredentials",passwordFieldRequired:"passwordFieldRequired"},providers:[{provide:P,useExisting:a((()=>vn)),multi:!0},{provide:w,useExisting:a((()=>vn)),multi:!0}],usesInheritance:!0,usesOnChanges:!0,ngImport:t,template:'
\n \n \n tb.rulenode.credentials\n \n {{ credentialsTypeTranslationsMap.get(credentialsConfigFormGroup.get(\'type\').value) | translate }}\n \n \n \n \n tb.rulenode.credentials-type\n \n \n {{ credentialsTypeTranslationsMap.get(credentialsType) | translate }}\n \n \n \n {{ \'tb.rulenode.credentials-type-required\' | translate }}\n \n \n
\n \n \n \n \n tb.rulenode.username\n \n \n {{ \'tb.rulenode.username-required\' | translate }}\n \n \n \n tb.rulenode.password\n \n \n \n {{ \'tb.rulenode.password-required\' | translate }}\n \n \n \n \n
{{ \'tb.rulenode.credentials-pem-hint\' | translate }}
\n \n \n \n \n \n \n \n tb.rulenode.private-key-password\n \n \n \n
\n
\n
\n
\n
\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:B.NgSwitch,selector:"[ngSwitch]",inputs:["ngSwitch"]},{kind:"directive",type:B.NgSwitchCase,selector:"[ngSwitchCase]",inputs:["ngSwitchCase"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:z.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:Ae.MatExpansionPanel,selector:"mat-expansion-panel",inputs:["disabled","expanded","hideToggle","togglePosition"],outputs:["opened","closed","expandedChange","afterExpand","afterCollapse"],exportAs:["matExpansionPanel"]},{kind:"component",type:Ae.MatExpansionPanelHeader,selector:"mat-expansion-panel-header",inputs:["tabIndex","expandedHeight","collapsedHeight"]},{kind:"directive",type:Ae.MatExpansionPanelTitle,selector:"mat-panel-title"},{kind:"directive",type:Ae.MatExpansionPanelDescription,selector:"mat-panel-description"},{kind:"directive",type:Ae.MatExpansionPanelContent,selector:"ng-template[matExpansionPanelContent]"},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:Be.FileInputComponent,selector:"tb-file-input",inputs:["label","hint","accept","noFileText","inputId","allowedExtensions","dropLabel","contentConvertFunction","required","requiredAsError","disabled","existingFileName","readAsBinary","workFromFileObj","multipleFile"],outputs:["fileNameChanged"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Ke.TogglePasswordComponent,selector:"tb-toggle-password"},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:vn,decorators:[{type:n,args:[{selector:"tb-credentials-config",providers:[{provide:P,useExisting:a((()=>vn)),multi:!0},{provide:w,useExisting:a((()=>vn)),multi:!0}],template:'
\n \n \n tb.rulenode.credentials\n \n {{ credentialsTypeTranslationsMap.get(credentialsConfigFormGroup.get(\'type\').value) | translate }}\n \n \n \n \n tb.rulenode.credentials-type\n \n \n {{ credentialsTypeTranslationsMap.get(credentialsType) | translate }}\n \n \n \n {{ \'tb.rulenode.credentials-type-required\' | translate }}\n \n \n
\n \n \n \n \n tb.rulenode.username\n \n \n {{ \'tb.rulenode.username-required\' | translate }}\n \n \n \n tb.rulenode.password\n \n \n \n {{ \'tb.rulenode.password-required\' | translate }}\n \n \n \n \n
{{ \'tb.rulenode.credentials-pem-hint\' | translate }}
\n \n \n \n \n \n \n \n tb.rulenode.private-key-password\n \n \n \n
\n
\n
\n
\n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.FormBuilder}]},propDecorators:{required:[{type:i}],disableCertPemCredentials:[{type:i}],passwordFieldRequired:[{type:i}]}});class Cn{constructor(e,t){this.fb=e,this.translate=t,this.propagateChange=e=>{},this.destroy$=new Ee,this.selectOptions=[];for(const e of _t.keys())this.selectOptions.push({value:e,name:this.translate.instant(_t.get(e))})}ngOnInit(){this.chipControlGroup=this.fb.group({chipControl:[null,[]]}),this.chipControlGroup.get("chipControl").valueChanges.pipe(Ne(this.destroy$)).subscribe((e=>{e&&this.propagateChange(e)}))}writeValue(e){this.chipControlGroup.get("chipControl").patchValue(e,{emitEvent:!1})}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){e?this.chipControlGroup.disable({emitEvent:!1}):this.chipControlGroup.enable({emitEvent:!1})}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}}e("MsgMetadataChipComponent",Cn),Cn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Cn,deps:[{token:E.FormBuilder},{token:j.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Cn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Cn,selector:"tb-msg-metadata-chip",inputs:{labelText:"labelText"},providers:[{provide:P,useExisting:a((()=>Cn)),multi:!0}],ngImport:t,template:'
\n
{{ labelText }}
\n \n {{ option.name }}\n \n
\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"component",type:ce.MatChipListbox,selector:"mat-chip-listbox",inputs:["tabIndex","multiple","aria-orientation","selectable","compareWith","required","hideSingleSelectionIndicator","value"],outputs:["change"]},{kind:"component",type:ce.MatChipOption,selector:"mat-basic-chip-option, [mat-basic-chip-option], mat-chip-option, [mat-chip-option]",inputs:["color","disabled","disableRipple","tabIndex","selectable","selected"],outputs:["selectionChange"]},{kind:"directive",type:he.DefaultClassDirective,selector:" [ngClass], [ngClass.xs], [ngClass.sm], [ngClass.md], [ngClass.lg], [ngClass.xl], [ngClass.lt-sm], [ngClass.lt-md], [ngClass.lt-lg], [ngClass.lt-xl], [ngClass.gt-xs], [ngClass.gt-sm], [ngClass.gt-md], [ngClass.gt-lg]",inputs:["ngClass","ngClass.xs","ngClass.sm","ngClass.md","ngClass.lg","ngClass.xl","ngClass.lt-sm","ngClass.lt-md","ngClass.lt-lg","ngClass.lt-xl","ngClass.gt-xs","ngClass.gt-sm","ngClass.gt-md","ngClass.gt-lg"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Cn,decorators:[{type:n,args:[{selector:"tb-msg-metadata-chip",providers:[{provide:P,useExisting:a((()=>Cn)),multi:!0}],template:'
\n
{{ labelText }}
\n \n {{ option.name }}\n \n
\n'}]}],ctorParameters:function(){return[{type:E.FormBuilder},{type:j.TranslateService}]},propDecorators:{labelText:[{type:i}]}});class Fn extends h{constructor(e,t,n,r){super(e),this.store=e,this.translate=t,this.injector=n,this.fb=r,this.destroy$=new Ee,this.sourceFieldSubcritption=[],this.propagateChange=null,this.disabled=!1,this.required=!1,this.oneMapRequiredValidator=e=>e.get("keyVals").value.length,this.propagateNestedErrors=e=>{if(this.svListFormGroup&&this.svListFormGroup.get("keyVals")&&"VALID"===this.svListFormGroup.get("keyVals")?.status)return null;const t={};if(this.svListFormGroup&&this.svListFormGroup.setErrors(null),e instanceof R||e instanceof O){if(e.errors)for(const n of Object.keys(e.errors))t[n]=!0;for(const n of Object.keys(e.controls)){const r=this.propagateNestedErrors(e.controls[n]);if(r&&Object.keys(r).length)for(const e of Object.keys(r))t[e]=!0}return t}if(e.errors)for(const n of Object.keys(e.errors))t[n]=!0;return te(t,{})?null:t}}ngOnInit(){this.ngControl=this.injector.get(V),null!=this.ngControl&&(this.ngControl.valueAccessor=this),this.svListFormGroup=this.fb.group({keyVals:this.fb.array([])},{validators:[this.propagateNestedErrors,this.oneMapRequiredValidator]}),this.svListFormGroup.valueChanges.pipe(Ne(this.destroy$)).subscribe((()=>{this.updateModel()}))}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}keyValsFormArray(){return this.svListFormGroup.get("keyVals")}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.svListFormGroup.disable({emitEvent:!1}):this.svListFormGroup.enable({emitEvent:!1})}writeValue(e){const t=Object.keys(e).map((t=>({key:t,value:e[t]})));if(this.keyValsFormArray().length===t.length)this.keyValsFormArray().patchValue(t,{emitEvent:!1});else{const e=[];t.forEach((t=>{e.push(this.fb.group({key:[t.key,[D.required]],value:[t.value,[D.required,D.pattern(/(?:.|\s)*\S(&:.|\s)*/)]]}))})),this.svListFormGroup.setControl("keyVals",this.fb.array(e,this.propagateNestedErrors),{emitEvent:!1});for(const e of this.keyValsFormArray().controls)this.keyChangeSubscribe(e)}}filterSelectOptions(e){const t=[];for(const e of this.svListFormGroup.get("keyVals").value){const n=this.selectOptions.find((t=>t.value===e.key));n&&t.push(n)}const n=[];for(const r of this.selectOptions)ee(t.find((e=>e.value===r.value)))&&r.value!==e?.get("key").value||n.push(r);return n}removeKeyVal(e){this.keyValsFormArray().removeAt(e),this.sourceFieldSubcritption[e].unsubscribe(),this.sourceFieldSubcritption.splice(e,1)}addKeyVal(){this.keyValsFormArray().push(this.fb.group({key:["",[D.required]],value:["",[D.required,D.pattern(/(?:.|\s)*\S(&:.|\s)*/)]]})),this.keyChangeSubscribe(this.keyValsFormArray().at(this.keyValsFormArray().length-1))}keyChangeSubscribe(e){this.sourceFieldSubcritption.push(e.get("key").valueChanges.pipe(Ne(this.destroy$)).subscribe((t=>{const n=dt.get(t);e.get("value").patchValue(this.targetKeyPrefix+n[0].toUpperCase()+n.slice(1))})))}validate(e){return!this.svListFormGroup.get("keyVals").value.length&&this.required?{svMapRequired:!0}:this.svListFormGroup.valid?null:{svFieldsRequired:!0}}updateModel(){const e=this.svListFormGroup.get("keyVals").value;if(this.required&&!e.length||!this.svListFormGroup.valid)this.propagateChange(null);else{const t={};e.forEach((e=>{t[e.key]=e.value})),this.propagateChange(t)}}}e("SvMapConfigComponent",Fn),Fn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Fn,deps:[{token:G.Store},{token:j.TranslateService},{token:t.Injector},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Fn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Fn,selector:"tb-sv-map-config",inputs:{selectOptions:"selectOptions",disabled:"disabled",labelText:"labelText",requiredText:"requiredText",targetKeyPrefix:"targetKeyPrefix",selectText:"selectText",selectRequiredText:"selectRequiredText",valText:"valText",valRequiredText:"valRequiredText",hintText:"hintText",popupHelpLink:"popupHelpLink",required:"required"},providers:[{provide:P,useExisting:a((()=>Fn)),multi:!0},{provide:w,useExisting:a((()=>Fn)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n
\n
{{ labelText }}
\n
\n tb.rulenode.map-fields-required\n
\n
\n {{ requiredText }}\n
\n
\n
\n
\n
\n
{{ selectText }}
\n
{{ valText }}
\n
\n
\n
\n
\n \n \n \n {{option.name}}\n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n
\n
\n \n
\n \n
\n',styles:[":host .field-space{flex:1 1 50%}:host .actions-header{width:40px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n'],dependencies:[{kind:"directive",type:B.NgClass,selector:"[ngClass]",inputs:["class","ngClass"]},{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ie.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ie.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:se.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:he.DefaultClassDirective,selector:" [ngClass], [ngClass.xs], [ngClass.sm], [ngClass.md], [ngClass.lg], [ngClass.xl], [ngClass.lt-sm], [ngClass.lt-md], [ngClass.lt-lg], [ngClass.lt-xl], [ngClass.gt-xs], [ngClass.gt-sm], [ngClass.gt-md], [ngClass.gt-lg]",inputs:["ngClass","ngClass.xs","ngClass.sm","ngClass.md","ngClass.lg","ngClass.xl","ngClass.lt-sm","ngClass.lt-md","ngClass.lt-lg","ngClass.lt-xl","ngClass.gt-xs","ngClass.gt-sm","ngClass.gt-md","ngClass.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormControlDirective,selector:"[formControl]",inputs:["formControl","disabled","ngModel"],outputs:["ngModelChange"],exportAs:["ngForm"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:gn,selector:"tb-example-hint",inputs:["hintText","popupHelpLink"]},{kind:"pipe",type:B.AsyncPipe,name:"async"},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),Me([v()],Fn.prototype,"disabled",void 0),Me([v()],Fn.prototype,"required",void 0),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Fn,decorators:[{type:n,args:[{selector:"tb-sv-map-config",providers:[{provide:P,useExisting:a((()=>Fn)),multi:!0},{provide:w,useExisting:a((()=>Fn)),multi:!0}],template:'
\n
\n
{{ labelText }}
\n
\n tb.rulenode.map-fields-required\n
\n
\n {{ requiredText }}\n
\n
\n
\n
\n
\n
{{ selectText }}
\n
{{ valText }}
\n
\n
\n
\n
\n \n \n \n {{option.name}}\n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n
\n
\n \n
\n \n
\n',styles:[":host .field-space{flex:1 1 50%}:host .actions-header{width:40px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n']}]}],ctorParameters:function(){return[{type:G.Store},{type:j.TranslateService},{type:t.Injector},{type:E.FormBuilder}]},propDecorators:{selectOptions:[{type:i}],disabled:[{type:i}],labelText:[{type:i}],requiredText:[{type:i}],targetKeyPrefix:[{type:i}],selectText:[{type:i}],selectRequiredText:[{type:i}],valText:[{type:i}],valRequiredText:[{type:i}],hintText:[{type:i}],popupHelpLink:[{type:i}],required:[{type:i}]}});class kn extends h{get required(){return this.requiredValue}set required(e){this.requiredValue=xe(e)}constructor(e,t){super(e),this.store=e,this.fb=t,this.directionTypes=Object.keys(g),this.directionTypeTranslations=y,this.propagateChange=null}ngOnInit(){this.relationsQueryFormGroup=this.fb.group({fetchLastLevelOnly:[!1,[]],direction:[null,[D.required]],maxLevel:[null,[]],filters:[null]}),this.relationsQueryFormGroup.valueChanges.subscribe((e=>{this.relationsQueryFormGroup.valid?this.propagateChange(e):this.propagateChange(null)}))}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.relationsQueryFormGroup.disable({emitEvent:!1}):this.relationsQueryFormGroup.enable({emitEvent:!1})}writeValue(e){this.relationsQueryFormGroup.reset(e||{},{emitEvent:!1})}}e("RelationsQueryConfigOldComponent",kn),kn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:kn,deps:[{token:G.Store},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),kn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:kn,selector:"tb-relations-query-config-old",inputs:{disabled:"disabled",required:"required"},providers:[{provide:P,useExisting:a((()=>kn)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'alias.last-level-relation\' | translate }}\n \n
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n \n tb.rulenode.max-relation-level\n \n \n
\n
relation.relation-filters
\n \n
\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"component",type:U.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Oe.RelationFiltersComponent,selector:"tb-relation-filters",inputs:["disabled","allowedEntityTypes"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:kn,decorators:[{type:n,args:[{selector:"tb-relations-query-config-old",providers:[{provide:P,useExisting:a((()=>kn)),multi:!0}],template:'
\n \n {{ \'alias.last-level-relation\' | translate }}\n \n
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n \n tb.rulenode.max-relation-level\n \n \n
\n
relation.relation-filters
\n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.FormBuilder}]},propDecorators:{disabled:[{type:i}],required:[{type:i}]}});class Ln{constructor(e,t){this.translate=e,this.fb=t,this.propagateChange=e=>{},this.destroy$=new Ee,this.separatorKeysCodes=[ue,pe,de],this.onTouched=()=>{}}ngOnInit(){this.attributeControlGroup=this.fb.group({clientAttributeNames:[null,[]],sharedAttributeNames:[null,[]],serverAttributeNames:[null,[]],latestTsKeyNames:[null,[]],getLatestValueWithTs:[!1,[]]},{validators:this.atLeastOne(D.required,["clientAttributeNames","sharedAttributeNames","serverAttributeNames","latestTsKeyNames"])}),this.attributeControlGroup.valueChanges.pipe(Ne(this.destroy$)).subscribe((e=>{this.propagateChange(e)}))}validate(){return this.attributeControlGroup.valid?null:{atLeastOneRequired:!0}}atLeastOne(e,t=null){return n=>{t||(t=Object.keys(n.controls));return n?.controls&&t.some((t=>!e(n.controls[t])))?null:{atLeastOne:!0}}}writeValue(e){this.attributeControlGroup.setValue(e,{emitEvent:!1})}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){this.onTouched=e}setDisabledState(e){e?this.attributeControlGroup.disable({emitEvent:!1}):this.attributeControlGroup.enable({emitEvent:!1})}ngOnDestroy(){this.destroy$.next(null),this.destroy$.complete()}}e("SelectAttributesComponent",Ln),Ln.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Ln,deps:[{token:j.TranslateService},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Ln.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Ln,selector:"tb-select-attributes",inputs:{popupHelpLink:"popupHelpLink"},providers:[{provide:P,useExisting:a((()=>Ln)),multi:!0},{provide:w,useExisting:Ln,multi:!0}],ngImport:t,template:'
\n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n \n {{ \'tb.rulenode.fetch-latest-telemetry-with-timestamp\' | translate }}\n \n
\n
\n\n\n help\n\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n'],dependencies:[{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:B.NgTemplateOutlet,selector:"[ngTemplateOutlet]",inputs:["ngTemplateOutletContext","ngTemplateOutlet","ngTemplateOutletInjector"]},{kind:"component",type:Ue.StringItemsListComponent,selector:"tb-string-items-list",inputs:["required","disabled","label","placeholder","hint","requiredText","floatLabel","appearance","editable","subscriptSizing","predefinedValues"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:z.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"directive",type:se.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"component",type:J.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:Y.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"component",type:gn,selector:"tb-example-hint",inputs:["hintText","popupHelpLink"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Ln,decorators:[{type:n,args:[{selector:"tb-select-attributes",providers:[{provide:P,useExisting:a((()=>Ln)),multi:!0},{provide:w,useExisting:Ln,multi:!0}],template:'
\n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n \n {{ \'tb.rulenode.fetch-latest-telemetry-with-timestamp\' | translate }}\n \n
\n
\n\n\n help\n\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n']}]}],ctorParameters:function(){return[{type:j.TranslateService},{type:E.FormBuilder}]},propDecorators:{popupHelpLink:[{type:i}]}});class Tn extends h{constructor(e,t){super(e),this.store=e,this.fb=t,this.propagateChange=null,this.destroy$=new Ee,this.alarmStatus=L,this.alarmStatusTranslations=T}ngOnInit(){this.alarmStatusGroup=this.fb.group({alarmStatus:[null,[]]}),this.alarmStatusGroup.get("alarmStatus").valueChanges.pipe(Ne(this.destroy$)).subscribe((e=>{this.propagateChange(e)}))}setDisabledState(e){e?this.alarmStatusGroup.disable({emitEvent:!1}):this.alarmStatusGroup.enable({emitEvent:!1})}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}writeValue(e){this.alarmStatusGroup.get("alarmStatus").patchValue(e,{emitEvent:!1})}}e("AlarmStatusSelectComponent",Tn),Tn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Tn,deps:[{token:G.Store},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Tn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Tn,selector:"tb-alarm-status-select",providers:[{provide:P,useExisting:a((()=>Tn)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n
\n \n {{ alarmStatusTranslations.get(alarmStatus.ACTIVE_UNACK) | translate }}\n \n \n {{ alarmStatusTranslations.get(alarmStatus.ACTIVE_ACK) | translate }}\n \n
\n
\n \n {{ alarmStatusTranslations.get(alarmStatus.CLEARED_UNACK) | translate }}\n \n \n {{ alarmStatusTranslations.get(alarmStatus.ACTIVE_UNACK) | translate }}\n \n
\n
\n
\n',styles:[":host .chip-listbox{max-width:460px;width:100%}:host .chip-listbox .toggle-column{display:flex;flex:1 1 100%;gap:8px}:host .chip-listbox .option{margin:0}@media screen and (max-width: 959px){:host .chip-listbox{max-width:360px}:host .chip-listbox .toggle-column{flex-direction:column}}:host ::ng-deep .chip-listbox .mdc-evolution-chip-set__chips{gap:8px}:host ::ng-deep .chip-listbox .option button{flex-basis:100%;justify-content:start}:host ::ng-deep .chip-listbox .option .mdc-evolution-chip__graphic{flex-grow:0}\n"],dependencies:[{kind:"component",type:ce.MatChipListbox,selector:"mat-chip-listbox",inputs:["tabIndex","multiple","aria-orientation","selectable","compareWith","required","hideSingleSelectionIndicator","value"],outputs:["change"]},{kind:"component",type:ce.MatChipOption,selector:"mat-basic-chip-option, [mat-basic-chip-option], mat-chip-option, [mat-chip-option]",inputs:["color","disabled","disableRipple","tabIndex","selectable","selected"],outputs:["selectionChange"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutAlignDirective,selector:" [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg]",inputs:["fxLayoutAlign","fxLayoutAlign.xs","fxLayoutAlign.sm","fxLayoutAlign.md","fxLayoutAlign.lg","fxLayoutAlign.xl","fxLayoutAlign.lt-sm","fxLayoutAlign.lt-md","fxLayoutAlign.lt-lg","fxLayoutAlign.lt-xl","fxLayoutAlign.gt-xs","fxLayoutAlign.gt-sm","fxLayoutAlign.gt-md","fxLayoutAlign.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Tn,decorators:[{type:n,args:[{selector:"tb-alarm-status-select",providers:[{provide:P,useExisting:a((()=>Tn)),multi:!0}],template:'
\n \n
\n \n {{ alarmStatusTranslations.get(alarmStatus.ACTIVE_UNACK) | translate }}\n \n \n {{ alarmStatusTranslations.get(alarmStatus.ACTIVE_ACK) | translate }}\n \n
\n
\n \n {{ alarmStatusTranslations.get(alarmStatus.CLEARED_UNACK) | translate }}\n \n \n {{ alarmStatusTranslations.get(alarmStatus.ACTIVE_UNACK) | translate }}\n \n
\n
\n
\n',styles:[":host .chip-listbox{max-width:460px;width:100%}:host .chip-listbox .toggle-column{display:flex;flex:1 1 100%;gap:8px}:host .chip-listbox .option{margin:0}@media screen and (max-width: 959px){:host .chip-listbox{max-width:360px}:host .chip-listbox .toggle-column{flex-direction:column}}:host ::ng-deep .chip-listbox .mdc-evolution-chip-set__chips{gap:8px}:host ::ng-deep .chip-listbox .option button{flex-basis:100%;justify-content:start}:host ::ng-deep .chip-listbox .option .mdc-evolution-chip__graphic{flex-grow:0}\n"]}]}],ctorParameters:function(){return[{type:G.Store},{type:E.FormBuilder}]}});class In{}e("RulenodeCoreConfigCommonModule",In),In.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:In,deps:[],target:t.ɵɵFactoryTarget.NgModule}),In.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.10",ngImport:t,type:In,declarations:[yn,xn,bn,hn,vn,un,pn,cn,on,Cn,Fn,kn,Ln,Tn,gn],imports:[K,I,Ve],exports:[yn,xn,bn,hn,vn,un,pn,cn,on,Cn,Fn,kn,Ln,Tn,gn]}),In.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:In,imports:[K,I,Ve]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:In,decorators:[{type:l,args:[{declarations:[yn,xn,bn,hn,vn,un,pn,cn,on,Cn,Fn,kn,Ln,Tn,gn],imports:[K,I,Ve],exports:[yn,xn,bn,hn,vn,un,pn,cn,on,Cn,Fn,kn,Ln,Tn,gn]}]}]});class Nn{}e("RuleNodeCoreConfigActionModule",Nn),Nn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Nn,deps:[],target:t.ɵɵFactoryTarget.NgModule}),Nn.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.10",ngImport:t,type:Nn,declarations:[mn,tt,ln,rn,Wt,et,rt,ot,at,Zt,it,st,Yt,Xt,nn,an,sn,nt,lt,tn,en,dn,fn],imports:[K,I,Ve,In],exports:[mn,tt,ln,rn,Wt,et,rt,ot,at,Zt,it,st,Yt,Xt,nn,an,sn,nt,lt,tn,en,dn,fn]}),Nn.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Nn,imports:[K,I,Ve,In]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Nn,decorators:[{type:l,args:[{declarations:[mn,tt,ln,rn,Wt,et,rt,ot,at,Zt,it,st,Yt,Xt,nn,an,sn,nt,lt,tn,en,dn,fn],imports:[K,I,Ve,In],exports:[mn,tt,ln,rn,Wt,et,rt,ot,at,Zt,it,st,Yt,Xt,nn,an,sn,nt,lt,tn,en,dn,fn]}]}]});class Sn extends s{constructor(e,t,n){super(e),this.store=e,this.translate=t,this.fb=n,this.separatorKeysCodes=[ue,pe,de]}configForm(){return this.calculateDeltaConfigForm}onConfigurationSet(e){this.calculateDeltaConfigForm=this.fb.group({inputValueKey:[e.inputValueKey,[D.required,D.pattern(/(?:.|\s)*\S(&:.|\s)*/)]],outputValueKey:[e.outputValueKey,[D.required,D.pattern(/(?:.|\s)*\S(&:.|\s)*/)]],useCache:[e.useCache,[]],addPeriodBetweenMsgs:[e.addPeriodBetweenMsgs,[]],periodValueKey:[e.periodValueKey,[]],round:[e.round,[D.min(0),D.max(15)]],tellFailureIfDeltaIsNegative:[e.tellFailureIfDeltaIsNegative,[]]})}prepareInputConfig(e){return{inputValueKey:ee(e?.inputValueKey)?e.inputValueKey:null,outputValueKey:ee(e?.outputValueKey)?e.outputValueKey:null,useCache:!ee(e?.useCache)||e.useCache,addPeriodBetweenMsgs:!!ee(e?.addPeriodBetweenMsgs)&&e.addPeriodBetweenMsgs,periodValueKey:ee(e?.periodValueKey)?e.periodValueKey:null,round:ee(e?.round)?e.round:null,tellFailureIfDeltaIsNegative:!ee(e?.tellFailureIfDeltaIsNegative)||e.tellFailureIfDeltaIsNegative}}prepareOutputConfig(e){return ne(e)}updateValidators(e){this.calculateDeltaConfigForm.get("addPeriodBetweenMsgs").value?this.calculateDeltaConfigForm.get("periodValueKey").setValidators([D.required]):this.calculateDeltaConfigForm.get("periodValueKey").setValidators([]),this.calculateDeltaConfigForm.get("periodValueKey").updateValueAndValidity({emitEvent:e})}validatorTriggers(){return["addPeriodBetweenMsgs"]}}e("CalculateDeltaConfigComponent",Sn),Sn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Sn,deps:[{token:G.Store},{token:j.TranslateService},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Sn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Sn,selector:"tb-enrichment-node-calculate-delta-config",usesInheritance:!0,ngImport:t,template:"
\n
\n \n {{ 'tb.rulenode.input-value-key' | translate }}\n \n \n {{ 'tb.rulenode.input-value-key-required' | translate }}\n \n \n \n {{ 'tb.rulenode.output-value-key' | translate }}\n \n \n {{ 'tb.rulenode.output-value-key-required' | translate }}\n \n \n
\n \n {{ 'tb.rulenode.number-of-digits-after-floating-point' | translate }}\n \n \n {{ 'tb.rulenode.number-of-digits-after-floating-point-range' | translate }}\n \n \n {{ 'tb.rulenode.number-of-digits-after-floating-point-range' | translate }}\n \n \n
\n
\n \n {{ 'tb.rulenode.failure-if-delta-negative' | translate }}\n \n
\n
\n \n {{ 'tb.rulenode.use-caching' | translate }}\n \n
\n
\n
\n \n {{ 'tb.rulenode.add-time-difference-between-readings' | translate:\n { inputValueKey: calculateDeltaConfigForm.get('inputValueKey').valid ?\n calculateDeltaConfigForm.get('inputValueKey').value : 'tb.rulenode.input-value-key' | translate } }}\n \n
\n \n {{ 'tb.rulenode.period-value-key' | translate }}\n \n \n {{ 'tb.rulenode.period-value-key-required' | translate }}\n \n \n
\n
\n
\n",dependencies:[{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:J.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:Y.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Sn,decorators:[{type:n,args:[{selector:"tb-enrichment-node-calculate-delta-config",template:"
\n
\n \n {{ 'tb.rulenode.input-value-key' | translate }}\n \n \n {{ 'tb.rulenode.input-value-key-required' | translate }}\n \n \n \n {{ 'tb.rulenode.output-value-key' | translate }}\n \n \n {{ 'tb.rulenode.output-value-key-required' | translate }}\n \n \n
\n \n {{ 'tb.rulenode.number-of-digits-after-floating-point' | translate }}\n \n \n {{ 'tb.rulenode.number-of-digits-after-floating-point-range' | translate }}\n \n \n {{ 'tb.rulenode.number-of-digits-after-floating-point-range' | translate }}\n \n \n
\n
\n \n {{ 'tb.rulenode.failure-if-delta-negative' | translate }}\n \n
\n
\n \n {{ 'tb.rulenode.use-caching' | translate }}\n \n
\n
\n
\n \n {{ 'tb.rulenode.add-time-difference-between-readings' | translate:\n { inputValueKey: calculateDeltaConfigForm.get('inputValueKey').valid ?\n calculateDeltaConfigForm.get('inputValueKey').value : 'tb.rulenode.input-value-key' | translate } }}\n \n
\n \n {{ 'tb.rulenode.period-value-key' | translate }}\n \n \n {{ 'tb.rulenode.period-value-key-required' | translate }}\n \n \n
\n
\n
\n"}]}],ctorParameters:function(){return[{type:G.Store},{type:j.TranslateService},{type:E.FormBuilder}]}});class qn extends s{constructor(e,t,n){super(e),this.store=e,this.fb=t,this.translate=n,this.fetchToData=[],this.DataToFetch=Tt;for(const e of It.keys())e!==Tt.FIELDS&&this.fetchToData.push({value:e,name:this.translate.instant(It.get(e))})}configForm(){return this.customerAttributesConfigForm}prepareOutputConfig(e){const t={};for(const n of Object.keys(e.dataMapping))t[n.trim()]=e.dataMapping[n];return e.dataMapping=t,ne(e)}prepareInputConfig(e){let t,n;return t=ee(e?.telemetry)?e.telemetry?Tt.LATEST_TELEMETRY:Tt.ATTRIBUTES:ee(e?.dataToFetch)?e.dataToFetch:Tt.ATTRIBUTES,n=ee(e?.attrMapping)?e.attrMapping:ee(e?.dataMapping)?e.dataMapping:null,{dataToFetch:t,dataMapping:n,fetchTo:ee(e?.fetchTo)?e.fetchTo:Ut.METADATA}}selectTranslation(e,t){return this.customerAttributesConfigForm.get("dataToFetch").value===Tt.LATEST_TELEMETRY?e:t}onConfigurationSet(e){this.customerAttributesConfigForm=this.fb.group({dataToFetch:[e.dataToFetch,[]],dataMapping:[e.dataMapping,[D.required]],fetchTo:[e.fetchTo]})}}e("CustomerAttributesConfigComponent",qn),qn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:qn,deps:[{token:G.Store},{token:E.FormBuilder},{token:j.TranslateService}],target:t.ɵɵFactoryTarget.Component}),qn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:qn,selector:"tb-enrichment-node-customer-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n
tb.rulenode.mapping-of-customers
\n
\n
\n \n \n {{ data.name }}\n \n \n
\n
\n \n \n \n \n
\n',styles:[":host .fetch-to-data-toggle{max-width:420px;width:100%}\n"],dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.DefaultLayoutAlignDirective,selector:" [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg]",inputs:["fxLayoutAlign","fxLayoutAlign.xs","fxLayoutAlign.sm","fxLayoutAlign.md","fxLayoutAlign.lg","fxLayoutAlign.xl","fxLayoutAlign.lt-sm","fxLayoutAlign.lt-md","fxLayoutAlign.lt-lg","fxLayoutAlign.lt-xl","fxLayoutAlign.gt-xs","fxLayoutAlign.gt-sm","fxLayoutAlign.gt-md","fxLayoutAlign.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"directive",type:_e.ToggleOption,selector:"tb-toggle-option",inputs:["value"]},{kind:"component",type:ze.ToggleSelectComponent,selector:"tb-toggle-select",inputs:["disabled","selectMediaBreakpoint","appearance","disablePagination"]},{kind:"component",type:yn,selector:"tb-kv-map-config",inputs:["disabled","uniqueKeyValuePairValidator","labelText","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","popupHelpLink","required"]},{kind:"component",type:Cn,selector:"tb-msg-metadata-chip",inputs:["labelText"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:qn,decorators:[{type:n,args:[{selector:"tb-enrichment-node-customer-attributes-config",template:'
\n
tb.rulenode.mapping-of-customers
\n
\n
\n \n \n {{ data.name }}\n \n \n
\n
\n \n \n \n \n
\n',styles:[":host .fetch-to-data-toggle{max-width:420px;width:100%}\n"]}]}],ctorParameters:function(){return[{type:G.Store},{type:E.FormBuilder},{type:j.TranslateService}]}});class An extends s{constructor(e,t,n){super(e),this.store=e,this.translate=t,this.fb=n}configForm(){return this.deviceAttributesConfigForm}onConfigurationSet(e){this.deviceAttributesConfigForm=this.fb.group({deviceRelationsQuery:[e.deviceRelationsQuery,[D.required]],tellFailureIfAbsent:[e.tellFailureIfAbsent,[]],fetchTo:[e.fetchTo,[]],attributesControl:[e.attributesControl,[]]})}prepareInputConfig(e){return re(e)&&(e.attributesControl={clientAttributeNames:ee(e?.clientAttributeNames)?e.clientAttributeNames:null,latestTsKeyNames:ee(e?.latestTsKeyNames)?e.latestTsKeyNames:null,serverAttributeNames:ee(e?.serverAttributeNames)?e.serverAttributeNames:null,sharedAttributeNames:ee(e?.sharedAttributeNames)?e.sharedAttributeNames:null,getLatestValueWithTs:!!ee(e?.getLatestValueWithTs)&&e.getLatestValueWithTs}),{deviceRelationsQuery:ee(e?.deviceRelationsQuery)?e.deviceRelationsQuery:null,tellFailureIfAbsent:!ee(e?.tellFailureIfAbsent)||e.tellFailureIfAbsent,fetchTo:ee(e?.fetchTo)?e.fetchTo:Ut.METADATA,attributesControl:e?e.attributesControl:null}}prepareOutputConfig(e){for(const t of Object.keys(e.attributesControl))e[t]=e.attributesControl[t];return delete e.attributesControl,e}}e("DeviceAttributesConfigComponent",An),An.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:An,deps:[{token:G.Store},{token:j.TranslateService},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),An.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:An,selector:"tb-enrichment-node-device-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n
\n
tb.rulenode.device-relations-query
\n \n \n
\n
\n
\n
tb.rulenode.related-device-attributes
\n
\n tb.rulenode.at-least-one-field-required\n
\n
\n \n \n
\n
\n \n {{ \'tb.rulenode.tell-failure\' | translate }}\n \n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n'],dependencies:[{kind:"component",type:J.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Y.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"component",type:xn,selector:"tb-device-relations-query-config",inputs:["disabled","required"]},{kind:"component",type:Cn,selector:"tb-msg-metadata-chip",inputs:["labelText"]},{kind:"component",type:Ln,selector:"tb-select-attributes",inputs:["popupHelpLink"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:An,decorators:[{type:n,args:[{selector:"tb-enrichment-node-device-attributes-config",template:'
\n
\n
tb.rulenode.device-relations-query
\n \n \n
\n
\n
\n
tb.rulenode.related-device-attributes
\n
\n tb.rulenode.at-least-one-field-required\n
\n
\n \n \n
\n
\n \n {{ \'tb.rulenode.tell-failure\' | translate }}\n \n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n']}]}],ctorParameters:function(){return[{type:G.Store},{type:j.TranslateService},{type:E.FormBuilder}]}});class Mn extends s{constructor(e,t,n){super(e),this.store=e,this.translate=t,this.fb=n,this.predefinedValues=[];for(const e of Object.keys(ht))this.predefinedValues.push({value:ht[e],name:this.translate.instant(vt.get(ht[e]))})}ngOnInit(){super.ngOnInit()}configForm(){return this.entityDetailsConfigForm}prepareInputConfig(e){let t;return t=ee(e?.addToMetadata)?e.addToMetadata?Ut.METADATA:Ut.DATA:e?.fetchTo?e.fetchTo:Ut.DATA,{detailsList:ee(e?.detailsList)?e.detailsList:null,fetchTo:t}}onConfigurationSet(e){this.entityDetailsConfigForm=this.fb.group({detailsList:[e.detailsList,[D.required]],fetchTo:[e.fetchTo,[]]})}}e("EntityDetailsConfigComponent",Mn),Mn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Mn,deps:[{token:G.Store},{token:j.TranslateService},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Mn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Mn,selector:"tb-enrichment-node-entity-details-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n help\n \n \n \n \n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n'],dependencies:[{kind:"component",type:Ue.StringItemsListComponent,selector:"tb-string-items-list",inputs:["required","disabled","label","placeholder","hint","requiredText","floatLabel","appearance","editable","subscriptSizing","predefinedValues"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:z.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"directive",type:se.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:Cn,selector:"tb-msg-metadata-chip",inputs:["labelText"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Mn,decorators:[{type:n,args:[{selector:"tb-enrichment-node-entity-details-config",template:'
\n \n \n help\n \n \n \n \n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n']}]}],ctorParameters:function(){return[{type:G.Store},{type:j.TranslateService},{type:E.FormBuilder}]}});class Gn extends s{constructor(e,t,n){super(e),this.store=e,this.translate=t,this.fb=n,this.separatorKeysCodes=[ue,pe,de],this.aggregationTypes=N,this.aggregations=Object.values(N),this.aggregationTypesTranslations=S,this.fetchMode=Ct,this.samplingOrders=Object.values(Lt),this.samplingOrdersTranslate=St,this.timeUnits=Object.values(gt),this.timeUnitsTranslationMap=yt,this.deduplicationStrategiesHintTranslations=kt,this.headerOptions=[],this.timeUnitMap={[gt.MILLISECONDS]:1,[gt.SECONDS]:1e3,[gt.MINUTES]:6e4,[gt.HOURS]:36e5,[gt.DAYS]:864e5},this.intervalValidator=()=>e=>e.get("startInterval").value*this.timeUnitMap[e.get("startIntervalTimeUnit").value]<=e.get("endInterval").value*this.timeUnitMap[e.get("endIntervalTimeUnit").value]?{intervalError:!0}:null;for(const e of Ft.keys())this.headerOptions.push({value:e,name:this.translate.instant(Ft.get(e))})}configForm(){return this.getTelemetryFromDatabaseConfigForm}onConfigurationSet(e){this.getTelemetryFromDatabaseConfigForm=this.fb.group({latestTsKeyNames:[e.latestTsKeyNames,[D.required]],aggregation:[e.aggregation,[D.required]],fetchMode:[e.fetchMode,[D.required]],orderBy:[e.orderBy,[]],limit:[e.limit,[]],useMetadataIntervalPatterns:[e.useMetadataIntervalPatterns,[]],interval:this.fb.group({startInterval:[e.interval.startInterval,[]],startIntervalTimeUnit:[e.interval.startIntervalTimeUnit,[]],endInterval:[e.interval.endInterval,[]],endIntervalTimeUnit:[e.interval.endIntervalTimeUnit,[]]}),startIntervalPattern:[e.startIntervalPattern,[]],endIntervalPattern:[e.endIntervalPattern,[]]})}validatorTriggers(){return["fetchMode","useMetadataIntervalPatterns"]}toggleChange(e){this.getTelemetryFromDatabaseConfigForm.get("fetchMode").patchValue(e,{emitEvent:!0})}prepareOutputConfig(e){return e.startInterval=e.interval.startInterval,e.startIntervalTimeUnit=e.interval.startIntervalTimeUnit,e.endInterval=e.interval.endInterval,e.endIntervalTimeUnit=e.interval.endIntervalTimeUnit,delete e.interval,ne(e)}prepareInputConfig(e){return re(e)&&(e.interval={startInterval:e.startInterval,startIntervalTimeUnit:e.startIntervalTimeUnit,endInterval:e.endInterval,endIntervalTimeUnit:e.endIntervalTimeUnit}),{latestTsKeyNames:ee(e?.latestTsKeyNames)?e.latestTsKeyNames:null,aggregation:ee(e?.aggregation)?e.aggregation:N.NONE,fetchMode:ee(e?.fetchMode)?e.fetchMode:Ct.FIRST,orderBy:ee(e?.orderBy)?e.orderBy:Lt.ASC,limit:ee(e?.limit)?e.limit:1e3,useMetadataIntervalPatterns:!!ee(e?.useMetadataIntervalPatterns)&&e.useMetadataIntervalPatterns,interval:{startInterval:ee(e?.interval?.startInterval)?e.interval.startInterval:2,startIntervalTimeUnit:ee(e?.interval?.startIntervalTimeUnit)?e.interval.startIntervalTimeUnit:gt.MINUTES,endInterval:ee(e?.interval?.endInterval)?e.interval.endInterval:1,endIntervalTimeUnit:ee(e?.interval?.endIntervalTimeUnit)?e.interval.endIntervalTimeUnit:gt.MINUTES},startIntervalPattern:ee(e?.startIntervalPattern)?e.startIntervalPattern:null,endIntervalPattern:ee(e?.endIntervalPattern)?e.endIntervalPattern:null}}updateValidators(e){const t=this.getTelemetryFromDatabaseConfigForm.get("fetchMode").value,n=this.getTelemetryFromDatabaseConfigForm.get("useMetadataIntervalPatterns").value;t&&t===Ct.ALL?(this.getTelemetryFromDatabaseConfigForm.get("aggregation").setValidators([D.required]),this.getTelemetryFromDatabaseConfigForm.get("orderBy").setValidators([D.required]),this.getTelemetryFromDatabaseConfigForm.get("limit").setValidators([D.required,D.min(2),D.max(1e3)])):(this.getTelemetryFromDatabaseConfigForm.get("aggregation").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("orderBy").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("limit").setValidators([])),n?(this.getTelemetryFromDatabaseConfigForm.get("interval.startInterval").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("interval.startIntervalTimeUnit").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("interval.endInterval").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("interval.endIntervalTimeUnit").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("interval").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("startIntervalPattern").setValidators([D.required,D.pattern(/(?:.|\s)*\S(&:.|\s)*/)]),this.getTelemetryFromDatabaseConfigForm.get("endIntervalPattern").setValidators([D.required,D.pattern(/(?:.|\s)*\S(&:.|\s)*/)])):(this.getTelemetryFromDatabaseConfigForm.get("interval.startInterval").setValidators([D.required,D.min(1),D.max(2147483647)]),this.getTelemetryFromDatabaseConfigForm.get("interval.startIntervalTimeUnit").setValidators([D.required]),this.getTelemetryFromDatabaseConfigForm.get("interval.endInterval").setValidators([D.required,D.min(1),D.max(2147483647)]),this.getTelemetryFromDatabaseConfigForm.get("interval.endIntervalTimeUnit").setValidators([D.required]),this.getTelemetryFromDatabaseConfigForm.get("interval").setValidators([this.intervalValidator()]),this.getTelemetryFromDatabaseConfigForm.get("startIntervalPattern").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("endIntervalPattern").setValidators([])),this.getTelemetryFromDatabaseConfigForm.get("aggregation").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("orderBy").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("limit").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("interval.startInterval").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("interval.startIntervalTimeUnit").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("interval.endInterval").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("interval.endIntervalTimeUnit").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("interval").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("startIntervalPattern").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("endIntervalPattern").updateValueAndValidity({emitEvent:e})}removeKey(e,t){const n=this.getTelemetryFromDatabaseConfigForm.get(t).value,r=n.indexOf(e);r>=0&&(n.splice(r,1),this.getTelemetryFromDatabaseConfigForm.get(t).setValue(n,{emitEvent:!0}))}clearChipGrid(){this.getTelemetryFromDatabaseConfigForm.get("latestTsKeyNames").patchValue([],{emitEvent:!0})}addKey(e,t){const n=e.input;let r=e.value;if((r||"").trim()){r=r.trim();let e=this.getTelemetryFromDatabaseConfigForm.get(t).value;e&&-1!==e.indexOf(r)||(e||(e=[]),e.push(r),this.getTelemetryFromDatabaseConfigForm.get(t).setValue(e,{emitEvent:!0}))}n&&(n.value="")}defaultPaddingEnable(){return this.getTelemetryFromDatabaseConfigForm.get("fetchMode").value===Ct.ALL&&this.getTelemetryFromDatabaseConfigForm.get("aggregation").value===N.NONE}}e("GetTelemetryFromDatabaseConfigComponent",Gn),Gn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Gn,deps:[{token:G.Store},{token:j.TranslateService},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Gn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Gn,selector:"tb-enrichment-node-get-telemetry-from-database",usesInheritance:!0,ngImport:t,template:'
\n \n
\n help\n \n
\n
\n
tb.rulenode.fetch-interval
\n
\n \n {{ \'tb.rulenode.use-metadata-dynamic-interval\' | translate }}\n \n
\n
\n
\n \n {{ \'tb.rulenode.interval-start\' | translate }}\n \n \n {{ \'tb.rulenode.start-interval-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n {{ \'tb.rulenode.time-unit\' | translate }}\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n \n {{ \'tb.rulenode.interval-end\' | translate }}\n \n \n {{ \'tb.rulenode.end-interval-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n {{ \'tb.rulenode.time-unit\' | translate }}\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n error_outline\n
\n \n {{ \'tb.rulenode.fetch-timeseries-from-to\' | translate:\n {\n startInterval: getTelemetryFromDatabaseConfigForm.get(\'interval.startInterval\').value,\n endInterval: getTelemetryFromDatabaseConfigForm.get(\'interval.endInterval\').value,\n startIntervalTimeUnit: getTelemetryFromDatabaseConfigForm.get(\'interval.startIntervalTimeUnit\').value.toLowerCase(),\n endIntervalTimeUnit: getTelemetryFromDatabaseConfigForm.get(\'interval.endIntervalTimeUnit\').value.toLowerCase()\n } }}\n \n \n {{ "tb.rulenode.fetch-timeseries-from-to-invalid" | translate }}\n \n
\n
\n
\n \n
\n \n {{ \'tb.rulenode.start-interval\' | translate }}\n \n \n {{ \'tb.rulenode.start-interval-required\' | translate }}\n \n \n \n {{ \'tb.rulenode.end-interval\' | translate }}\n \n \n {{ \'tb.rulenode.end-interval-required\' | translate }}\n \n \n \n \n
\n
\n
\n
\n
tb.rulenode.fetch-strategy
\n
\n
\n \n \n {{ data.name }}\n \n \n
\n
\n {{ deduplicationStrategiesHintTranslations.get(getTelemetryFromDatabaseConfigForm.get(\'fetchMode\').value) | translate }}\n
\n
\n
\n \n {{ \'aggregation.function\' | translate }}\n \n \n {{ aggregationTypesTranslations.get(aggregationTypes[aggregation]) | translate }}\n \n \n \n
\n \n {{ "tb.rulenode.order-by-timestamp" | translate }} \n \n \n {{ samplingOrdersTranslate.get(order) | translate }}\n \n \n \n \n {{ "tb.rulenode.limit" | translate }}\n \n {{ "tb.rulenode.limit-hint" | translate }}\n \n {{ \'tb.rulenode.limit-required\' | translate }}\n \n \n {{ \'tb.rulenode.limit-range\' | translate }}\n \n \n {{ \'tb.rulenode.limit-range\' | translate }}\n \n \n
\n
\n
\n
\n',styles:[":host .see-example{display:inline-block}:host .description-block{display:flex;align-items:center;border-radius:6px;border:1px solid #EAEAEA}:host .description-block .description-icon{font-size:24px;height:24px;min-height:24px;width:24px;min-width:24px;line-height:24px;color:#d9d9d9;margin:4px}:host .description-block .description-text{font-size:12px;line-height:16px;letter-spacing:.25px;margin:6px}:host .description-block.error{color:var(--mdc-theme-error, #f44336)}:host .description-block.error .description-icon{color:var(--mdc-theme-error, #f44336)}:host .item-center{align-items:center}:host .item-center .fetch-mod-toggle{width:100%}:host .hint-container{width:100%}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n'],dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:Pe.HelpPopupComponent,selector:"[tb-help-popup], [tb-help-popup-content]",inputs:["tb-help-popup","tb-help-popup-content","trigger-text","trigger-style","tb-help-popup-placement","tb-help-popup-style","hintMode"]},{kind:"component",type:Ue.StringItemsListComponent,selector:"tb-string-items-list",inputs:["required","disabled","label","placeholder","hint","requiredText","floatLabel","appearance","editable","subscriptSizing","predefinedValues"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:z.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:se.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"component",type:J.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:E.FormGroupName,selector:"[formGroupName]",inputs:["formGroupName"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"directive",type:_e.ToggleOption,selector:"tb-toggle-option",inputs:["value"]},{kind:"component",type:ze.ToggleSelectComponent,selector:"tb-toggle-select",inputs:["disabled","selectMediaBreakpoint","appearance","disablePagination"]},{kind:"component",type:Y.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"component",type:gn,selector:"tb-example-hint",inputs:["hintText","popupHelpLink"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Gn,decorators:[{type:n,args:[{selector:"tb-enrichment-node-get-telemetry-from-database",template:'
\n \n
\n help\n \n
\n
\n
tb.rulenode.fetch-interval
\n
\n \n {{ \'tb.rulenode.use-metadata-dynamic-interval\' | translate }}\n \n
\n
\n
\n \n {{ \'tb.rulenode.interval-start\' | translate }}\n \n \n {{ \'tb.rulenode.start-interval-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n {{ \'tb.rulenode.time-unit\' | translate }}\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n \n {{ \'tb.rulenode.interval-end\' | translate }}\n \n \n {{ \'tb.rulenode.end-interval-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n {{ \'tb.rulenode.time-unit\' | translate }}\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n error_outline\n
\n \n {{ \'tb.rulenode.fetch-timeseries-from-to\' | translate:\n {\n startInterval: getTelemetryFromDatabaseConfigForm.get(\'interval.startInterval\').value,\n endInterval: getTelemetryFromDatabaseConfigForm.get(\'interval.endInterval\').value,\n startIntervalTimeUnit: getTelemetryFromDatabaseConfigForm.get(\'interval.startIntervalTimeUnit\').value.toLowerCase(),\n endIntervalTimeUnit: getTelemetryFromDatabaseConfigForm.get(\'interval.endIntervalTimeUnit\').value.toLowerCase()\n } }}\n \n \n {{ "tb.rulenode.fetch-timeseries-from-to-invalid" | translate }}\n \n
\n
\n
\n \n
\n \n {{ \'tb.rulenode.start-interval\' | translate }}\n \n \n {{ \'tb.rulenode.start-interval-required\' | translate }}\n \n \n \n {{ \'tb.rulenode.end-interval\' | translate }}\n \n \n {{ \'tb.rulenode.end-interval-required\' | translate }}\n \n \n \n \n
\n
\n
\n
\n
tb.rulenode.fetch-strategy
\n
\n
\n \n \n {{ data.name }}\n \n \n
\n
\n {{ deduplicationStrategiesHintTranslations.get(getTelemetryFromDatabaseConfigForm.get(\'fetchMode\').value) | translate }}\n
\n
\n
\n \n {{ \'aggregation.function\' | translate }}\n \n \n {{ aggregationTypesTranslations.get(aggregationTypes[aggregation]) | translate }}\n \n \n \n
\n \n {{ "tb.rulenode.order-by-timestamp" | translate }} \n \n \n {{ samplingOrdersTranslate.get(order) | translate }}\n \n \n \n \n {{ "tb.rulenode.limit" | translate }}\n \n {{ "tb.rulenode.limit-hint" | translate }}\n \n {{ \'tb.rulenode.limit-required\' | translate }}\n \n \n {{ \'tb.rulenode.limit-range\' | translate }}\n \n \n {{ \'tb.rulenode.limit-range\' | translate }}\n \n \n
\n
\n
\n
\n',styles:[":host .see-example{display:inline-block}:host .description-block{display:flex;align-items:center;border-radius:6px;border:1px solid #EAEAEA}:host .description-block .description-icon{font-size:24px;height:24px;min-height:24px;width:24px;min-width:24px;line-height:24px;color:#d9d9d9;margin:4px}:host .description-block .description-text{font-size:12px;line-height:16px;letter-spacing:.25px;margin:6px}:host .description-block.error{color:var(--mdc-theme-error, #f44336)}:host .description-block.error .description-icon{color:var(--mdc-theme-error, #f44336)}:host .item-center{align-items:center}:host .item-center .fetch-mod-toggle{width:100%}:host .hint-container{width:100%}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n']}]}],ctorParameters:function(){return[{type:G.Store},{type:j.TranslateService},{type:E.FormBuilder}]}});class En extends s{constructor(e,t,n){super(e),this.store=e,this.translate=t,this.fb=n}configForm(){return this.originatorAttributesConfigForm}onConfigurationSet(e){this.originatorAttributesConfigForm=this.fb.group({tellFailureIfAbsent:[e.tellFailureIfAbsent,[]],fetchTo:[e.fetchTo,[]],attributesControl:[e.attributesControl,[]]})}prepareInputConfig(e){return re(e)&&(e.attributesControl={clientAttributeNames:ee(e?.clientAttributeNames)?e.clientAttributeNames:null,latestTsKeyNames:ee(e?.latestTsKeyNames)?e.latestTsKeyNames:null,serverAttributeNames:ee(e?.serverAttributeNames)?e.serverAttributeNames:null,sharedAttributeNames:ee(e?.sharedAttributeNames)?e.sharedAttributeNames:null,getLatestValueWithTs:!!ee(e?.getLatestValueWithTs)&&e.getLatestValueWithTs}),{fetchTo:ee(e?.fetchTo)?e.fetchTo:Ut.METADATA,tellFailureIfAbsent:!!ee(e?.tellFailureIfAbsent)&&e.tellFailureIfAbsent,attributesControl:ee(e?.attributesControl)?e.attributesControl:null}}prepareOutputConfig(e){for(const t of Object.keys(e.attributesControl))e[t]=e.attributesControl[t];return delete e.attributesControl,e}}e("OriginatorAttributesConfigComponent",En),En.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:En,deps:[{token:G.Store},{token:j.TranslateService},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),En.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:En,selector:"tb-enrichment-node-originator-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n
\n
\n
tb.rulenode.originator-attributes
\n
\n tb.rulenode.at-least-one-field-required\n
\n
\n \n \n \n \n
\n
\n \n {{ \'tb.rulenode.tell-failure\' | translate }}\n \n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n'],dependencies:[{kind:"component",type:J.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Y.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"component",type:Cn,selector:"tb-msg-metadata-chip",inputs:["labelText"]},{kind:"component",type:Ln,selector:"tb-select-attributes",inputs:["popupHelpLink"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:En,decorators:[{type:n,args:[{selector:"tb-enrichment-node-originator-attributes-config",template:'
\n
\n
\n
tb.rulenode.originator-attributes
\n
\n tb.rulenode.at-least-one-field-required\n
\n
\n \n \n \n \n
\n
\n \n {{ \'tb.rulenode.tell-failure\' | translate }}\n \n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n']}]}],ctorParameters:function(){return[{type:G.Store},{type:j.TranslateService},{type:E.FormBuilder}]}});class Dn extends s{constructor(e,t,n){super(e),this.store=e,this.fb=t,this.translate=n,this.originatorFields=[];for(const e of pt)this.originatorFields.push({value:e.value,name:this.translate.instant(e.name)})}configForm(){return this.originatorFieldsConfigForm}prepareOutputConfig(e){return ne(e)}prepareInputConfig(e){return{dataMapping:ee(e?.dataMapping)?e.dataMapping:null,ignoreNullStrings:ee(e?.ignoreNullStrings)?e.ignoreNullStrings:null,fetchTo:ee(e?.fetchTo)?e.fetchTo:Ut.METADATA}}onConfigurationSet(e){this.originatorFieldsConfigForm=this.fb.group({dataMapping:[e.dataMapping,[D.required]],ignoreNullStrings:[e.ignoreNullStrings,[]],fetchTo:[e.fetchTo,[]]})}}e("OriginatorFieldsConfigComponent",Dn),Dn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Dn,deps:[{token:G.Store},{token:E.FormBuilder},{token:j.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Dn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Dn,selector:"tb-enrichment-node-originator-fields-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n
\n \n {{ \'tb.rulenode.skip-empty-fields\' | translate }}\n \n
\n
\n',dependencies:[{kind:"component",type:J.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:Y.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"component",type:Cn,selector:"tb-msg-metadata-chip",inputs:["labelText"]},{kind:"component",type:Fn,selector:"tb-sv-map-config",inputs:["selectOptions","disabled","labelText","requiredText","targetKeyPrefix","selectText","selectRequiredText","valText","valRequiredText","hintText","popupHelpLink","required"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Dn,decorators:[{type:n,args:[{selector:"tb-enrichment-node-originator-fields-config",template:'
\n \n \n \n \n
\n \n {{ \'tb.rulenode.skip-empty-fields\' | translate }}\n \n
\n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.FormBuilder},{type:j.TranslateService}]}});class Vn extends s{constructor(e,t,n){super(e),this.store=e,this.fb=t,this.translate=n,this.DataToFetch=Tt,this.msgMetadataLabelTranslations=Nt,this.originatorFields=[],this.fetchToData=[];for(const e of Object.keys(pt))this.originatorFields.push({value:pt[e].value,name:this.translate.instant(pt[e].name)});for(const e of It.keys())this.fetchToData.push({value:e,name:this.translate.instant(It.get(e))})}configForm(){return this.relatedAttributesConfigForm}prepareOutputConfig(e){e.dataToFetch===Tt.FIELDS?(e.dataMapping=e.svMap,delete e.svMap):(e.dataMapping=e.kvMap,delete e.kvMap);const t={};if(e&&e.dataMapping)for(const n of Object.keys(e.dataMapping))t[n.trim()]=e.dataMapping[n];return e.dataMapping=t,delete e.svMap,delete e.kvMap,ne(e)}prepareInputConfig(e){let t,n,r={[b.name.value]:`relatedEntity${this.translate.instant(b.name.name)}`},o={serialNumber:"sn"};return t=ee(e?.telemetry)?e.telemetry?Tt.LATEST_TELEMETRY:Tt.ATTRIBUTES:ee(e?.dataToFetch)?e.dataToFetch:Tt.ATTRIBUTES,n=ee(e?.attrMapping)?e.attrMapping:ee(e?.dataMapping)?e.dataMapping:null,t===Tt.FIELDS?r=n:o=n,{relationsQuery:ee(e?.relationsQuery)?e.relationsQuery:null,dataToFetch:t,svMap:r,kvMap:o,fetchTo:ee(e?.fetchTo)?e.fetchTo:Ut.METADATA}}selectTranslation(e,t){return this.relatedAttributesConfigForm.get("dataToFetch").value===Tt.LATEST_TELEMETRY?e:t}onConfigurationSet(e){this.relatedAttributesConfigForm=this.fb.group({relationsQuery:[e.relationsQuery,[D.required]],dataToFetch:[e.dataToFetch,[]],kvMap:[e.kvMap,[D.required]],svMap:[e.svMap,[D.required]],fetchTo:[e.fetchTo,[]]})}validatorTriggers(){return["dataToFetch"]}updateValidators(e){this.relatedAttributesConfigForm.get("dataToFetch").value===Tt.FIELDS?(this.relatedAttributesConfigForm.get("svMap").enable({emitEvent:!1}),this.relatedAttributesConfigForm.get("kvMap").disable({emitEvent:!1}),this.relatedAttributesConfigForm.get("svMap").updateValueAndValidity()):(this.relatedAttributesConfigForm.get("svMap").disable({emitEvent:!1}),this.relatedAttributesConfigForm.get("kvMap").enable({emitEvent:!1}),this.relatedAttributesConfigForm.get("kvMap").updateValueAndValidity())}}e("RelatedAttributesConfigComponent",Vn),Vn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Vn,deps:[{token:G.Store},{token:E.FormBuilder},{token:j.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Vn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Vn,selector:"tb-enrichment-node-related-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n
\n
tb.rulenode.data-to-fetch
\n \n \n {{ data.name }}\n \n \n \n \n \n \n \n \n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n'],dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"directive",type:_e.ToggleOption,selector:"tb-toggle-option",inputs:["value"]},{kind:"component",type:ze.ToggleSelectComponent,selector:"tb-toggle-select",inputs:["disabled","selectMediaBreakpoint","appearance","disablePagination"]},{kind:"component",type:yn,selector:"tb-kv-map-config",inputs:["disabled","uniqueKeyValuePairValidator","labelText","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","popupHelpLink","required"]},{kind:"component",type:bn,selector:"tb-relations-query-config",inputs:["disabled","required"]},{kind:"component",type:Cn,selector:"tb-msg-metadata-chip",inputs:["labelText"]},{kind:"component",type:Fn,selector:"tb-sv-map-config",inputs:["selectOptions","disabled","labelText","requiredText","targetKeyPrefix","selectText","selectRequiredText","valText","valRequiredText","hintText","popupHelpLink","required"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Vn,decorators:[{type:n,args:[{selector:"tb-enrichment-node-related-attributes-config",template:'
\n \n \n
\n
tb.rulenode.data-to-fetch
\n \n \n {{ data.name }}\n \n \n \n \n \n \n \n \n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n']}]}],ctorParameters:function(){return[{type:G.Store},{type:E.FormBuilder},{type:j.TranslateService}]}});class Pn extends s{constructor(e,t,n){super(e),this.store=e,this.fb=t,this.translate=n,this.fetchToData=[],this.DataToFetch=Tt;for(const e of It.keys())e!==Tt.FIELDS&&this.fetchToData.push({value:e,name:this.translate.instant(It.get(e))})}configForm(){return this.tenantAttributesConfigForm}prepareInputConfig(e){let t,n;return t=ee(e?.telemetry)?e.telemetry?Tt.LATEST_TELEMETRY:Tt.ATTRIBUTES:ee(e?.dataToFetch)?e.dataToFetch:Tt.ATTRIBUTES,n=ee(e?.attrMapping)?e.attrMapping:ee(e?.dataMapping)?e.dataMapping:null,{dataToFetch:t,dataMapping:n,fetchTo:ee(e?.fetchTo)?e.fetchTo:Ut.METADATA}}selectTranslation(e,t){return this.tenantAttributesConfigForm.get("dataToFetch").value===Tt.LATEST_TELEMETRY?e:t}onConfigurationSet(e){this.tenantAttributesConfigForm=this.fb.group({dataToFetch:[e.dataToFetch,[]],dataMapping:[e.dataMapping,[D.required]],fetchTo:[e.fetchTo,[]]})}}e("TenantAttributesConfigComponent",Pn),Pn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Pn,deps:[{token:G.Store},{token:E.FormBuilder},{token:j.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Pn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Pn,selector:"tb-enrichment-node-tenant-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n
tb.rulenode.mapping-of-tenant
\n
\n
\n \n \n {{ data.name }}\n \n \n
\n
\n \n \n \n \n
\n',styles:[":host .fetch-to-data-toggle{max-width:420px;width:100%}\n"],dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.DefaultLayoutAlignDirective,selector:" [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg]",inputs:["fxLayoutAlign","fxLayoutAlign.xs","fxLayoutAlign.sm","fxLayoutAlign.md","fxLayoutAlign.lg","fxLayoutAlign.xl","fxLayoutAlign.lt-sm","fxLayoutAlign.lt-md","fxLayoutAlign.lt-lg","fxLayoutAlign.lt-xl","fxLayoutAlign.gt-xs","fxLayoutAlign.gt-sm","fxLayoutAlign.gt-md","fxLayoutAlign.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"directive",type:_e.ToggleOption,selector:"tb-toggle-option",inputs:["value"]},{kind:"component",type:ze.ToggleSelectComponent,selector:"tb-toggle-select",inputs:["disabled","selectMediaBreakpoint","appearance","disablePagination"]},{kind:"component",type:yn,selector:"tb-kv-map-config",inputs:["disabled","uniqueKeyValuePairValidator","labelText","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","popupHelpLink","required"]},{kind:"component",type:Cn,selector:"tb-msg-metadata-chip",inputs:["labelText"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Pn,decorators:[{type:n,args:[{selector:"tb-enrichment-node-tenant-attributes-config",template:'
\n
tb.rulenode.mapping-of-tenant
\n
\n
\n \n \n {{ data.name }}\n \n \n
\n
\n \n \n \n \n
\n',styles:[":host .fetch-to-data-toggle{max-width:420px;width:100%}\n"]}]}],ctorParameters:function(){return[{type:G.Store},{type:E.FormBuilder},{type:j.TranslateService}]}});class wn extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.fetchDeviceCredentialsConfigForm}prepareInputConfig(e){return{fetchTo:ee(e?.fetchTo)?e.fetchTo:Ut.METADATA}}onConfigurationSet(e){this.fetchDeviceCredentialsConfigForm=this.fb.group({fetchTo:[e.fetchTo,[]]})}}e("FetchDeviceCredentialsConfigComponent",wn),wn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:wn,deps:[{token:G.Store},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),wn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:wn,selector:"./tb-enrichment-node-fetch-device-credentials-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n
\n',dependencies:[{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:Cn,selector:"tb-msg-metadata-chip",inputs:["labelText"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:wn,decorators:[{type:n,args:[{selector:"./tb-enrichment-node-fetch-device-credentials-config",template:'
\n \n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.FormBuilder}]}});class Rn{}e("RulenodeCoreConfigEnrichmentModule",Rn),Rn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Rn,deps:[],target:t.ɵɵFactoryTarget.NgModule}),Rn.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.10",ngImport:t,type:Rn,declarations:[qn,Mn,An,En,Dn,Gn,Vn,Pn,Sn,wn],imports:[K,I,In],exports:[qn,Mn,An,En,Dn,Gn,Vn,Pn,Sn,wn]}),Rn.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Rn,imports:[K,I,In]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Rn,decorators:[{type:l,args:[{declarations:[qn,Mn,An,En,Dn,Gn,Vn,Pn,Sn,wn],imports:[K,I,In],exports:[qn,Mn,An,En,Dn,Gn,Vn,Pn,Sn,wn]}]}]});class On extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.allAzureIotHubCredentialsTypes=Et,this.azureIotHubCredentialsTypeTranslationsMap=Dt}configForm(){return this.azureIotHubConfigForm}onConfigurationSet(e){this.azureIotHubConfigForm=this.fb.group({topicPattern:[e?e.topicPattern:null,[D.required]],host:[e?e.host:null,[D.required]],port:[e?e.port:null,[D.required,D.min(1),D.max(65535)]],connectTimeoutSec:[e?e.connectTimeoutSec:null,[D.required,D.min(1),D.max(200)]],clientId:[e?e.clientId:null,[D.required]],cleanSession:[!!e&&e.cleanSession,[]],ssl:[!!e&&e.ssl,[]],credentials:this.fb.group({type:[e&&e.credentials?e.credentials.type:null,[D.required]],sasKey:[e&&e.credentials?e.credentials.sasKey:null,[]],caCert:[e&&e.credentials?e.credentials.caCert:null,[]],caCertFileName:[e&&e.credentials?e.credentials.caCertFileName:null,[]],privateKey:[e&&e.credentials?e.credentials.privateKey:null,[]],privateKeyFileName:[e&&e.credentials?e.credentials.privateKeyFileName:null,[]],cert:[e&&e.credentials?e.credentials.cert:null,[]],certFileName:[e&&e.credentials?e.credentials.certFileName:null,[]],password:[e&&e.credentials?e.credentials.password:null,[]]})})}prepareOutputConfig(e){const t=e.credentials.type;return"sas"===t&&(e.credentials={type:t,sasKey:e.credentials.sasKey,caCert:e.credentials.caCert,caCertFileName:e.credentials.caCertFileName}),e}validatorTriggers(){return["credentials.type"]}updateValidators(e){const t=this.azureIotHubConfigForm.get("credentials"),n=t.get("type").value;switch(e&&t.reset({type:n},{emitEvent:!1}),t.get("sasKey").setValidators([]),t.get("privateKey").setValidators([]),t.get("privateKeyFileName").setValidators([]),t.get("cert").setValidators([]),t.get("certFileName").setValidators([]),n){case"sas":t.get("sasKey").setValidators([D.required]);break;case"cert.PEM":t.get("privateKey").setValidators([D.required]),t.get("privateKeyFileName").setValidators([D.required]),t.get("cert").setValidators([D.required]),t.get("certFileName").setValidators([D.required])}t.get("sasKey").updateValueAndValidity({emitEvent:e}),t.get("privateKey").updateValueAndValidity({emitEvent:e}),t.get("privateKeyFileName").updateValueAndValidity({emitEvent:e}),t.get("cert").updateValueAndValidity({emitEvent:e}),t.get("certFileName").updateValueAndValidity({emitEvent:e})}}e("AzureIotHubConfigComponent",On),On.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:On,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),On.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:On,selector:"tb-external-node-azure-iot-hub-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.topic\n \n \n {{ \'tb.rulenode.topic-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.hostname\n \n \n {{ \'tb.rulenode.hostname-required\' | translate }}\n \n \n \n tb.rulenode.device-id\n \n \n {{ \'tb.rulenode.device-id-required\' | translate }}\n \n \n \n \n \n tb.rulenode.credentials\n \n {{ azureIotHubCredentialsTypeTranslationsMap.get(azureIotHubConfigForm.get(\'credentials.type\').value) | translate }}\n \n \n
\n \n tb.rulenode.credentials-type\n \n \n {{ azureIotHubCredentialsTypeTranslationsMap.get(credentialsType) | translate }}\n \n \n \n {{ \'tb.rulenode.credentials-type-required\' | translate }}\n \n \n
\n \n \n \n \n tb.rulenode.sas-key\n \n \n \n {{ \'tb.rulenode.sas-key-required\' | translate }}\n \n \n \n \n \n \n \n \n \n \n \n \n \n tb.rulenode.private-key-password\n \n \n \n \n
\n
\n
\n
\n
\n',styles:[":host .tb-mqtt-credentials-panel-group{margin:0 6px}\n"],dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:B.NgSwitch,selector:"[ngSwitch]",inputs:["ngSwitch"]},{kind:"directive",type:B.NgSwitchCase,selector:"[ngSwitchCase]",inputs:["ngSwitchCase"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:z.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:Ae.MatAccordion,selector:"mat-accordion",inputs:["multi","hideToggle","displayMode","togglePosition"],exportAs:["matAccordion"]},{kind:"component",type:Ae.MatExpansionPanel,selector:"mat-expansion-panel",inputs:["disabled","expanded","hideToggle","togglePosition"],outputs:["opened","closed","expandedChange","afterExpand","afterCollapse"],exportAs:["matExpansionPanel"]},{kind:"component",type:Ae.MatExpansionPanelHeader,selector:"mat-expansion-panel-header",inputs:["tabIndex","expandedHeight","collapsedHeight"]},{kind:"directive",type:Ae.MatExpansionPanelTitle,selector:"mat-panel-title"},{kind:"directive",type:Ae.MatExpansionPanelDescription,selector:"mat-panel-description"},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:E.FormGroupName,selector:"[formGroupName]",inputs:["formGroupName"]},{kind:"component",type:Be.FileInputComponent,selector:"tb-file-input",inputs:["label","hint","accept","noFileText","inputId","allowedExtensions","dropLabel","contentConvertFunction","required","requiredAsError","disabled","existingFileName","readAsBinary","workFromFileObj","multipleFile"],outputs:["fileNameChanged"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Ke.TogglePasswordComponent,selector:"tb-toggle-password"},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:On,decorators:[{type:n,args:[{selector:"tb-external-node-azure-iot-hub-config",template:'
\n \n tb.rulenode.topic\n \n \n {{ \'tb.rulenode.topic-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.hostname\n \n \n {{ \'tb.rulenode.hostname-required\' | translate }}\n \n \n \n tb.rulenode.device-id\n \n \n {{ \'tb.rulenode.device-id-required\' | translate }}\n \n \n \n \n \n tb.rulenode.credentials\n \n {{ azureIotHubCredentialsTypeTranslationsMap.get(azureIotHubConfigForm.get(\'credentials.type\').value) | translate }}\n \n \n
\n \n tb.rulenode.credentials-type\n \n \n {{ azureIotHubCredentialsTypeTranslationsMap.get(credentialsType) | translate }}\n \n \n \n {{ \'tb.rulenode.credentials-type-required\' | translate }}\n \n \n
\n \n \n \n \n tb.rulenode.sas-key\n \n \n \n {{ \'tb.rulenode.sas-key-required\' | translate }}\n \n \n \n \n \n \n \n \n \n \n \n \n \n tb.rulenode.private-key-password\n \n \n \n \n
\n
\n
\n
\n
\n',styles:[":host .tb-mqtt-credentials-panel-group{margin:0 6px}\n"]}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class Bn extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.ackValues=["all","-1","0","1"],this.ToByteStandartCharsetTypesValues=Pt,this.ToByteStandartCharsetTypeTranslationMap=wt}configForm(){return this.kafkaConfigForm}onConfigurationSet(e){this.kafkaConfigForm=this.fb.group({topicPattern:[e?e.topicPattern:null,[D.required]],keyPattern:[e?e.keyPattern:null],bootstrapServers:[e?e.bootstrapServers:null,[D.required]],retries:[e?e.retries:null,[D.min(0)]],batchSize:[e?e.batchSize:null,[D.min(0)]],linger:[e?e.linger:null,[D.min(0)]],bufferMemory:[e?e.bufferMemory:null,[D.min(0)]],acks:[e?e.acks:null,[D.required]],keySerializer:[e?e.keySerializer:null,[D.required]],valueSerializer:[e?e.valueSerializer:null,[D.required]],otherProperties:[e?e.otherProperties:null,[]],addMetadataKeyValuesAsKafkaHeaders:[!!e&&e.addMetadataKeyValuesAsKafkaHeaders,[]],kafkaHeadersCharset:[e?e.kafkaHeadersCharset:null,[]]})}validatorTriggers(){return["addMetadataKeyValuesAsKafkaHeaders"]}updateValidators(e){this.kafkaConfigForm.get("addMetadataKeyValuesAsKafkaHeaders").value?this.kafkaConfigForm.get("kafkaHeadersCharset").setValidators([D.required]):this.kafkaConfigForm.get("kafkaHeadersCharset").setValidators([]),this.kafkaConfigForm.get("kafkaHeadersCharset").updateValueAndValidity({emitEvent:e})}}e("KafkaConfigComponent",Bn),Bn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Bn,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Bn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Bn,selector:"tb-external-node-kafka-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.topic-pattern\n \n \n {{ \'tb.rulenode.topic-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.key-pattern\n \n tb.rulenode.general-pattern-hint\n \n
tb.rulenode.key-pattern-hint
\n \n tb.rulenode.bootstrap-servers\n \n \n {{ \'tb.rulenode.bootstrap-servers-required\' | translate }}\n \n \n \n tb.rulenode.retries\n \n \n {{ \'tb.rulenode.min-retries-message\' | translate }}\n \n \n \n tb.rulenode.batch-size-bytes\n \n \n {{ \'tb.rulenode.min-batch-size-bytes-message\' | translate }}\n \n \n \n tb.rulenode.linger-ms\n \n \n {{ \'tb.rulenode.min-linger-ms-message\' | translate }}\n \n \n \n tb.rulenode.buffer-memory-bytes\n \n \n {{ \'tb.rulenode.min-buffer-memory-bytes-message\' | translate }}\n \n \n \n tb.rulenode.acks\n \n \n {{ ackValue }}\n \n \n \n \n tb.rulenode.key-serializer\n \n \n {{ \'tb.rulenode.key-serializer-required\' | translate }}\n \n \n \n tb.rulenode.value-serializer\n \n \n {{ \'tb.rulenode.value-serializer-required\' | translate }}\n \n \n \n \n \n \n {{ \'tb.rulenode.add-metadata-key-values-as-kafka-headers\' | translate }}\n \n
tb.rulenode.add-metadata-key-values-as-kafka-headers-hint
\n \n tb.rulenode.charset-encoding\n \n \n {{ ToByteStandartCharsetTypeTranslationMap.get(charset) | translate }}\n \n \n \n
\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:U.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:on,selector:"tb-kv-map-config-old",inputs:["disabled","uniqueKeyValuePairValidator","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Bn,decorators:[{type:n,args:[{selector:"tb-external-node-kafka-config",template:'
\n \n tb.rulenode.topic-pattern\n \n \n {{ \'tb.rulenode.topic-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.key-pattern\n \n tb.rulenode.general-pattern-hint\n \n
tb.rulenode.key-pattern-hint
\n \n tb.rulenode.bootstrap-servers\n \n \n {{ \'tb.rulenode.bootstrap-servers-required\' | translate }}\n \n \n \n tb.rulenode.retries\n \n \n {{ \'tb.rulenode.min-retries-message\' | translate }}\n \n \n \n tb.rulenode.batch-size-bytes\n \n \n {{ \'tb.rulenode.min-batch-size-bytes-message\' | translate }}\n \n \n \n tb.rulenode.linger-ms\n \n \n {{ \'tb.rulenode.min-linger-ms-message\' | translate }}\n \n \n \n tb.rulenode.buffer-memory-bytes\n \n \n {{ \'tb.rulenode.min-buffer-memory-bytes-message\' | translate }}\n \n \n \n tb.rulenode.acks\n \n \n {{ ackValue }}\n \n \n \n \n tb.rulenode.key-serializer\n \n \n {{ \'tb.rulenode.key-serializer-required\' | translate }}\n \n \n \n tb.rulenode.value-serializer\n \n \n {{ \'tb.rulenode.value-serializer-required\' | translate }}\n \n \n \n \n \n \n {{ \'tb.rulenode.add-metadata-key-values-as-kafka-headers\' | translate }}\n \n
tb.rulenode.add-metadata-key-values-as-kafka-headers-hint
\n \n tb.rulenode.charset-encoding\n \n \n {{ ToByteStandartCharsetTypeTranslationMap.get(charset) | translate }}\n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class Kn extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.mqttConfigForm}onConfigurationSet(e){this.mqttConfigForm=this.fb.group({topicPattern:[e?e.topicPattern:null,[D.required]],host:[e?e.host:null,[D.required]],port:[e?e.port:null,[D.required,D.min(1),D.max(65535)]],connectTimeoutSec:[e?e.connectTimeoutSec:null,[D.required,D.min(1),D.max(200)]],clientId:[e?e.clientId:null,[]],appendClientIdSuffix:[{value:!!e&&e.appendClientIdSuffix,disabled:!(e&&oe(e.clientId))},[]],cleanSession:[!!e&&e.cleanSession,[]],retainedMessage:[!!e&&e.retainedMessage,[]],ssl:[!!e&&e.ssl,[]],credentials:[e?e.credentials:null,[]]})}updateValidators(e){oe(this.mqttConfigForm.get("clientId").value)?this.mqttConfigForm.get("appendClientIdSuffix").enable({emitEvent:!1}):this.mqttConfigForm.get("appendClientIdSuffix").disable({emitEvent:!1}),this.mqttConfigForm.get("appendClientIdSuffix").updateValueAndValidity({emitEvent:e})}validatorTriggers(){return["clientId"]}}e("MqttConfigComponent",Kn),Kn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Kn,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Kn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Kn,selector:"tb-external-node-mqtt-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.topic-pattern\n \n \n {{ \'tb.rulenode.topic-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n \n tb.rulenode.host\n \n \n {{ \'tb.rulenode.host-required\' | translate }}\n \n \n \n tb.rulenode.port\n \n \n {{ \'tb.rulenode.port-required\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n \n tb.rulenode.connect-timeout\n \n \n {{ \'tb.rulenode.connect-timeout-required\' | translate }}\n \n \n {{ \'tb.rulenode.connect-timeout-range\' | translate }}\n \n \n {{ \'tb.rulenode.connect-timeout-range\' | translate }}\n \n \n
\n \n tb.rulenode.client-id\n \n {{\'tb.rulenode.client-id-hint\' | translate}}\n \n \n {{ \'tb.rulenode.append-client-id-suffix\' | translate }}\n \n
{{ "tb.rulenode.client-id-suffix-hint" | translate }}
\n \n {{ \'tb.rulenode.clean-session\' | translate }}\n \n \n {{ "tb.rulenode.retained-message" | translate }}\n \n \n {{ \'tb.rulenode.enable-ssl\' | translate }}\n \n \n
\n',styles:[":host .tb-mqtt-credentials-panel-group{margin:0 6px}\n"],dependencies:[{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:U.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:vn,selector:"tb-credentials-config",inputs:["required","disableCertPemCredentials","passwordFieldRequired"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Kn,decorators:[{type:n,args:[{selector:"tb-external-node-mqtt-config",template:'
\n \n tb.rulenode.topic-pattern\n \n \n {{ \'tb.rulenode.topic-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n \n tb.rulenode.host\n \n \n {{ \'tb.rulenode.host-required\' | translate }}\n \n \n \n tb.rulenode.port\n \n \n {{ \'tb.rulenode.port-required\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n \n tb.rulenode.connect-timeout\n \n \n {{ \'tb.rulenode.connect-timeout-required\' | translate }}\n \n \n {{ \'tb.rulenode.connect-timeout-range\' | translate }}\n \n \n {{ \'tb.rulenode.connect-timeout-range\' | translate }}\n \n \n
\n \n tb.rulenode.client-id\n \n {{\'tb.rulenode.client-id-hint\' | translate}}\n \n \n {{ \'tb.rulenode.append-client-id-suffix\' | translate }}\n \n
{{ "tb.rulenode.client-id-suffix-hint" | translate }}
\n \n {{ \'tb.rulenode.clean-session\' | translate }}\n \n \n {{ "tb.rulenode.retained-message" | translate }}\n \n \n {{ \'tb.rulenode.enable-ssl\' | translate }}\n \n \n
\n',styles:[":host .tb-mqtt-credentials-panel-group{margin:0 6px}\n"]}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class Un extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.notificationType=q,this.entityType=x}configForm(){return this.notificationConfigForm}onConfigurationSet(e){this.notificationConfigForm=this.fb.group({templateId:[e?e.templateId:null,[D.required]],targets:[e?e.targets:[],[D.required]]})}}e("NotificationConfigComponent",Un),Un.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Un,deps:[{token:G.Store},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Un.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Un,selector:"tb-external-node-notification-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n
\n',dependencies:[{kind:"component",type:He.EntityListComponent,selector:"tb-entity-list",inputs:["entityType","subType","labelText","placeholderText","requiredText","required","disabled","subscriptSizing","hint"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:je.TemplateAutocompleteComponent,selector:"tb-template-autocomplete",inputs:["required","allowCreate","allowEdit","disabled","notificationTypes"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Un,decorators:[{type:n,args:[{selector:"tb-external-node-notification-config",template:'
\n \n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.FormBuilder}]}});class _n extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.pubSubConfigForm}onConfigurationSet(e){this.pubSubConfigForm=this.fb.group({projectId:[e?e.projectId:null,[D.required]],topicName:[e?e.topicName:null,[D.required]],serviceAccountKey:[e?e.serviceAccountKey:null,[D.required]],serviceAccountKeyFileName:[e?e.serviceAccountKeyFileName:null,[D.required]],messageAttributes:[e?e.messageAttributes:null,[]]})}}e("PubSubConfigComponent",_n),_n.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:_n,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),_n.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:_n,selector:"tb-external-node-pub-sub-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.gcp-project-id\n \n \n {{ \'tb.rulenode.gcp-project-id-required\' | translate }}\n \n \n \n tb.rulenode.pubsub-topic-name\n \n \n {{ \'tb.rulenode.pubsub-topic-name-required\' | translate }}\n \n \n \n \n \n
\n \n \n
\n',dependencies:[{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:Be.FileInputComponent,selector:"tb-file-input",inputs:["label","hint","accept","noFileText","inputId","allowedExtensions","dropLabel","contentConvertFunction","required","requiredAsError","disabled","existingFileName","readAsBinary","workFromFileObj","multipleFile"],outputs:["fileNameChanged"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:on,selector:"tb-kv-map-config-old",inputs:["disabled","uniqueKeyValuePairValidator","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{kind:"pipe",type:fe.SafePipe,name:"safe"},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:_n,decorators:[{type:n,args:[{selector:"tb-external-node-pub-sub-config",template:'
\n \n tb.rulenode.gcp-project-id\n \n \n {{ \'tb.rulenode.gcp-project-id-required\' | translate }}\n \n \n \n tb.rulenode.pubsub-topic-name\n \n \n {{ \'tb.rulenode.pubsub-topic-name-required\' | translate }}\n \n \n \n \n \n
\n \n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class zn extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.messageProperties=[null,"BASIC","TEXT_PLAIN","MINIMAL_BASIC","MINIMAL_PERSISTENT_BASIC","PERSISTENT_BASIC","PERSISTENT_TEXT_PLAIN"]}configForm(){return this.rabbitMqConfigForm}onConfigurationSet(e){this.rabbitMqConfigForm=this.fb.group({exchangeNamePattern:[e?e.exchangeNamePattern:null,[]],routingKeyPattern:[e?e.routingKeyPattern:null,[]],messageProperties:[e?e.messageProperties:null,[]],host:[e?e.host:null,[D.required]],port:[e?e.port:null,[D.required,D.min(1),D.max(65535)]],virtualHost:[e?e.virtualHost:null,[]],username:[e?e.username:null,[]],password:[e?e.password:null,[]],automaticRecoveryEnabled:[!!e&&e.automaticRecoveryEnabled,[]],connectionTimeout:[e?e.connectionTimeout:null,[D.min(0)]],handshakeTimeout:[e?e.handshakeTimeout:null,[D.min(0)]],clientProperties:[e?e.clientProperties:null,[]]})}}e("RabbitMqConfigComponent",zn),zn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:zn,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),zn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:zn,selector:"tb-external-node-rabbit-mq-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.exchange-name-pattern\n \n \n \n tb.rulenode.routing-key-pattern\n \n \n \n tb.rulenode.message-properties\n \n \n {{ property }}\n \n \n \n
\n \n tb.rulenode.host\n \n \n {{ \'tb.rulenode.host-required\' | translate }}\n \n \n \n tb.rulenode.port\n \n \n {{ \'tb.rulenode.port-required\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n
\n \n tb.rulenode.virtual-host\n \n \n \n tb.rulenode.username\n \n \n \n tb.rulenode.password\n \n \n \n \n {{ \'tb.rulenode.automatic-recovery\' | translate }}\n \n \n tb.rulenode.connection-timeout-ms\n \n \n {{ \'tb.rulenode.min-connection-timeout-ms-message\' | translate }}\n \n \n \n tb.rulenode.handshake-timeout-ms\n \n \n {{ \'tb.rulenode.min-handshake-timeout-ms-message\' | translate }}\n \n \n \n \n \n
\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:U.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:z.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Ke.TogglePasswordComponent,selector:"tb-toggle-password"},{kind:"component",type:on,selector:"tb-kv-map-config-old",inputs:["disabled","uniqueKeyValuePairValidator","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:zn,decorators:[{type:n,args:[{selector:"tb-external-node-rabbit-mq-config",template:'
\n \n tb.rulenode.exchange-name-pattern\n \n \n \n tb.rulenode.routing-key-pattern\n \n \n \n tb.rulenode.message-properties\n \n \n {{ property }}\n \n \n \n
\n \n tb.rulenode.host\n \n \n {{ \'tb.rulenode.host-required\' | translate }}\n \n \n \n tb.rulenode.port\n \n \n {{ \'tb.rulenode.port-required\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n
\n \n tb.rulenode.virtual-host\n \n \n \n tb.rulenode.username\n \n \n \n tb.rulenode.password\n \n \n \n \n {{ \'tb.rulenode.automatic-recovery\' | translate }}\n \n \n tb.rulenode.connection-timeout-ms\n \n \n {{ \'tb.rulenode.min-connection-timeout-ms-message\' | translate }}\n \n \n \n tb.rulenode.handshake-timeout-ms\n \n \n {{ \'tb.rulenode.min-handshake-timeout-ms-message\' | translate }}\n \n \n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class Hn extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.proxySchemes=["http","https"],this.httpRequestTypes=Object.keys(Vt)}configForm(){return this.restApiCallConfigForm}onConfigurationSet(e){this.restApiCallConfigForm=this.fb.group({restEndpointUrlPattern:[e?e.restEndpointUrlPattern:null,[D.required]],requestMethod:[e?e.requestMethod:null,[D.required]],useSimpleClientHttpFactory:[!!e&&e.useSimpleClientHttpFactory,[]],parseToPlainText:[!!e&&e.parseToPlainText,[]],ignoreRequestBody:[!!e&&e.ignoreRequestBody,[]],enableProxy:[!!e&&e.enableProxy,[]],useSystemProxyProperties:[!!e&&e.enableProxy,[]],proxyScheme:[e?e.proxyHost:null,[]],proxyHost:[e?e.proxyHost:null,[]],proxyPort:[e?e.proxyPort:null,[]],proxyUser:[e?e.proxyUser:null,[]],proxyPassword:[e?e.proxyPassword:null,[]],readTimeoutMs:[e?e.readTimeoutMs:null,[]],maxParallelRequestsCount:[e?e.maxParallelRequestsCount:null,[D.min(0)]],headers:[e?e.headers:null,[]],useRedisQueueForMsgPersistence:[!!e&&e.useRedisQueueForMsgPersistence,[]],trimQueue:[!!e&&e.trimQueue,[]],maxQueueSize:[e?e.maxQueueSize:null,[]],credentials:[e?e.credentials:null,[]]})}validatorTriggers(){return["useSimpleClientHttpFactory","useRedisQueueForMsgPersistence","enableProxy","useSystemProxyProperties"]}updateValidators(e){const t=this.restApiCallConfigForm.get("useSimpleClientHttpFactory").value,n=this.restApiCallConfigForm.get("useRedisQueueForMsgPersistence").value,r=this.restApiCallConfigForm.get("enableProxy").value,o=this.restApiCallConfigForm.get("useSystemProxyProperties").value;r&&!o?(this.restApiCallConfigForm.get("proxyHost").setValidators(r?[D.required]:[]),this.restApiCallConfigForm.get("proxyPort").setValidators(r?[D.required,D.min(1),D.max(65535)]:[])):(this.restApiCallConfigForm.get("proxyHost").setValidators([]),this.restApiCallConfigForm.get("proxyPort").setValidators([]),t?this.restApiCallConfigForm.get("readTimeoutMs").setValidators([]):this.restApiCallConfigForm.get("readTimeoutMs").setValidators([D.min(0)])),n?this.restApiCallConfigForm.get("maxQueueSize").setValidators([D.min(0)]):this.restApiCallConfigForm.get("maxQueueSize").setValidators([]),this.restApiCallConfigForm.get("readTimeoutMs").updateValueAndValidity({emitEvent:e}),this.restApiCallConfigForm.get("maxQueueSize").updateValueAndValidity({emitEvent:e}),this.restApiCallConfigForm.get("proxyHost").updateValueAndValidity({emitEvent:e}),this.restApiCallConfigForm.get("proxyPort").updateValueAndValidity({emitEvent:e}),this.restApiCallConfigForm.get("credentials").updateValueAndValidity({emitEvent:e})}}e("RestApiCallConfigComponent",Hn),Hn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Hn,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Hn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Hn,selector:"tb-external-node-rest-api-call-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.endpoint-url-pattern\n \n \n {{ \'tb.rulenode.endpoint-url-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.request-method\n \n \n {{ requestType }}\n \n \n \n \n {{ \'tb.rulenode.enable-proxy\' | translate }}\n \n \n {{ \'tb.rulenode.use-simple-client-http-factory\' | translate }}\n \n \n {{ \'tb.rulenode.parse-to-plain-text\' | translate }}\n \n
tb.rulenode.parse-to-plain-text-hint
\n \n {{ \'tb.rulenode.ignore-request-body\' | translate }}\n \n
\n \n {{ \'tb.rulenode.use-system-proxy-properties\' | translate }}\n \n
\n
\n \n tb.rulenode.proxy-scheme\n \n \n {{ proxyScheme }}\n \n \n \n \n tb.rulenode.proxy-host\n \n \n {{ \'tb.rulenode.proxy-host-required\' | translate }}\n \n \n \n tb.rulenode.proxy-port\n \n \n {{ \'tb.rulenode.proxy-port-required\' | translate }}\n \n \n {{ \'tb.rulenode.proxy-port-range\' | translate }}\n \n \n
\n \n tb.rulenode.proxy-user\n \n \n \n tb.rulenode.proxy-password\n \n \n
\n
\n \n tb.rulenode.read-timeout\n \n tb.rulenode.read-timeout-hint\n \n \n tb.rulenode.max-parallel-requests-count\n \n tb.rulenode.max-parallel-requests-count-hint\n \n \n
\n \n \n \n {{ \'tb.rulenode.use-redis-queue\' | translate }}\n \n
\n \n {{ \'tb.rulenode.trim-redis-queue\' | translate }}\n \n \n tb.rulenode.redis-queue-max-size\n \n \n
\n \n
\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:U.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:vn,selector:"tb-credentials-config",inputs:["required","disableCertPemCredentials","passwordFieldRequired"]},{kind:"component",type:on,selector:"tb-kv-map-config-old",inputs:["disabled","uniqueKeyValuePairValidator","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{kind:"pipe",type:fe.SafePipe,name:"safe"},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Hn,decorators:[{type:n,args:[{selector:"tb-external-node-rest-api-call-config",template:'
\n \n tb.rulenode.endpoint-url-pattern\n \n \n {{ \'tb.rulenode.endpoint-url-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.request-method\n \n \n {{ requestType }}\n \n \n \n \n {{ \'tb.rulenode.enable-proxy\' | translate }}\n \n \n {{ \'tb.rulenode.use-simple-client-http-factory\' | translate }}\n \n \n {{ \'tb.rulenode.parse-to-plain-text\' | translate }}\n \n
tb.rulenode.parse-to-plain-text-hint
\n \n {{ \'tb.rulenode.ignore-request-body\' | translate }}\n \n
\n \n {{ \'tb.rulenode.use-system-proxy-properties\' | translate }}\n \n
\n
\n \n tb.rulenode.proxy-scheme\n \n \n {{ proxyScheme }}\n \n \n \n \n tb.rulenode.proxy-host\n \n \n {{ \'tb.rulenode.proxy-host-required\' | translate }}\n \n \n \n tb.rulenode.proxy-port\n \n \n {{ \'tb.rulenode.proxy-port-required\' | translate }}\n \n \n {{ \'tb.rulenode.proxy-port-range\' | translate }}\n \n \n
\n \n tb.rulenode.proxy-user\n \n \n \n tb.rulenode.proxy-password\n \n \n
\n
\n \n tb.rulenode.read-timeout\n \n tb.rulenode.read-timeout-hint\n \n \n tb.rulenode.max-parallel-requests-count\n \n tb.rulenode.max-parallel-requests-count-hint\n \n \n
\n \n \n \n {{ \'tb.rulenode.use-redis-queue\' | translate }}\n \n
\n \n {{ \'tb.rulenode.trim-redis-queue\' | translate }}\n \n \n tb.rulenode.redis-queue-max-size\n \n \n
\n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class jn extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.smtpProtocols=["smtp","smtps"],this.tlsVersions=["TLSv1","TLSv1.1","TLSv1.2","TLSv1.3"]}configForm(){return this.sendEmailConfigForm}onConfigurationSet(e){this.sendEmailConfigForm=this.fb.group({useSystemSmtpSettings:[!!e&&e.useSystemSmtpSettings,[]],smtpProtocol:[e?e.smtpProtocol:null,[]],smtpHost:[e?e.smtpHost:null,[]],smtpPort:[e?e.smtpPort:null,[]],timeout:[e?e.timeout:null,[]],enableTls:[!!e&&e.enableTls,[]],tlsVersion:[e?e.tlsVersion:null,[]],enableProxy:[!!e&&e.enableProxy,[]],proxyHost:[e?e.proxyHost:null,[]],proxyPort:[e?e.proxyPort:null,[]],proxyUser:[e?e.proxyUser:null,[]],proxyPassword:[e?e.proxyPassword:null,[]],username:[e?e.username:null,[]],password:[e?e.password:null,[]]})}validatorTriggers(){return["useSystemSmtpSettings","enableProxy"]}updateValidators(e){const t=this.sendEmailConfigForm.get("useSystemSmtpSettings").value,n=this.sendEmailConfigForm.get("enableProxy").value;t?(this.sendEmailConfigForm.get("smtpProtocol").setValidators([]),this.sendEmailConfigForm.get("smtpHost").setValidators([]),this.sendEmailConfigForm.get("smtpPort").setValidators([]),this.sendEmailConfigForm.get("timeout").setValidators([]),this.sendEmailConfigForm.get("proxyHost").setValidators([]),this.sendEmailConfigForm.get("proxyPort").setValidators([])):(this.sendEmailConfigForm.get("smtpProtocol").setValidators([D.required]),this.sendEmailConfigForm.get("smtpHost").setValidators([D.required]),this.sendEmailConfigForm.get("smtpPort").setValidators([D.required,D.min(1),D.max(65535)]),this.sendEmailConfigForm.get("timeout").setValidators([D.required,D.min(0)]),this.sendEmailConfigForm.get("proxyHost").setValidators(n?[D.required]:[]),this.sendEmailConfigForm.get("proxyPort").setValidators(n?[D.required,D.min(1),D.max(65535)]:[])),this.sendEmailConfigForm.get("smtpProtocol").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("smtpHost").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("smtpPort").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("timeout").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("proxyHost").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("proxyPort").updateValueAndValidity({emitEvent:e})}}e("SendEmailConfigComponent",jn),jn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:jn,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),jn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:jn,selector:"tb-external-node-send-email-config",usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.use-system-smtp-settings\' | translate }}\n \n
\n \n tb.rulenode.smtp-protocol\n \n \n {{ smtpProtocol.toUpperCase() }}\n \n \n \n
\n \n tb.rulenode.smtp-host\n \n \n {{ \'tb.rulenode.smtp-host-required\' | translate }}\n \n \n \n tb.rulenode.smtp-port\n \n \n {{ \'tb.rulenode.smtp-port-required\' | translate }}\n \n \n {{ \'tb.rulenode.smtp-port-range\' | translate }}\n \n \n {{ \'tb.rulenode.smtp-port-range\' | translate }}\n \n \n
\n \n tb.rulenode.timeout-msec\n \n \n {{ \'tb.rulenode.timeout-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-timeout-msec-message\' | translate }}\n \n \n \n {{ \'tb.rulenode.enable-tls\' | translate }}\n \n \n tb.rulenode.tls-version\n \n \n {{ tlsVersion }}\n \n \n \n \n {{ \'tb.rulenode.enable-proxy\' | translate }}\n \n
\n
\n \n tb.rulenode.proxy-host\n \n \n {{ \'tb.rulenode.proxy-host-required\' | translate }}\n \n \n \n tb.rulenode.proxy-port\n \n \n {{ \'tb.rulenode.proxy-port-required\' | translate }}\n \n \n {{ \'tb.rulenode.proxy-port-range\' | translate }}\n \n \n
\n \n tb.rulenode.proxy-user\n \n \n \n tb.rulenode.proxy-password\n \n \n
\n \n tb.rulenode.username\n \n \n \n tb.rulenode.password\n \n \n \n
\n
\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:Qe.TbCheckboxComponent,selector:"tb-checkbox",inputs:["disabled","trueValue","falseValue"],outputs:["valueChange"]},{kind:"component",type:U.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:z.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Ke.TogglePasswordComponent,selector:"tb-toggle-password"},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:jn,decorators:[{type:n,args:[{selector:"tb-external-node-send-email-config",template:'
\n \n {{ \'tb.rulenode.use-system-smtp-settings\' | translate }}\n \n
\n \n tb.rulenode.smtp-protocol\n \n \n {{ smtpProtocol.toUpperCase() }}\n \n \n \n
\n \n tb.rulenode.smtp-host\n \n \n {{ \'tb.rulenode.smtp-host-required\' | translate }}\n \n \n \n tb.rulenode.smtp-port\n \n \n {{ \'tb.rulenode.smtp-port-required\' | translate }}\n \n \n {{ \'tb.rulenode.smtp-port-range\' | translate }}\n \n \n {{ \'tb.rulenode.smtp-port-range\' | translate }}\n \n \n
\n \n tb.rulenode.timeout-msec\n \n \n {{ \'tb.rulenode.timeout-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-timeout-msec-message\' | translate }}\n \n \n \n {{ \'tb.rulenode.enable-tls\' | translate }}\n \n \n tb.rulenode.tls-version\n \n \n {{ tlsVersion }}\n \n \n \n \n {{ \'tb.rulenode.enable-proxy\' | translate }}\n \n
\n
\n \n tb.rulenode.proxy-host\n \n \n {{ \'tb.rulenode.proxy-host-required\' | translate }}\n \n \n \n tb.rulenode.proxy-port\n \n \n {{ \'tb.rulenode.proxy-port-required\' | translate }}\n \n \n {{ \'tb.rulenode.proxy-port-range\' | translate }}\n \n \n
\n \n tb.rulenode.proxy-user\n \n \n \n tb.rulenode.proxy-password\n \n \n
\n \n tb.rulenode.username\n \n \n \n tb.rulenode.password\n \n \n \n
\n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class Qn extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.sendSmsConfigForm}onConfigurationSet(e){this.sendSmsConfigForm=this.fb.group({numbersToTemplate:[e?e.numbersToTemplate:null,[D.required]],smsMessageTemplate:[e?e.smsMessageTemplate:null,[D.required]],useSystemSmsSettings:[!!e&&e.useSystemSmsSettings,[]],smsProviderConfiguration:[e?e.smsProviderConfiguration:null,[]]})}validatorTriggers(){return["useSystemSmsSettings"]}updateValidators(e){this.sendSmsConfigForm.get("useSystemSmsSettings").value?this.sendSmsConfigForm.get("smsProviderConfiguration").setValidators([]):this.sendSmsConfigForm.get("smsProviderConfiguration").setValidators([D.required]),this.sendSmsConfigForm.get("smsProviderConfiguration").updateValueAndValidity({emitEvent:e})}}e("SendSmsConfigComponent",Qn),Qn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Qn,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Qn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Qn,selector:"tb-external-node-send-sms-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.numbers-to-template\n \n \n {{ \'tb.rulenode.numbers-to-template-required\' | translate }}\n \n \n \n \n tb.rulenode.sms-message-template\n \n \n {{ \'tb.rulenode.sms-message-template-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.use-system-sms-settings\' | translate }}\n \n \n \n
\n',dependencies:[{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:U.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:$e.SmsProviderConfigurationComponent,selector:"tb-sms-provider-configuration",inputs:["required","disabled"]},{kind:"pipe",type:fe.SafePipe,name:"safe"},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Qn,decorators:[{type:n,args:[{selector:"tb-external-node-send-sms-config",template:'
\n \n tb.rulenode.numbers-to-template\n \n \n {{ \'tb.rulenode.numbers-to-template-required\' | translate }}\n \n \n \n \n tb.rulenode.sms-message-template\n \n \n {{ \'tb.rulenode.sms-message-template-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.use-system-sms-settings\' | translate }}\n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class $n extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.slackChanelTypes=Object.keys(A),this.slackChanelTypesTranslateMap=M}configForm(){return this.slackConfigForm}onConfigurationSet(e){this.slackConfigForm=this.fb.group({botToken:[e?e.botToken:null],useSystemSettings:[!!e&&e.useSystemSettings],messageTemplate:[e?e.messageTemplate:null,[D.required]],conversationType:[e?e.conversationType:null,[D.required]],conversation:[e?e.conversation:null,[D.required]]})}validatorTriggers(){return["useSystemSettings"]}updateValidators(e){this.slackConfigForm.get("useSystemSettings").value?this.slackConfigForm.get("botToken").clearValidators():this.slackConfigForm.get("botToken").setValidators([D.required]),this.slackConfigForm.get("botToken").updateValueAndValidity({emitEvent:e})}}e("SlackConfigComponent",$n),$n.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:$n,deps:[{token:G.Store},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),$n.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:$n,selector:"tb-external-node-slack-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.message-template\n \n \n {{ \'tb.rulenode.message-template-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.use-system-slack-settings\' | translate }}\n \n \n tb.rulenode.slack-api-token\n \n \n {{ \'tb.rulenode.slack-api-token-required\' | translate }}\n \n \n \n \n \n {{ slackChanelTypesTranslateMap.get(slackChanelType) | translate }}\n \n \n \n \n
\n',styles:[":host .tb-title{display:block;padding-bottom:6px}:host ::ng-deep .mat-mdc-radio-group{display:flex;flex-direction:row;margin-bottom:22px;gap:12px}:host ::ng-deep .mat-mdc-radio-group .mat-mdc-radio-button{flex:1 1 100%;padding:4px;border:1px solid rgba(0,0,0,.12);border-radius:6px}@media screen and (max-width: 599px){:host ::ng-deep .mat-mdc-radio-group{flex-direction:column}}\n"],dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:U.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Je.MatRadioGroup,selector:"mat-radio-group",exportAs:["matRadioGroup"]},{kind:"component",type:Je.MatRadioButton,selector:"mat-radio-button",inputs:["disableRipple","tabIndex"],exportAs:["matRadioButton"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Ye.SlackConversationAutocompleteComponent,selector:"tb-slack-conversation-autocomplete",inputs:["labelText","requiredText","required","disabled","slackChanelType","token"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:$n,decorators:[{type:n,args:[{selector:"tb-external-node-slack-config",template:'
\n \n tb.rulenode.message-template\n \n \n {{ \'tb.rulenode.message-template-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.use-system-slack-settings\' | translate }}\n \n \n tb.rulenode.slack-api-token\n \n \n {{ \'tb.rulenode.slack-api-token-required\' | translate }}\n \n \n \n \n \n {{ slackChanelTypesTranslateMap.get(slackChanelType) | translate }}\n \n \n \n \n
\n',styles:[":host .tb-title{display:block;padding-bottom:6px}:host ::ng-deep .mat-mdc-radio-group{display:flex;flex-direction:row;margin-bottom:22px;gap:12px}:host ::ng-deep .mat-mdc-radio-group .mat-mdc-radio-button{flex:1 1 100%;padding:4px;border:1px solid rgba(0,0,0,.12);border-radius:6px}@media screen and (max-width: 599px){:host ::ng-deep .mat-mdc-radio-group{flex-direction:column}}\n"]}]}],ctorParameters:function(){return[{type:G.Store},{type:E.FormBuilder}]}});class Jn extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.snsConfigForm}onConfigurationSet(e){this.snsConfigForm=this.fb.group({topicArnPattern:[e?e.topicArnPattern:null,[D.required]],accessKeyId:[e?e.accessKeyId:null,[D.required]],secretAccessKey:[e?e.secretAccessKey:null,[D.required]],region:[e?e.region:null,[D.required]]})}}e("SnsConfigComponent",Jn),Jn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Jn,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Jn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Jn,selector:"tb-external-node-sns-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.topic-arn-pattern\n \n \n {{ \'tb.rulenode.topic-arn-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.aws-access-key-id\n \n \n {{ \'tb.rulenode.aws-access-key-id-required\' | translate }}\n \n \n \n tb.rulenode.aws-secret-access-key\n \n \n {{ \'tb.rulenode.aws-secret-access-key-required\' | translate }}\n \n \n \n tb.rulenode.aws-region\n \n \n {{ \'tb.rulenode.aws-region-required\' | translate }}\n \n \n
\n',dependencies:[{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Jn,decorators:[{type:n,args:[{selector:"tb-external-node-sns-config",template:'
\n \n tb.rulenode.topic-arn-pattern\n \n \n {{ \'tb.rulenode.topic-arn-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.aws-access-key-id\n \n \n {{ \'tb.rulenode.aws-access-key-id-required\' | translate }}\n \n \n \n tb.rulenode.aws-secret-access-key\n \n \n {{ \'tb.rulenode.aws-secret-access-key-required\' | translate }}\n \n \n \n tb.rulenode.aws-region\n \n \n {{ \'tb.rulenode.aws-region-required\' | translate }}\n \n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class Yn extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.sqsQueueType=qt,this.sqsQueueTypes=Object.keys(qt),this.sqsQueueTypeTranslationsMap=At}configForm(){return this.sqsConfigForm}onConfigurationSet(e){this.sqsConfigForm=this.fb.group({queueType:[e?e.queueType:null,[D.required]],queueUrlPattern:[e?e.queueUrlPattern:null,[D.required]],delaySeconds:[e?e.delaySeconds:null,[D.min(0),D.max(900)]],messageAttributes:[e?e.messageAttributes:null,[]],accessKeyId:[e?e.accessKeyId:null,[D.required]],secretAccessKey:[e?e.secretAccessKey:null,[D.required]],region:[e?e.region:null,[D.required]]})}}e("SqsConfigComponent",Yn),Yn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Yn,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Yn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Yn,selector:"tb-external-node-sqs-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.queue-type\n \n \n {{ sqsQueueTypeTranslationsMap.get(type) | translate }}\n \n \n \n \n tb.rulenode.queue-url-pattern\n \n \n {{ \'tb.rulenode.queue-url-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.delay-seconds\n \n \n {{ \'tb.rulenode.min-delay-seconds-message\' | translate }}\n \n \n {{ \'tb.rulenode.max-delay-seconds-message\' | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.aws-access-key-id\n \n \n {{ \'tb.rulenode.aws-access-key-id-required\' | translate }}\n \n \n \n tb.rulenode.aws-secret-access-key\n \n \n {{ \'tb.rulenode.aws-secret-access-key-required\' | translate }}\n \n \n \n tb.rulenode.aws-region\n \n \n {{ \'tb.rulenode.aws-region-required\' | translate }}\n \n \n
\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:on,selector:"tb-kv-map-config-old",inputs:["disabled","uniqueKeyValuePairValidator","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{kind:"pipe",type:fe.SafePipe,name:"safe"},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Yn,decorators:[{type:n,args:[{selector:"tb-external-node-sqs-config",template:'
\n \n tb.rulenode.queue-type\n \n \n {{ sqsQueueTypeTranslationsMap.get(type) | translate }}\n \n \n \n \n tb.rulenode.queue-url-pattern\n \n \n {{ \'tb.rulenode.queue-url-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.delay-seconds\n \n \n {{ \'tb.rulenode.min-delay-seconds-message\' | translate }}\n \n \n {{ \'tb.rulenode.max-delay-seconds-message\' | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.aws-access-key-id\n \n \n {{ \'tb.rulenode.aws-access-key-id-required\' | translate }}\n \n \n \n tb.rulenode.aws-secret-access-key\n \n \n {{ \'tb.rulenode.aws-secret-access-key-required\' | translate }}\n \n \n \n tb.rulenode.aws-region\n \n \n {{ \'tb.rulenode.aws-region-required\' | translate }}\n \n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class Wn{}e("RulenodeCoreConfigExternalModule",Wn),Wn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Wn,deps:[],target:t.ɵɵFactoryTarget.NgModule}),Wn.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.10",ngImport:t,type:Wn,declarations:[Jn,Yn,_n,Bn,Kn,Un,zn,Hn,jn,On,Qn,$n],imports:[K,I,Ve,In],exports:[Jn,Yn,_n,Bn,Kn,Un,zn,Hn,jn,On,Qn,$n]}),Wn.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Wn,imports:[K,I,Ve,In]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Wn,decorators:[{type:l,args:[{declarations:[Jn,Yn,_n,Bn,Kn,Un,zn,Hn,jn,On,Qn,$n],imports:[K,I,Ve,In],exports:[Jn,Yn,_n,Bn,Kn,Un,zn,Hn,jn,On,Qn,$n]}]}]});class Xn extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.searchText=""}configForm(){return this.alarmStatusConfigForm}prepareInputConfig(e){return{alarmStatusList:ee(e?.alarmStatusList)?e.alarmStatusList:null}}onConfigurationSet(e){this.alarmStatusConfigForm=this.fb.group({alarmStatusList:[e.alarmStatusList,[D.required]]})}}e("CheckAlarmStatusComponent",Xn),Xn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Xn,deps:[{token:G.Store},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Xn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Xn,selector:"tb-filter-node-check-alarm-status-config",usesInheritance:!0,ngImport:t,template:'
\n
\n
tb.rulenode.alarm-status
\n
\n tb.rulenode.alarm-required\n
\n
\n \n
\n\n\n\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n'],dependencies:[{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Tn,selector:"tb-alarm-status-select"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Xn,decorators:[{type:n,args:[{selector:"tb-filter-node-check-alarm-status-config",template:'
\n
\n
tb.rulenode.alarm-status
\n
\n tb.rulenode.alarm-required\n
\n
\n \n
\n\n\n\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n']}]}],ctorParameters:function(){return[{type:G.Store},{type:E.FormBuilder}]}});class Zn extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.checkMessageConfigForm}prepareInputConfig(e){return{messageNames:ee(e?.messageNames)?e.messageNames:null,metadataNames:ee(e?.metadataNames)?e.metadataNames:null,checkAllKeys:!!ee(e?.checkAllKeys)&&e.checkAllKeys}}atLeastOne(e,t=null){return n=>{t||(t=Object.keys(n.controls));return n?.controls&&t.some((t=>!e(n.controls[t])))?null:{atLeastOne:!0}}}onConfigurationSet(e){this.checkMessageConfigForm=this.fb.group({messageNames:[e.messageNames,[]],metadataNames:[e.metadataNames,[]],checkAllKeys:[e.checkAllKeys,[]]},{validators:this.atLeastOne(D.required,["messageNames","metadataNames"])})}get touchedValidationControl(){return["messageNames","metadataNames"].some((e=>this.checkMessageConfigForm.get(e).touched))}}e("CheckMessageConfigComponent",Zn),Zn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Zn,deps:[{token:G.Store},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Zn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Zn,selector:"tb-filter-node-check-message-config",usesInheritance:!0,ngImport:t,template:'
\n
\n
tb.rulenode.fields-to-check
\n
\n tb.rulenode.at-least-one-field-required\n
\n
\n \n help\n \n \n help\n \n
\n \n {{ \'tb.rulenode.check-all-keys\' | translate }}\n \n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n'],dependencies:[{kind:"component",type:Ue.StringItemsListComponent,selector:"tb-string-items-list",inputs:["required","disabled","label","placeholder","hint","requiredText","floatLabel","appearance","editable","subscriptSizing","predefinedValues"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:z.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"directive",type:se.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"component",type:J.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Y.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Zn,decorators:[{type:n,args:[{selector:"tb-filter-node-check-message-config",template:'
\n
\n
tb.rulenode.fields-to-check
\n
\n tb.rulenode.at-least-one-field-required\n
\n
\n \n help\n \n \n help\n \n
\n \n {{ \'tb.rulenode.check-all-keys\' | translate }}\n \n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n']}]}],ctorParameters:function(){return[{type:G.Store},{type:E.FormBuilder}]}});class er extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.entitySearchDirection=Object.values(g),this.entitySearchDirectionTranslationsMap=y}configForm(){return this.checkRelationConfigForm}prepareInputConfig(e){return{checkForSingleEntity:!!ee(e?.checkForSingleEntity)&&e.checkForSingleEntity,direction:ee(e?.direction)?e.direction:null,entityType:ee(e?.entityType)?e.entityType:null,entityId:ee(e?.entityId)?e.entityId:null,relationType:ee(e?.relationType)?e.relationType:null}}onConfigurationSet(e){this.checkRelationConfigForm=this.fb.group({checkForSingleEntity:[e.checkForSingleEntity,[]],direction:[e.direction,[]],entityType:[e.entityType,e&&e.checkForSingleEntity?[D.required]:[]],entityId:[e.entityId,e&&e.checkForSingleEntity?[D.required]:[]],relationType:[e.relationType,[D.required]]})}validatorTriggers(){return["checkForSingleEntity"]}updateValidators(e){const t=this.checkRelationConfigForm.get("checkForSingleEntity").value;this.checkRelationConfigForm.get("entityType").setValidators(t?[D.required]:[]),this.checkRelationConfigForm.get("entityType").updateValueAndValidity({emitEvent:e}),this.checkRelationConfigForm.get("entityId").setValidators(t?[D.required]:[]),this.checkRelationConfigForm.get("entityId").updateValueAndValidity({emitEvent:e})}}e("CheckRelationConfigComponent",er),er.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:er,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),er.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:er,selector:"tb-filter-node-check-relation-config",usesInheritance:!0,ngImport:t,template:'
\n
tb.rulenode.relation-search-parameters
\n
\n \n {{ \'relation.direction\' | translate }}\n \n \n {{ entitySearchDirectionTranslationsMap.get(direction) | translate }} tb.rulenode.relations-query-config-direction-suffix\n \n \n \n \n \n
\n \n {{ \'tb.rulenode.check-relation-to-specific-entity\' | translate }}\n \n
\n
\n \n \n \n \n
\n
\n
\n',styles:[":host .slide-toggle{margin-bottom:18px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n'],dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:We.EntityAutocompleteComponent,selector:"tb-entity-autocomplete",inputs:["entityType","entitySubtype","excludeEntityIds","labelText","requiredText","useFullEntityId","appearance","required","disabled"],outputs:["entityChanged"]},{kind:"component",type:ge.EntityTypeSelectComponent,selector:"tb-entity-type-select",inputs:["allowedEntityTypes","useAliasEntityTypes","filterAllowedEntityTypes","showLabel","required","disabled"]},{kind:"component",type:Re.RelationTypeAutocompleteComponent,selector:"tb-relation-type-autocomplete",inputs:["showLabel","additionalClasses","appearance","required","disabled","subscriptSizing"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:J.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Y.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:er,decorators:[{type:n,args:[{selector:"tb-filter-node-check-relation-config",template:'
\n
tb.rulenode.relation-search-parameters
\n
\n \n {{ \'relation.direction\' | translate }}\n \n \n {{ entitySearchDirectionTranslationsMap.get(direction) | translate }} tb.rulenode.relations-query-config-direction-suffix\n \n \n \n \n \n
\n \n {{ \'tb.rulenode.check-relation-to-specific-entity\' | translate }}\n \n
\n
\n \n \n \n \n
\n
\n
\n',styles:[":host .slide-toggle{margin-bottom:18px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n']}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class tr extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.perimeterType=ct,this.perimeterTypes=Object.values(ct),this.perimeterTypeTranslationMap=ft,this.rangeUnits=Object.values(xt),this.rangeUnitTranslationMap=bt,this.defaultPaddingEnable=!0}configForm(){return this.geoFilterConfigForm}prepareInputConfig(e){return{latitudeKeyName:ee(e?.latitudeKeyName)?e.latitudeKeyName:null,longitudeKeyName:ee(e?.longitudeKeyName)?e.longitudeKeyName:null,perimeterType:ee(e?.perimeterType)?e.perimeterType:null,fetchPerimeterInfoFromMessageMetadata:!!ee(e?.fetchPerimeterInfoFromMessageMetadata)&&e.fetchPerimeterInfoFromMessageMetadata,perimeterKeyName:ee(e?.perimeterKeyName)?e.perimeterKeyName:null,centerLatitude:ee(e?.centerLatitude)?e.centerLatitude:null,centerLongitude:ee(e?.centerLongitude)?e.centerLongitude:null,range:ee(e?.range)?e.range:null,rangeUnit:ee(e?.rangeUnit)?e.rangeUnit:null,polygonsDefinition:ee(e?.polygonsDefinition)?e.polygonsDefinition:null}}onConfigurationSet(e){this.geoFilterConfigForm=this.fb.group({latitudeKeyName:[e.latitudeKeyName,[D.required]],longitudeKeyName:[e.longitudeKeyName,[D.required]],perimeterType:[e.perimeterType,[D.required]],fetchPerimeterInfoFromMessageMetadata:[e.fetchPerimeterInfoFromMessageMetadata,[]],perimeterKeyName:[e.perimeterKeyName,[]],centerLatitude:[e.centerLatitude,[]],centerLongitude:[e.centerLongitude,[]],range:[e.range,[]],rangeUnit:[e.rangeUnit,[]],polygonsDefinition:[e.polygonsDefinition,[]]})}validatorTriggers(){return["fetchPerimeterInfoFromMessageMetadata","perimeterType"]}updateValidators(e){const t=this.geoFilterConfigForm.get("fetchPerimeterInfoFromMessageMetadata").value,n=this.geoFilterConfigForm.get("perimeterType").value;t?this.geoFilterConfigForm.get("perimeterKeyName").setValidators([D.required]):this.geoFilterConfigForm.get("perimeterKeyName").setValidators([]),t||n!==ct.CIRCLE?(this.geoFilterConfigForm.get("centerLatitude").setValidators([]),this.geoFilterConfigForm.get("centerLongitude").setValidators([]),this.geoFilterConfigForm.get("range").setValidators([]),this.geoFilterConfigForm.get("rangeUnit").setValidators([]),this.defaultPaddingEnable=!0):(this.geoFilterConfigForm.get("centerLatitude").setValidators([D.required,D.min(-90),D.max(90)]),this.geoFilterConfigForm.get("centerLongitude").setValidators([D.required,D.min(-180),D.max(180)]),this.geoFilterConfigForm.get("range").setValidators([D.required,D.min(0)]),this.geoFilterConfigForm.get("rangeUnit").setValidators([D.required]),this.defaultPaddingEnable=!1),t||n!==ct.POLYGON?this.geoFilterConfigForm.get("polygonsDefinition").setValidators([]):this.geoFilterConfigForm.get("polygonsDefinition").setValidators([D.required]),this.geoFilterConfigForm.get("perimeterKeyName").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("centerLatitude").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("centerLongitude").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("range").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("rangeUnit").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("polygonsDefinition").updateValueAndValidity({emitEvent:e})}}e("GpsGeoFilterConfigComponent",tr),tr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:tr,deps:[{token:G.Store},{token:E.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),tr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:tr,selector:"tb-filter-node-gps-geofencing-config",usesInheritance:!0,ngImport:t,template:'
\n
\n
tb.rulenode.coordinate-field-names
\n
\n
\n \n {{ \'tb.rulenode.latitude-field-name\' | translate }}\n \n \n {{ \'tb.rulenode.latitude-field-name-required\' | translate }}\n \n \n \n {{ \'tb.rulenode.longitude-field-name\' | translate }}\n \n \n {{ \'tb.rulenode.longitude-field-name-required\' | translate }}\n \n \n
\n
tb.rulenode.coordinate-field-hint
\n
\n
\n
\n
tb.rulenode.geofence-configuration
\n
\n \n {{ \'tb.rulenode.perimeter-type\' | translate }}\n \n \n {{ perimeterTypeTranslationMap.get(type) | translate }}\n \n \n \n
\n \n {{ \'tb.rulenode.fetch-perimeter-info-from-metadata\' | translate }}\n \n
\n \n {{ \'tb.rulenode.perimeter-key-name\' | translate }}\n \n \n {{ \'tb.rulenode.perimeter-key-name-required\' | translate }}\n \n {{ \'tb.rulenode.perimeter-key-name-hint\' | translate }}\n \n
\n
\n \n {{ \'tb.rulenode.circle-center-latitude\' | translate }}\n \n \n {{ \'tb.rulenode.circle-center-latitude-required\' | translate }}\n \n \n \n {{ \'tb.rulenode.circle-center-longitude\' | translate }}\n \n \n {{ \'tb.rulenode.circle-center-longitude-required\' | translate }}\n \n \n
\n
\n \n {{ \'tb.rulenode.range\' | translate }}\n \n \n {{ \'tb.rulenode.range-required\' | translate }}\n \n \n \n {{ \'tb.rulenode.range-units\' | translate }}\n \n \n {{ rangeUnitTranslationMap.get(type) | translate }}\n \n \n \n {{ \'tb.rulenode.range-units-required\' | translate }}\n \n \n
\n
\n \n {{ \'tb.rulenode.polygon-definition\' | translate }}\n \n {{ \'tb.rulenode.polygon-definition-hint\' | translate }}\n \n {{ \'tb.rulenode.polygon-definition-required\' | translate }}\n \n \n
\n
\n
\n',styles:[":host .slide-toggle{margin-bottom:18px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n'],dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:J.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:E.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Y.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:tr,decorators:[{type:n,args:[{selector:"tb-filter-node-gps-geofencing-config",template:'
\n
\n
tb.rulenode.coordinate-field-names
\n
\n
\n \n {{ \'tb.rulenode.latitude-field-name\' | translate }}\n \n \n {{ \'tb.rulenode.latitude-field-name-required\' | translate }}\n \n \n \n {{ \'tb.rulenode.longitude-field-name\' | translate }}\n \n \n {{ \'tb.rulenode.longitude-field-name-required\' | translate }}\n \n \n
\n
tb.rulenode.coordinate-field-hint
\n
\n
\n
\n
tb.rulenode.geofence-configuration
\n
\n \n {{ \'tb.rulenode.perimeter-type\' | translate }}\n \n \n {{ perimeterTypeTranslationMap.get(type) | translate }}\n \n \n \n
\n \n {{ \'tb.rulenode.fetch-perimeter-info-from-metadata\' | translate }}\n \n
\n \n {{ \'tb.rulenode.perimeter-key-name\' | translate }}\n \n \n {{ \'tb.rulenode.perimeter-key-name-required\' | translate }}\n \n {{ \'tb.rulenode.perimeter-key-name-hint\' | translate }}\n \n
\n
\n \n {{ \'tb.rulenode.circle-center-latitude\' | translate }}\n \n \n {{ \'tb.rulenode.circle-center-latitude-required\' | translate }}\n \n \n \n {{ \'tb.rulenode.circle-center-longitude\' | translate }}\n \n \n {{ \'tb.rulenode.circle-center-longitude-required\' | translate }}\n \n \n
\n
\n \n {{ \'tb.rulenode.range\' | translate }}\n \n \n {{ \'tb.rulenode.range-required\' | translate }}\n \n \n \n {{ \'tb.rulenode.range-units\' | translate }}\n \n \n {{ rangeUnitTranslationMap.get(type) | translate }}\n \n \n \n {{ \'tb.rulenode.range-units-required\' | translate }}\n \n \n
\n
\n \n {{ \'tb.rulenode.polygon-definition\' | translate }}\n \n {{ \'tb.rulenode.polygon-definition-hint\' | translate }}\n \n {{ \'tb.rulenode.polygon-definition-required\' | translate }}\n \n \n
\n
\n
\n',styles:[":host .slide-toggle{margin-bottom:18px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n']}]}],ctorParameters:function(){return[{type:G.Store},{type:E.FormBuilder}]}});class nr extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.messageTypeConfigForm}prepareInputConfig(e){return{messageTypes:ee(e?.messageTypes)?e.messageTypes:null}}onConfigurationSet(e){this.messageTypeConfigForm=this.fb.group({messageTypes:[e.messageTypes,[D.required]]})}}e("MessageTypeConfigComponent",nr),nr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:nr,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),nr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:nr,selector:"tb-filter-node-message-type-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n
\n',dependencies:[{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:hn,selector:"tb-message-types-config",inputs:["required","label","placeholder","disabled"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:nr,decorators:[{type:n,args:[{selector:"tb-filter-node-message-type-config",template:'
\n \n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class rr extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.allowedEntityTypes=[x.DEVICE,x.ASSET,x.ENTITY_VIEW,x.TENANT,x.CUSTOMER,x.USER,x.DASHBOARD,x.RULE_CHAIN,x.RULE_NODE]}configForm(){return this.originatorTypeConfigForm}prepareInputConfig(e){return{originatorTypes:ee(e?.originatorTypes)?e.originatorTypes:null}}onConfigurationSet(e){this.originatorTypeConfigForm=this.fb.group({originatorTypes:[e.originatorTypes,[D.required]]})}}e("OriginatorTypeConfigComponent",rr),rr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:rr,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),rr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:rr,selector:"tb-filter-node-originator-type-config",usesInheritance:!0,ngImport:t,template:'
\n \n help\n \n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n'],dependencies:[{kind:"component",type:Xe.EntityTypeListComponent,selector:"tb-entity-type-list",inputs:["required","additionalClasses","appearance","label","floatLabel","disabled","subscriptSizing","allowedEntityTypes","emptyInputPlaceholder","filledInputPlaceholder","ignoreAuthorityFilter"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:z.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"directive",type:se.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:rr,decorators:[{type:n,args:[{selector:"tb-filter-node-originator-type-config",template:'
\n \n help\n \n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}\n']}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class or extends s{constructor(e,t,n,o){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=n,this.translate=o,this.tbelEnabled=X(this.store).tbelEnabled,this.scriptLanguage=d,this.changeScript=new r,this.hasScript=!0,this.testScriptLabel="tb.rulenode.test-filter-function"}configForm(){return this.scriptConfigForm}onConfigurationSet(e){this.scriptConfigForm=this.fb.group({scriptLang:[e.scriptLang,[D.required]],jsScript:[e.jsScript,[]],tbelScript:[e.tbelScript,[]]})}validatorTriggers(){return["scriptLang"]}updateValidators(e){let t=this.scriptConfigForm.get("scriptLang").value;t!==d.TBEL||this.tbelEnabled||(t=d.JS,this.scriptConfigForm.get("scriptLang").patchValue(t,{emitEvent:!1}),setTimeout((()=>{this.scriptConfigForm.updateValueAndValidity({emitEvent:!0})}))),this.scriptConfigForm.get("jsScript").setValidators(t===d.JS?[D.required]:[]),this.scriptConfigForm.get("jsScript").updateValueAndValidity({emitEvent:e}),this.scriptConfigForm.get("tbelScript").setValidators(t===d.TBEL?[D.required]:[]),this.scriptConfigForm.get("tbelScript").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=d.JS)),{scriptLang:ee(e?.scriptLang)?e.scriptLang:d.JS,jsScript:ee(e?.jsScript)?e.jsScript:null,tbelScript:ee(e?.tbelScript)?e.tbelScript:null}}testScript(e){const t=this.scriptConfigForm.get("scriptLang").value,n=t===d.JS?"jsScript":"tbelScript",r=t===d.JS?"rulenode/filter_node_script_fn":"rulenode/tbel/filter_node_script_fn",o=this.scriptConfigForm.get(n).value;this.nodeScriptTestService.testNodeScript(o,"filter",this.translate.instant("tb.rulenode.filter"),"Filter",["msg","metadata","msgType"],this.ruleNodeId,r,t,e).subscribe((e=>{e&&(this.scriptConfigForm.get(n).setValue(e),this.changeScript.emit())}))}onValidate(){this.scriptConfigForm.get("scriptLang").value===d.JS&&this.jsFuncComponent.validateOnSubmit()}}e("ScriptConfigComponent",or),or.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:or,deps:[{token:G.Store},{token:E.UntypedFormBuilder},{token:Z.NodeScriptTestService},{token:j.TranslateService}],target:t.ɵɵFactoryTarget.Component}),or.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:or,selector:"tb-filter-node-script-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0},{propertyName:"tbelFuncComponent",first:!0,predicate:["tbelFuncComponent"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n \n \n \n \n
\n',dependencies:[{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ae.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","scriptLanguage","noValidate","required"]},{kind:"component",type:ie.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ie.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:se.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:me.TbScriptLangComponent,selector:"tb-script-lang",inputs:["disabled"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:or,decorators:[{type:n,args:[{selector:"tb-filter-node-script-config",template:'
\n \n \n \n \n \n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder},{type:Z.NodeScriptTestService},{type:j.TranslateService}]},propDecorators:{jsFuncComponent:[{type:o,args:["jsFuncComponent",{static:!1}]}],tbelFuncComponent:[{type:o,args:["tbelFuncComponent",{static:!1}]}]}});class ar extends s{constructor(e,t,n,o){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=n,this.translate=o,this.tbelEnabled=X(this.store).tbelEnabled,this.scriptLanguage=d,this.changeScript=new r,this.hasScript=!0,this.testScriptLabel="tb.rulenode.test-switch-function"}configForm(){return this.switchConfigForm}onConfigurationSet(e){this.switchConfigForm=this.fb.group({scriptLang:[e.scriptLang,[D.required]],jsScript:[e.jsScript,[]],tbelScript:[e.tbelScript,[]]})}validatorTriggers(){return["scriptLang"]}updateValidators(e){let t=this.switchConfigForm.get("scriptLang").value;t!==d.TBEL||this.tbelEnabled||(t=d.JS,this.switchConfigForm.get("scriptLang").patchValue(t,{emitEvent:!1}),setTimeout((()=>{this.switchConfigForm.updateValueAndValidity({emitEvent:!0})}))),this.switchConfigForm.get("jsScript").setValidators(t===d.JS?[D.required]:[]),this.switchConfigForm.get("jsScript").updateValueAndValidity({emitEvent:e}),this.switchConfigForm.get("tbelScript").setValidators(t===d.TBEL?[D.required]:[]),this.switchConfigForm.get("tbelScript").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=d.JS)),{scriptLang:ee(e?.scriptLang)?e.scriptLang:d.JS,jsScript:ee(e?.jsScript)?e.jsScript:null,tbelScript:ee(e?.tbelScript)?e.tbelScript:null}}testScript(e){const t=this.switchConfigForm.get("scriptLang").value,n=t===d.JS?"jsScript":"tbelScript",r=t===d.JS?"rulenode/switch_node_script_fn":"rulenode/tbel/switch_node_script_fn",o=this.switchConfigForm.get(n).value;this.nodeScriptTestService.testNodeScript(o,"switch",this.translate.instant("tb.rulenode.switch"),"Switch",["msg","metadata","msgType"],this.ruleNodeId,r,t,e).subscribe((e=>{e&&(this.switchConfigForm.get(n).setValue(e),this.changeScript.emit())}))}onValidate(){this.switchConfigForm.get("scriptLang").value===d.JS&&this.jsFuncComponent.validateOnSubmit()}}e("SwitchConfigComponent",ar),ar.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ar,deps:[{token:G.Store},{token:E.UntypedFormBuilder},{token:Z.NodeScriptTestService},{token:j.TranslateService}],target:t.ɵɵFactoryTarget.Component}),ar.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:ar,selector:"tb-filter-node-switch-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0},{propertyName:"tbelFuncComponent",first:!0,predicate:["tbelFuncComponent"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n \n \n \n \n
\n',dependencies:[{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ae.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","scriptLanguage","noValidate","required"]},{kind:"component",type:ie.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ie.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:se.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:me.TbScriptLangComponent,selector:"tb-script-lang",inputs:["disabled"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ar,decorators:[{type:n,args:[{selector:"tb-filter-node-switch-config",template:'
\n \n \n \n \n \n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder},{type:Z.NodeScriptTestService},{type:j.TranslateService}]},propDecorators:{jsFuncComponent:[{type:o,args:["jsFuncComponent",{static:!1}]}],tbelFuncComponent:[{type:o,args:["tbelFuncComponent",{static:!1}]}]}});class ir{}e("RuleNodeCoreConfigFilterModule",ir),ir.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ir,deps:[],target:t.ɵɵFactoryTarget.NgModule}),ir.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.10",ngImport:t,type:ir,declarations:[Zn,er,tr,nr,rr,or,ar,Xn],imports:[K,I,In],exports:[Zn,er,tr,nr,rr,or,ar,Xn]}),ir.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ir,imports:[K,I,In]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ir,decorators:[{type:l,args:[{declarations:[Zn,er,tr,nr,rr,or,ar,Xn],imports:[K,I,In],exports:[Zn,er,tr,nr,rr,or,ar,Xn]}]}]});class lr extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.originatorSource=mt,this.originatorSources=Object.keys(mt),this.originatorSourceTranslationMap=ut,this.allowedEntityTypes=[x.DEVICE,x.ASSET,x.ENTITY_VIEW,x.USER,x.EDGE]}configForm(){return this.changeOriginatorConfigForm}onConfigurationSet(e){this.changeOriginatorConfigForm=this.fb.group({originatorSource:[e?e.originatorSource:null,[D.required]],entityType:[e?e.entityType:null,[]],entityNamePattern:[e?e.entityNamePattern:null,[]],relationsQuery:[e?e.relationsQuery:null,[]]})}validatorTriggers(){return["originatorSource"]}updateValidators(e){const t=this.changeOriginatorConfigForm.get("originatorSource").value;t===mt.RELATED?this.changeOriginatorConfigForm.get("relationsQuery").setValidators([D.required]):this.changeOriginatorConfigForm.get("relationsQuery").setValidators([]),t===mt.ENTITY?(this.changeOriginatorConfigForm.get("entityType").setValidators([D.required]),this.changeOriginatorConfigForm.get("entityNamePattern").setValidators([D.required,D.pattern(/.*\S.*/)])):(this.changeOriginatorConfigForm.get("entityType").patchValue(null,{emitEvent:e}),this.changeOriginatorConfigForm.get("entityNamePattern").patchValue(null,{emitEvent:e}),this.changeOriginatorConfigForm.get("entityType").setValidators([]),this.changeOriginatorConfigForm.get("entityNamePattern").setValidators([])),this.changeOriginatorConfigForm.get("relationsQuery").updateValueAndValidity({emitEvent:e}),this.changeOriginatorConfigForm.get("entityType").updateValueAndValidity({emitEvent:e}),this.changeOriginatorConfigForm.get("entityNamePattern").updateValueAndValidity({emitEvent:e})}}e("ChangeOriginatorConfigComponent",lr),lr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:lr,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),lr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:lr,selector:"tb-transformation-node-change-originator-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.originator-source\n \n \n {{ originatorSourceTranslationMap.get(source) | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.entity-name-pattern\n \n \n {{ \'tb.rulenode.entity-name-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n
\n \n \n \n
\n
\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ge.EntityTypeSelectComponent,selector:"tb-entity-type-select",inputs:["allowedEntityTypes","useAliasEntityTypes","filterAllowedEntityTypes","showLabel","required","disabled"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:H.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:kn,selector:"tb-relations-query-config-old",inputs:["disabled","required"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:lr,decorators:[{type:n,args:[{selector:"tb-transformation-node-change-originator-config",template:'
\n \n tb.rulenode.originator-source\n \n \n {{ originatorSourceTranslationMap.get(source) | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.entity-name-pattern\n \n \n {{ \'tb.rulenode.entity-name-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n
\n \n \n \n
\n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class sr extends s{constructor(e,t,n,o){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=n,this.translate=o,this.tbelEnabled=X(this.store).tbelEnabled,this.scriptLanguage=d,this.changeScript=new r,this.hasScript=!0,this.testScriptLabel="tb.rulenode.test-transformer-function"}configForm(){return this.scriptConfigForm}onConfigurationSet(e){this.scriptConfigForm=this.fb.group({scriptLang:[e?e.scriptLang:d.JS,[D.required]],jsScript:[e?e.jsScript:null,[D.required]],tbelScript:[e?e.tbelScript:null,[]]})}validatorTriggers(){return["scriptLang"]}updateValidators(e){let t=this.scriptConfigForm.get("scriptLang").value;t!==d.TBEL||this.tbelEnabled||(t=d.JS,this.scriptConfigForm.get("scriptLang").patchValue(t,{emitEvent:!1}),setTimeout((()=>{this.scriptConfigForm.updateValueAndValidity({emitEvent:!0})}))),this.scriptConfigForm.get("jsScript").setValidators(t===d.JS?[D.required]:[]),this.scriptConfigForm.get("jsScript").updateValueAndValidity({emitEvent:e}),this.scriptConfigForm.get("tbelScript").setValidators(t===d.TBEL?[D.required]:[]),this.scriptConfigForm.get("tbelScript").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=d.JS)),e}testScript(e){const t=this.scriptConfigForm.get("scriptLang").value,n=t===d.JS?"jsScript":"tbelScript",r=t===d.JS?"rulenode/transformation_node_script_fn":"rulenode/tbel/transformation_node_script_fn",o=this.scriptConfigForm.get(n).value;this.nodeScriptTestService.testNodeScript(o,"update",this.translate.instant("tb.rulenode.transformer"),"Transform",["msg","metadata","msgType"],this.ruleNodeId,r,t,e).subscribe((e=>{e&&(this.scriptConfigForm.get(n).setValue(e),this.changeScript.emit())}))}onValidate(){this.scriptConfigForm.get("scriptLang").value===d.JS&&this.jsFuncComponent.validateOnSubmit()}}e("TransformScriptConfigComponent",sr),sr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:sr,deps:[{token:G.Store},{token:E.UntypedFormBuilder},{token:Z.NodeScriptTestService},{token:j.TranslateService}],target:t.ɵɵFactoryTarget.Component}),sr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:sr,selector:"tb-transformation-node-script-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0},{propertyName:"tbelFuncComponent",first:!0,predicate:["tbelFuncComponent"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n \n \n \n
\n \n
\n
\n',dependencies:[{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ae.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","scriptLanguage","noValidate","required"]},{kind:"component",type:ie.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ie.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:se.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:me.TbScriptLangComponent,selector:"tb-script-lang",inputs:["disabled"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:sr,decorators:[{type:n,args:[{selector:"tb-transformation-node-script-config",template:'
\n \n \n \n \n \n \n \n
\n \n
\n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder},{type:Z.NodeScriptTestService},{type:j.TranslateService}]},propDecorators:{jsFuncComponent:[{type:o,args:["jsFuncComponent",{static:!1}]}],tbelFuncComponent:[{type:o,args:["tbelFuncComponent",{static:!1}]}]}});class mr extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.mailBodyTypes=[{name:"tb.mail-body-type.plain-text",value:"false"},{name:"tb.mail-body-type.html",value:"true"},{name:"tb.mail-body-type.dynamic",value:"dynamic"}]}configForm(){return this.toEmailConfigForm}onConfigurationSet(e){this.toEmailConfigForm=this.fb.group({fromTemplate:[e?e.fromTemplate:null,[D.required]],toTemplate:[e?e.toTemplate:null,[D.required]],ccTemplate:[e?e.ccTemplate:null,[]],bccTemplate:[e?e.bccTemplate:null,[]],subjectTemplate:[e?e.subjectTemplate:null,[D.required]],mailBodyType:[e?e.mailBodyType:null],isHtmlTemplate:[e?e.isHtmlTemplate:null,[D.required]],bodyTemplate:[e?e.bodyTemplate:null,[D.required]]})}updateValidators(e){"dynamic"===this.toEmailConfigForm.get("mailBodyType").value?this.toEmailConfigForm.get("isHtmlTemplate").enable({emitEvent:!1}):this.toEmailConfigForm.get("isHtmlTemplate").disable({emitEvent:!1}),this.toEmailConfigForm.get("isHtmlTemplate").updateValueAndValidity({emitEvent:e})}validatorTriggers(){return["mailBodyType"]}}e("ToEmailConfigComponent",mr),mr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:mr,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),mr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:mr,selector:"tb-transformation-node-to-email-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.from-template\n \n \n {{ \'tb.rulenode.from-template-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.to-template\n \n \n {{ \'tb.rulenode.to-template-required\' | translate }}\n \n \n \n \n tb.rulenode.cc-template\n \n \n \n \n tb.rulenode.bcc-template\n \n \n \n \n tb.rulenode.subject-template\n \n \n {{ \'tb.rulenode.subject-template-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.mail-body-type\n \n \n {{ type.name | translate }}\n \n \n \n \n tb.rulenode.dynamic-mail-body-type\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.body-template\n \n \n {{ \'tb.rulenode.body-template-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:Q.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:$.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:fe.SafePipe,name:"safe"},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:mr,decorators:[{type:n,args:[{selector:"tb-transformation-node-to-email-config",template:'
\n \n tb.rulenode.from-template\n \n \n {{ \'tb.rulenode.from-template-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.to-template\n \n \n {{ \'tb.rulenode.to-template-required\' | translate }}\n \n \n \n \n tb.rulenode.cc-template\n \n \n \n \n tb.rulenode.bcc-template\n \n \n \n \n tb.rulenode.subject-template\n \n \n {{ \'tb.rulenode.subject-template-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.mail-body-type\n \n \n {{ type.name | translate }}\n \n \n \n \n tb.rulenode.dynamic-mail-body-type\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.body-template\n \n \n {{ \'tb.rulenode.body-template-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class ur extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.separatorKeysCodes=[ue,pe,de]}onConfigurationSet(e){this.copyKeysConfigForm=this.fb.group({fromMetadata:[e?e.fromMetadata:null,[D.required]],keys:[e?e.keys:null,[D.required]]})}configForm(){return this.copyKeysConfigForm}removeKey(e){const t=this.copyKeysConfigForm.get("keys").value,n=t.indexOf(e);n>=0&&(t.splice(n,1),this.copyKeysConfigForm.get("keys").patchValue(t,{emitEvent:!0}))}addKey(e){const t=e.input;let n=e.value;if((n||"").trim()){n=n.trim();let e=this.copyKeysConfigForm.get("keys").value;e&&-1!==e.indexOf(n)||(e||(e=[]),e.push(n),this.copyKeysConfigForm.get("keys").patchValue(e,{emitEvent:!0}))}t&&(t.value="")}}e("CopyKeysConfigComponent",ur),ur.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ur,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),ur.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:ur,selector:"tb-transformation-node-copy-keys-config",usesInheritance:!0,ngImport:t,template:'
\n
{{\'tb.rulenode.copy-from\' | translate}}
\n \n \n {{\'tb.rulenode.data-to-metadata\' | translate}}\n \n \n {{\'tb.rulenode.metadata-to-data\' | translate}}\n \n \n \n \n \n {{key}}\n close\n \n \n \n {{ \'tb.rulenode.keys-required\' | translate }}\n tb.rulenode.use-regular-expression-hint\n \n
\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Je.MatRadioGroup,selector:"mat-radio-group",exportAs:["matRadioGroup"]},{kind:"component",type:Je.MatRadioButton,selector:"mat-radio-button",inputs:["disableRipple","tabIndex"],exportAs:["matRadioButton"]},{kind:"component",type:ce.MatChipGrid,selector:"mat-chip-grid",inputs:["tabIndex","disabled","placeholder","required","value","errorStateMatcher"],outputs:["change","valueChange"]},{kind:"directive",type:ce.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputFor","matChipInputAddOnBlur","matChipInputSeparatorKeyCodes","placeholder","id","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]},{kind:"directive",type:ce.MatChipRemove,selector:"[matChipRemove]"},{kind:"component",type:ce.MatChipRow,selector:"mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]",inputs:["color","disabled","disableRipple","tabIndex","editable"],outputs:["edited"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ur,decorators:[{type:n,args:[{selector:"tb-transformation-node-copy-keys-config",template:'
\n
{{\'tb.rulenode.copy-from\' | translate}}
\n \n \n {{\'tb.rulenode.data-to-metadata\' | translate}}\n \n \n {{\'tb.rulenode.metadata-to-data\' | translate}}\n \n \n \n \n \n {{key}}\n close\n \n \n \n {{ \'tb.rulenode.keys-required\' | translate }}\n tb.rulenode.use-regular-expression-hint\n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class pr extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.renameKeysConfigForm}onConfigurationSet(e){this.renameKeysConfigForm=this.fb.group({fromMetadata:[e?e.fromMetadata:null,[D.required]],renameKeysMapping:[e?e.renameKeysMapping:null,[D.required]]})}}e("RenameKeysConfigComponent",pr),pr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:pr,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),pr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:pr,selector:"tb-transformation-node-rename-keys-config",usesInheritance:!0,ngImport:t,template:'
\n
{{ \'tb.rulenode.rename-keys-in\' | translate }}
\n \n \n {{\'tb.rulenode.data\' | translate}}\n \n \n {{\'tb.rulenode.metadata\' | translate}}\n \n \n \n \n
\n',dependencies:[{kind:"directive",type:Je.MatRadioGroup,selector:"mat-radio-group",exportAs:["matRadioGroup"]},{kind:"component",type:Je.MatRadioButton,selector:"mat-radio-button",inputs:["disableRipple","tabIndex"],exportAs:["matRadioButton"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:on,selector:"tb-kv-map-config-old",inputs:["disabled","uniqueKeyValuePairValidator","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:pr,decorators:[{type:n,args:[{selector:"tb-transformation-node-rename-keys-config",template:'
\n
{{ \'tb.rulenode.rename-keys-in\' | translate }}
\n \n \n {{\'tb.rulenode.data\' | translate}}\n \n \n {{\'tb.rulenode.metadata\' | translate}}\n \n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class dr extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.jsonPathConfigForm}onConfigurationSet(e){this.jsonPathConfigForm=this.fb.group({jsonPath:[e?e.jsonPath:null,[D.required]]})}}e("NodeJsonPathConfigComponent",dr),dr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:dr,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),dr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:dr,selector:"tb-transformation-node-json-path-config",usesInheritance:!0,ngImport:t,template:"
\n \n {{ 'tb.rulenode.json-path-expression' | translate }}\n \n {{ 'tb.rulenode.json-path-expression-hint' | translate }}\n {{ 'tb.rulenode.json-path-expression-required' | translate }}\n \n
\n\n",dependencies:[{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatLabel,selector:"mat-label"},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:E.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:dr,decorators:[{type:n,args:[{selector:"tb-transformation-node-json-path-config",template:"
\n \n {{ 'tb.rulenode.json-path-expression' | translate }}\n \n {{ 'tb.rulenode.json-path-expression-hint' | translate }}\n {{ 'tb.rulenode.json-path-expression-required' | translate }}\n \n
\n\n"}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class cr extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.separatorKeysCodes=[ue,pe,de]}onConfigurationSet(e){this.deleteKeysConfigForm=this.fb.group({fromMetadata:[e?e.fromMetadata:null,[D.required]],keys:[e?e.keys:null,[D.required]]})}configForm(){return this.deleteKeysConfigForm}removeKey(e){const t=this.deleteKeysConfigForm.get("keys").value,n=t.indexOf(e);n>=0&&(t.splice(n,1),this.deleteKeysConfigForm.get("keys").patchValue(t,{emitEvent:!0}))}addKey(e){const t=e.input;let n=e.value;if((n||"").trim()){n=n.trim();let e=this.deleteKeysConfigForm.get("keys").value;e&&-1!==e.indexOf(n)||(e||(e=[]),e.push(n),this.deleteKeysConfigForm.get("keys").patchValue(e,{emitEvent:!0}))}t&&(t.value="")}}e("DeleteKeysConfigComponent",cr),cr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:cr,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),cr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:cr,selector:"tb-transformation-node-delete-keys-config",usesInheritance:!0,ngImport:t,template:'
\n
{{\'tb.rulenode.delete-from\' | translate}}
\n \n \n {{\'tb.rulenode.data\' | translate}}\n \n \n {{\'tb.rulenode.metadata\' | translate}}\n \n \n \n \n \n {{key}}\n close\n \n \n \n {{ \'tb.rulenode.keys-required\' | translate }}\n tb.rulenode.use-regular-expression-delete-hint\n \n
\n',dependencies:[{kind:"directive",type:B.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:B.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:le.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:_.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:z.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:z.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:z.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Je.MatRadioGroup,selector:"mat-radio-group",exportAs:["matRadioGroup"]},{kind:"component",type:Je.MatRadioButton,selector:"mat-radio-button",inputs:["disableRipple","tabIndex"],exportAs:["matRadioButton"]},{kind:"component",type:ce.MatChipGrid,selector:"mat-chip-grid",inputs:["tabIndex","disabled","placeholder","required","value","errorStateMatcher"],outputs:["change","valueChange"]},{kind:"directive",type:ce.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputFor","matChipInputAddOnBlur","matChipInputSeparatorKeyCodes","placeholder","id","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]},{kind:"directive",type:ce.MatChipRemove,selector:"[matChipRemove]"},{kind:"component",type:ce.MatChipRow,selector:"mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]",inputs:["color","disabled","disableRipple","tabIndex","editable"],outputs:["edited"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:H.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:j.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:cr,decorators:[{type:n,args:[{selector:"tb-transformation-node-delete-keys-config",template:'
\n
{{\'tb.rulenode.delete-from\' | translate}}
\n \n \n {{\'tb.rulenode.data\' | translate}}\n \n \n {{\'tb.rulenode.metadata\' | translate}}\n \n \n \n \n \n {{key}}\n close\n \n \n \n {{ \'tb.rulenode.keys-required\' | translate }}\n tb.rulenode.use-regular-expression-delete-hint\n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class fr{}e("RulenodeCoreConfigTransformModule",fr),fr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:fr,deps:[],target:t.ɵɵFactoryTarget.NgModule}),fr.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.10",ngImport:t,type:fr,declarations:[lr,sr,mr,ur,pr,dr,cr],imports:[K,I,In],exports:[lr,sr,mr,ur,pr,dr,cr]}),fr.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:fr,imports:[K,I,In]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:fr,decorators:[{type:l,args:[{declarations:[lr,sr,mr,ur,pr,dr,cr],imports:[K,I,In],exports:[lr,sr,mr,ur,pr,dr,cr]}]}]});class gr extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.entityType=x}configForm(){return this.ruleChainInputConfigForm}onConfigurationSet(e){this.ruleChainInputConfigForm=this.fb.group({ruleChainId:[e?e.ruleChainId:null,[D.required]]})}}e("RuleChainInputComponent",gr),gr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:gr,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),gr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:gr,selector:"tb-flow-node-rule-chain-input-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n
\n',dependencies:[{kind:"component",type:We.EntityAutocompleteComponent,selector:"tb-entity-autocomplete",inputs:["entityType","entitySubtype","excludeEntityIds","labelText","requiredText","useFullEntityId","appearance","required","disabled"],outputs:["entityChanged"]},{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:E.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:E.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:gr,decorators:[{type:n,args:[{selector:"tb-flow-node-rule-chain-input-config",template:'
\n \n \n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class yr extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.ruleChainOutputConfigForm}onConfigurationSet(e){this.ruleChainOutputConfigForm=this.fb.group({})}}e("RuleChainOutputComponent",yr),yr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:yr,deps:[{token:G.Store},{token:E.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),yr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:yr,selector:"tb-flow-node-rule-chain-output-config",usesInheritance:!0,ngImport:t,template:'
\n
\n
\n',dependencies:[{kind:"directive",type:H.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:E.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:E.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"pipe",type:j.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:yr,decorators:[{type:n,args:[{selector:"tb-flow-node-rule-chain-output-config",template:'
\n
\n
\n'}]}],ctorParameters:function(){return[{type:G.Store},{type:E.UntypedFormBuilder}]}});class xr{}e("RuleNodeCoreConfigFlowModule",xr),xr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:xr,deps:[],target:t.ɵɵFactoryTarget.NgModule}),xr.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.10",ngImport:t,type:xr,declarations:[gr,yr],imports:[K,I,In],exports:[gr,yr]}),xr.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:xr,imports:[K,I,In]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:xr,decorators:[{type:l,args:[{declarations:[gr,yr],imports:[K,I,In],exports:[gr,yr]}]}]});class br{constructor(e){!function(e){e.setTranslation("en_US",{tb:{rulenode:{id:"Id","additional-info":"Additional Info","create-entity-if-not-exists":"Create new entity if not exists","create-entity-if-not-exists-hint":"Create a new entity set above if it does not exist.","entity-name-pattern":"Name pattern","entity-name-pattern-required":"Name pattern is required","entity-type-pattern":"Type pattern","entity-type-pattern-required":"Type pattern is required","output-message-type":"Output message type","output-message-type-required":"Output message type is required","output-message-type-max-length":"Output message type should be less than 256","entity-cache-expiration":"Entities cache expiration time (sec)","entity-cache-expiration-hint":"Specifies maximum time interval allowed to store found entity records. 0 value means that records will never expire.","entity-cache-expiration-required":"Entities cache expiration time is required.","entity-cache-expiration-range":"Entities cache expiration time should be greater than or equal to 0.","customer-name-pattern":"Customer name pattern","customer-name-pattern-required":"Customer name pattern is required","create-customer-if-not-exists":"Create new customer if not exists","customer-cache-expiration":"Customers cache expiration time (sec)","customer-cache-expiration-hint":"Specifies maximum time interval allowed to store found customer records. 0 value means that records will never expire.","customer-cache-expiration-required":"Customers cache expiration time is required.","customer-cache-expiration-range":"Customers cache expiration time should be greater than or equal to 0.","interval-start":"Interval start","interval-end":"Interval end","time-unit":"Time unit","fetch-mode":"Fetch mode","order-by-timestamp":"Order by timestamp",limit:"Limit","limit-hint":"Min limit value is 2, max - 1000. If you want to fetch a single entry, select fetch mode 'First' or 'Last'.","limit-required":"Limit is required.","limit-range":"Limit should be in a range from 2 to 1000.","time-unit-milliseconds":"Milliseconds","time-unit-seconds":"Seconds","time-unit-minutes":"Minutes","time-unit-hours":"Hours","time-unit-days":"Days","time-value-range":"Allowing range from 1 to 2147483647.","start-interval-value-required":"Interval start is required.","end-interval-value-required":"Interval end is required.",filter:"Filter",switch:"Switch","math-templatization-tooltip":"This field support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","add-message-type":"Add message type","select-message-types-required":"At least one message type should be selected.","select-message-types":"Select message types","no-message-types-found":"No message types found","no-message-type-matching":"'{{messageType}}' not found.","create-new-message-type":"Create a new one.","message-types-required":"Message types are required.","client-attributes":"Client attributes","shared-attributes":"Shared attributes","server-attributes":"Server attributes","attributes-keys":"Attributes keys","attributes-keys-required":"Attributes keys are required","notify-device":"Notify device","send-attributes-updated-notification":"Send attributes updated notification","send-attributes-updated-notification-hint":"Send notification about updated attributes as a separate message to the rule engine queue.","send-attributes-deleted-notification":"Send attributes deleted notification","send-attributes-deleted-notification-hint":"Send notification about deleted attributes as a separate message to the rule engine queue.","update-attributes-only-on-value-change":"Save attributes only if the value changes","update-attributes-only-on-value-change-hint":"Updates the attributes on every incoming message disregarding if their value has changed. Increases API usage and reduces performance.","update-attributes-only-on-value-change-hint-enabled":"Updates the attributes only if their value has changed. If the value is not changed, no update to the attribute timestamp nor attribute change notification will be sent.","fetch-credentials-to-metadata":"Fetch credentials to metadata","notify-device-hint":"If the message arrives from the device, we will push it back to the device by default.","notify-device-delete-hint":"Send notification about deleted attributes to device.","latest-timeseries":"Latest time-series data keys","timeseries-keys":"Timeseries keys","timeseries-keys-required":"At least one timeseries key should be selected.","add-timeseries-key":"Add timeseries key","add-message-field":"Add message field","relation-search-parameters":"Relation search parameters","add-metadata-field":"Add metadata field","data-keys":"Message field names","copy-from":"Copy from","data-to-metadata":"Data to metadata","metadata-to-data":"Metadata to data","use-regular-expression-hint":"Use regular expression to copy keys by pattern",interval:"Interval","interval-required":"Interval is required","interval-hint":"Deduplication interval in seconds.","interval-min-error":"Min allowed value is 1","max-pending-msgs":"Max pending messages","max-pending-msgs-hint":"Maximum number of messages that are stored in memory for each unique deduplication id.","max-pending-msgs-required":"Max pending messages is required","max-pending-msgs-max-error":"Max allowed value is 1000","max-pending-msgs-min-error":"Min allowed value is 1","max-retries":"Max retries","max-retries-required":"Max retries is required","max-retries-hint":"Maximum number of retries to push the deduplicated messages into the queue. 10 seconds delay is used between retries","max-retries-max-error":"Max allowed value is 100","max-retries-min-error":"Min allowed value is 0",strategy:"Strategy","strategy-required":"Strategy is required","strategy-all-hint":"Return all messages that arrived during deduplication period as a single JSON array message. Where each element represents object with msg and metadata inner properties.","strategy-first-hint":"Return first message that arrived during deduplication period.","strategy-last-hint":"Return last message that arrived during deduplication period.",first:"First",last:"Last",all:"All","output-msg-type-hint":"The message type of the deduplication result.","queue-name-hint":"The queue name where the deduplication result will be published.",keys:"Keys","keys-required":"Keys are required","rename-keys-in":"Rename keys in",data:"Data",message:"Message",metadata:"Metadata","key-name":"Key name","key-name-required":"Key name is required","new-key-name":"New key name","new-key-name-required":"New key name is required","metadata-keys":"Metadata field names","json-path-expression":"JSON path expression","json-path-expression-required":"JSON path expression is required","json-path-expression-hint":"JSONPath specifies a path to an element or a set of elements in a JSON structure. '$' represents the root object or array.","relations-query":"Relations query","device-relations-query":"Device relations query","max-relation-level":"Max relation level","max-relation-level-error":"Value should be greater than 0 or unspecified.","relation-type":"Relation type","relation-type-pattern":"Relation type pattern","relation-type-pattern-required":"Relation type pattern is required","relation-types-list":"Relation types to propagate","relation-types-list-hint":"If Propagate relation types are not selected, alarms will be propagated without filtering by relation type.","unlimited-level":"Unlimited level","latest-telemetry":"Latest telemetry","add-telemetry-key":"Add telemetry key","delete-from":"Delete from","use-regular-expression-delete-hint":"Use regular expression to delete keys by pattern","fetch-into":"Fetch into","attr-mapping":"Attributes mapping:","source-attribute":"Source attribute key","source-attribute-required":"Source attribute key is required.","source-telemetry":"Source telemetry key","source-telemetry-required":"Source telemetry key is required.","target-key":"Target key","target-key-required":"Target key is required.","attr-mapping-required":"At least one mapping entry should be specified.","fields-mapping":"Fields mapping","relations-query-config-direction-suffix":"originator","add-device-profile":"Add device profile","profile-name":"Profile name","fetch-circle-parameter-info-from-metadata-hint":'Metadata field \'{{perimeterKeyName}}\' should be defined in next format: {"latitude":48.196, "longitude":24.6532, "radius":100.0, "radiusUnit":"METER"}',"fetch-poligon-parameter-info-from-metadata-hint":"Metadata field '{{perimeterKeyName}}' should be defined in next format: [[48.19736,24.65235],[48.19800,24.65060],...,[48.19849,24.65420]]","short-templatization-tooltip":"Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","fields-mapping-required":"At least one field mapping should be specified.","at-least-one-field-required":"At least one input field must have a value(s) provided.","originator-fields-sv-map-hint":"Target key fields support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","sv-map-hint":"Only target key fields support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","source-field":"Source field","source-field-required":"Source field is required.","originator-source":"Originator source","originator-customer":"Customer","originator-tenant":"Tenant","originator-related":"Related","originator-alarm-originator":"Alarm Originator","originator-entity":"Entity","clone-message":"Clone message",transform:"Transform","default-ttl":"Default TTL in seconds","default-ttl-required":"Default TTL is required.","min-default-ttl-message":"Only 0 minimum TTL is allowed.","message-count":"Message count (0 - unlimited)","message-count-required":"Message count is required.","min-message-count-message":"Only 0 minimum message count is allowed.","period-seconds":"Period in seconds","period-seconds-required":"Period is required.","use-metadata-period-in-seconds-patterns":"Use period in seconds pattern","use-metadata-period-in-seconds-patterns-hint":"If selected, rule node use period in seconds interval pattern from message metadata or data assuming that intervals are in the seconds.","period-in-seconds-pattern":"Period in seconds pattern","period-in-seconds-pattern-required":"Period in seconds pattern is required","min-period-seconds-message":"Only 1 second minimum period is allowed.",originator:"Originator","message-body":"Message body","message-metadata":"Message metadata",generate:"Generate","test-generator-function":"Test generator function",generator:"Generator","test-filter-function":"Test filter function","test-switch-function":"Test switch function","test-transformer-function":"Test transformer function",transformer:"Transformer","alarm-create-condition":"Alarm create condition","test-condition-function":"Test condition function","alarm-clear-condition":"Alarm clear condition","alarm-details-builder":"Alarm details builder","test-details-function":"Test details function","alarm-type":"Alarm type","select-entity-types":"Select entity types","alarm-type-required":"Alarm type is required.","alarm-severity":"Alarm severity","alarm-severity-required":"Alarm severity is required","alarm-severity-pattern":"Alarm severity pattern","alarm-status-filter":"Alarm status filter","alarm-status-list-empty":"Alarm status list is empty","no-alarm-status-matching":"No alarm status matching were found.",propagate:"Propagate alarm to related entities","propagate-to-owner":"Propagate alarm to entity owner (Customer or Tenant)","propagate-to-tenant":"Propagate alarm to Tenant",condition:"Condition",details:"Details","to-string":"To string","test-to-string-function":"Test to string function","from-template":"From Template","from-template-required":"From Template is required","to-template":"To Template","to-template-required":"To Template is required","mail-address-list-template-hint":'Comma separated address list, use ${metadataKey} for value from metadata, $[messageKey] for value from message body',"cc-template":"Cc Template","bcc-template":"Bcc Template","subject-template":"Subject Template","subject-template-required":"Subject Template is required","body-template":"Body Template","body-template-required":"Body Template is required","dynamic-mail-body-type":"Dynamic mail body type","mail-body-type":"Mail body type","request-id-metadata-attribute":"Request Id Metadata attribute name","timeout-sec":"Timeout in seconds","timeout-required":"Timeout is required","min-timeout-message":"Only 0 minimum timeout value is allowed.","endpoint-url-pattern":"Endpoint URL pattern","endpoint-url-pattern-required":"Endpoint URL pattern is required","request-method":"Request method","use-simple-client-http-factory":"Use simple client HTTP factory","ignore-request-body":"Without request body","parse-to-plain-text":"Parse to plain text","parse-to-plain-text-hint":'If selected, request body message payload will be transformed from JSON string to plain text, e.g. msg = "Hello,\\t\\"world\\"" will be parsed to Hello, "world"',"read-timeout":"Read timeout in millis","read-timeout-hint":"The value of 0 means an infinite timeout","max-parallel-requests-count":"Max number of parallel requests","max-parallel-requests-count-hint":"The value of 0 specifies no limit in parallel processing",headers:"Headers","headers-hint":'Use ${metadataKey} for value from metadata, $[messageKey] for value from message body in header/value fields',header:"Header","header-required":"Header is required",value:"Value","value-required":"Value is required","topic-pattern":"Topic pattern","key-pattern":"Key pattern","key-pattern-hint":"Optional. If a valid partition number is specified, it will be used when sending the record. If no partition is specified, the key will be used instead. If neither is specified, a partition will be assigned in a round-robin fashion.","topic-pattern-required":"Topic pattern is required",topic:"Topic","topic-required":"Topic is required","bootstrap-servers":"Bootstrap servers","bootstrap-servers-required":"Bootstrap servers value is required","other-properties":"Other properties",key:"Key","key-required":"Key is required",retries:"Automatically retry times if fails","min-retries-message":"Only 0 minimum retries is allowed.","batch-size-bytes":"Produces batch size in bytes","min-batch-size-bytes-message":"Only 0 minimum batch size is allowed.","linger-ms":"Time to buffer locally (ms)","min-linger-ms-message":"Only 0 ms minimum value is allowed.","buffer-memory-bytes":"Client buffer max size in bytes","min-buffer-memory-message":"Only 0 minimum buffer size is allowed.",acks:"Number of acknowledgments","key-serializer":"Key serializer","key-serializer-required":"Key serializer is required","value-serializer":"Value serializer","value-serializer-required":"Value serializer is required","topic-arn-pattern":"Topic ARN pattern","topic-arn-pattern-required":"Topic ARN pattern is required","aws-access-key-id":"AWS Access Key ID","aws-access-key-id-required":"AWS Access Key ID is required","aws-secret-access-key":"AWS Secret Access Key","aws-secret-access-key-required":"AWS Secret Access Key is required","aws-region":"AWS Region","aws-region-required":"AWS Region is required","exchange-name-pattern":"Exchange name pattern","routing-key-pattern":"Routing key pattern","message-properties":"Message properties",host:"Host","host-required":"Host is required",port:"Port","port-required":"Port is required","port-range":"Port should be in a range from 1 to 65535.","virtual-host":"Virtual host",username:"Username",password:"Password","automatic-recovery":"Automatic recovery","connection-timeout-ms":"Connection timeout (ms)","min-connection-timeout-ms-message":"Only 0 ms minimum value is allowed.","handshake-timeout-ms":"Handshake timeout (ms)","min-handshake-timeout-ms-message":"Only 0 ms minimum value is allowed.","client-properties":"Client properties","queue-url-pattern":"Queue URL pattern","queue-url-pattern-required":"Queue URL pattern is required","delay-seconds":"Delay (seconds)","min-delay-seconds-message":"Only 0 seconds minimum value is allowed.","max-delay-seconds-message":"Only 900 seconds maximum value is allowed.",name:"Name","name-required":"Name is required","queue-type":"Queue type","sqs-queue-standard":"Standard","sqs-queue-fifo":"FIFO","gcp-project-id":"GCP project ID","gcp-project-id-required":"GCP project ID is required","gcp-service-account-key":"GCP service account key file","gcp-service-account-key-required":"GCP service account key file is required","pubsub-topic-name":"Topic name","pubsub-topic-name-required":"Topic name is required","message-attributes":"Message attributes","message-attributes-hint":'Use ${metadataKey} for value from metadata, $[messageKey] for value from message body in name/value fields',"connect-timeout":"Connection timeout (sec)","connect-timeout-required":"Connection timeout is required.","connect-timeout-range":"Connection timeout should be in a range from 1 to 200.","client-id":"Client ID","client-id-hint":'Optional. Leave empty for auto-generated Client ID. Be careful when specifying the Client ID. Majority of the MQTT brokers will not allow multiple connections with the same Client ID. To connect to such brokers, your mqtt Client ID must be unique. When platform is running in a micro-services mode, the copy of rule node is launched in each micro-service. This will automatically lead to multiple mqtt clients with the same ID and may cause failures of the rule node. To avoid such failures enable "Add Service ID as suffix to Client ID" option below.',"append-client-id-suffix":"Add Service ID as suffix to Client ID","client-id-suffix-hint":'Optional. Applied when "Client ID" specified explicitly. If selected then Service ID will be added to Client ID as a suffix. Helps to avoid failures when platform is running in a micro-services mode.',"device-id":"Device ID","device-id-required":"Device ID is required.","clean-session":"Clean session","enable-ssl":"Enable SSL",credentials:"Credentials","credentials-type":"Credentials type","credentials-type-required":"Credentials type is required.","credentials-anonymous":"Anonymous","credentials-basic":"Basic","credentials-pem":"PEM","credentials-pem-hint":"At least Server CA certificate file or a pair of Client certificate and Client private key files are required","credentials-sas":"Shared Access Signature","sas-key":"SAS Key","sas-key-required":"SAS Key is required.",hostname:"Hostname","hostname-required":"Hostname is required.","azure-ca-cert":"CA certificate file","username-required":"Username is required.","password-required":"Password is required.","ca-cert":"Server CA certificate file","private-key":"Client private key file",cert:"Client certificate file","no-file":"No file selected.","drop-file":"Drop a file or click to select a file to upload.","private-key-password":"Private key password","use-system-smtp-settings":"Use system SMTP settings","use-metadata-dynamic-interval":"Use dynamic interval","metadata-dynamic-interval-hint":"Interval start and end input fields support templatization. Note that the substituted template value should be set in milliseconds. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","use-metadata-interval-patterns-hint":"If selected, rule node use start and end interval patterns from message metadata or data assuming that intervals are in the milliseconds.","use-message-alarm-data":"Use message alarm data","overwrite-alarm-details":"Overwrite alarm details","use-alarm-severity-pattern":"Use alarm severity pattern","check-all-keys":"Check that all specified fields are present","check-all-keys-hint":"If selected, checks that all specified keys are present in the message data and metadata.","check-relation-to-specific-entity":"Check relation to specific entity","check-relation-to-specific-entity-tooltip":"If enabled, checks the presence of relation with a specific entity otherwise, checks the presence of relation with any entity. In both cases, relation lookup is based on configured direction and type.","check-relation-hint":"Checks existence of relation to specific entity or to any entity based on direction and relation type.","delete-relation-to-specific-entity":"Delete relation to specific entity","delete-relation-hint":"Deletes relation from the originator of the incoming message to the specified entity or list of entities based on direction and type.","remove-current-relations":"Remove current relations","remove-current-relations-hint":"Removes current relations from the originator of the incoming message based on direction and type.","change-originator-to-related-entity":"Change originator to related entity","change-originator-to-related-entity-hint":"Used to process submitted message as a message from another entity.","start-interval":"Interval start","end-interval":"Interval end","start-interval-required":"Interval start is required.","end-interval-required":"Interval end is required.","smtp-protocol":"Protocol","smtp-host":"SMTP host","smtp-host-required":"SMTP host is required.","smtp-port":"SMTP port","smtp-port-required":"You must supply a smtp port.","smtp-port-range":"SMTP port should be in a range from 1 to 65535.","timeout-msec":"Timeout ms","min-timeout-msec-message":"Only 0 ms minimum value is allowed.","enter-username":"Enter username","enter-password":"Enter password","enable-tls":"Enable TLS","tls-version":"TLS version","enable-proxy":"Enable proxy","use-system-proxy-properties":"Use system proxy properties","proxy-host":"Proxy host","proxy-host-required":"Proxy host is required.","proxy-port":"Proxy port","proxy-port-required":"Proxy port is required.","proxy-port-range":"Proxy port should be in a range from 1 to 65535.","proxy-user":"Proxy user","proxy-password":"Proxy password","proxy-scheme":"Proxy scheme","numbers-to-template":"Phone Numbers To Template","numbers-to-template-required":"Phone Numbers To Template is required","numbers-to-template-hint":'Comma separated Phone Numbers, use ${metadataKey} for value from metadata, $[messageKey] for value from message body',"sms-message-template":"SMS message Template","sms-message-template-required":"SMS message Template is required","use-system-sms-settings":"Use system SMS provider settings","min-period-0-seconds-message":"Only 0 second minimum period is allowed.","max-pending-messages":"Maximum pending messages","max-pending-messages-required":"Maximum pending messages is required.","max-pending-messages-range":"Maximum pending messages should be in a range from 1 to 100000.","originator-types-filter":"Originator types filter","interval-seconds":"Interval in seconds","interval-seconds-required":"Interval is required.","min-interval-seconds-message":"Only 1 second minimum interval is allowed.","output-timeseries-key-prefix":"Output timeseries key prefix","output-timeseries-key-prefix-required":"Output timeseries key prefix required.","separator-hint":'Press "Enter" to complete field input.',"select-details":"Select details","entity-details-id":"Id","entity-details-title":"Title","entity-details-country":"Country","entity-details-state":"State","entity-details-city":"City","entity-details-zip":"Zip","entity-details-address":"Address","entity-details-address2":"Address2","entity-details-additional_info":"Additional Info","entity-details-phone":"Phone","entity-details-email":"Email","fields-to-check":"Fields to check","add-detail":"Add detail","check-all-keys-tooltip":"If enabled, checks the presence of all fields listed in the message and metadata field names within the incoming message and its metadata.","fields-to-check-hint":'Press "Enter" to complete field name input. Multiple field names supported.',"entity-details-list-empty":"At least one detail should be selected.","alarm-status":"Alarm status","alarm-required":"At least one alarm status should be selected.","no-entity-details-matching":"No entity details matching were found.","custom-table-name":"Custom table name","custom-table-name-required":"Table Name is required","custom-table-hint":"Enter the table name without prefix 'cs_tb_'.","message-field":"Message field","message-field-required":"Message field is required.","table-col":"Table column","table-col-required":"Table column is required.","latitude-field-name":"Latitude field name","longitude-field-name":"Longitude field name","latitude-field-name-required":"Latitude field name is required.","longitude-field-name-required":"Longitude field name is required.","fetch-perimeter-info-from-metadata":"Fetch perimeter information from metadata","fetch-perimeter-info-from-metadata-tooltip":"If perimeter type is set to 'Polygon' the value of metadata field '{{perimeterKeyName}}' will be set as perimeter definition without additional parsing of the value. Otherwise, if perimeter type is set to 'Circle' the value of '{{perimeterKeyName}}' metadata field will be parsed to extract 'latitude', 'longitude', 'radius', 'radiusUnit' fields that uses for circle perimeter definition.","perimeter-key-name":"Perimeter key name","perimeter-key-name-hint":"Metadata field name that includes perimeter information.","perimeter-key-name-required":"Perimeter key name is required.","perimeter-circle":"Circle","perimeter-polygon":"Polygon","perimeter-type":"Perimeter type","circle-center-latitude":"Center latitude","circle-center-latitude-required":"Center latitude is required.","circle-center-longitude":"Center longitude","circle-center-longitude-required":"Center longitude is required.","range-unit-meter":"Meter","range-unit-kilometer":"Kilometer","range-unit-foot":"Foot","range-unit-mile":"Mile","range-unit-nautical-mile":"Nautical mile","range-units":"Range units","range-units-required":"Range units is required.",range:"Range","range-required":"Range is required.","polygon-definition":"Polygon definition","polygon-definition-required":"Polygon definition is required.","polygon-definition-hint":"Use the following format for manual definition of polygon: [[lat1,lon1],[lat2,lon2], ... ,[latN,lonN]].","min-inside-duration":"Minimal inside duration","min-inside-duration-value-required":"Minimal inside duration is required","min-inside-duration-time-unit":"Minimal inside duration time unit","min-outside-duration":"Minimal outside duration","min-outside-duration-value-required":"Minimal outside duration is required","min-outside-duration-time-unit":"Minimal outside duration time unit","tell-failure-if-absent":"Tell Failure","tell-failure-if-absent-hint":'If at least one selected key doesn\'t exist the outbound message will report "Failure".',"get-latest-value-with-ts":"Fetch timestamp for the latest telemetry values","get-latest-value-with-ts-hint":'If selected, the latest telemetry values will also include timestamp, e.g: "temp": "{"ts":1574329385897, "value":42}"',"use-redis-queue":"Use redis queue for message persistence","ignore-null-strings":"Ignore null strings","ignore-null-strings-hint":"If selected rule node will ignore entity fields with empty value.","trim-redis-queue":"Trim redis queue","redis-queue-max-size":"Redis queue max size","add-metadata-key-values-as-kafka-headers":"Add Message metadata key-value pairs to Kafka record headers","add-metadata-key-values-as-kafka-headers-hint":"If selected, key-value pairs from message metadata will be added to the outgoing records headers as byte arrays with predefined charset encoding.","charset-encoding":"Charset encoding","charset-encoding-required":"Charset encoding is required.","charset-us-ascii":"US-ASCII","charset-iso-8859-1":"ISO-8859-1","charset-utf-8":"UTF-8","charset-utf-16be":"UTF-16BE","charset-utf-16le":"UTF-16LE","charset-utf-16":"UTF-16","select-queue-hint":"The queue name can be selected from a drop-down list or add a custom name.","persist-alarm-rules":"Persist state of alarm rules","fetch-alarm-rules":"Fetch state of alarm rules","input-value-key":"Input value key","input-value-key-required":"Input value key is required.","output-value-key":"Output value key","output-value-key-required":"Output value key is required.","number-of-digits-after-floating-point":"Number of digits after floating point","number-of-digits-after-floating-point-range":"Number of digits after floating point should be in a range from 0 to 15.","failure-if-delta-negative":"Tell Failure if delta is negative","failure-if-delta-negative-tooltip":"Rule node forces failure of message processing if delta value is negative.","use-caching":"Use caching","use-caching-tooltip":'Rule node will cache the value of "{{inputValueKey}}" that arrives from the incoming message to improve performance. Note that the cache will not be updated if you modify the "{{inputValueKey}}" value elsewhere.',"add-time-difference-between-readings":'Add the time difference between "{{inputValueKey}}" readings',"add-time-difference-between-readings-tooltip":'If enabled, the rule node will add the "{{periodValueKey}}" to the outbound message.',"period-value-key":"Period value key","period-value-key-required":"Period value key is required.","general-pattern-hint":"Use ${metadataKey} for value from metadata, $[messageKey] for value from message body.","alarm-severity-pattern-hint":'Use ${metadataKey} for value from metadata, $[messageKey] for value from message body. Alarm severity should be system (CRITICAL, MAJOR etc.)',"output-node-name-hint":"The rule node name corresponds to the relation type of the output message, and it is used to forward messages to other rule nodes in the caller rule chain.","skip-latest-persistence":"Skip latest persistence","use-server-ts":"Use server ts","use-server-ts-hint":"Enable this setting to use the timestamp of the message processing instead of the timestamp from the message. Useful for all sorts of sequential processing if you merge messages from multiple sources (devices, assets, etc).","kv-map-pattern-hint":"All input fields support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","shared-scope":"Shared scope","server-scope":"Server scope","client-scope":"Client scope","attribute-type":"Attribute","constant-type":"Constant","time-series-type":"Time-series","message-body-type":"Message","message-metadata-type":"Metadata","argument-tile":"Arguments","no-arguments-prompt":"No arguments configured","result-title":"Result","functions-field-input":"Functions","no-option-found":"No option found","argument-source-field-input":"Source","argument-source-field-input-required":"Argument source is required.","argument-key-field-input":"Key","argument-key-field-input-required":"Argument key is required.","constant-value-field-input":"Constant value","constant-value-field-input-required":"Constant value is required.","attribute-scope-field-input":"Attribute scope","attribute-scope-field-input-required":"Attribute scope os required.","default-value-field-input":"Default value","type-field-input":"Type","type-field-input-required":"Type is required.","key-field-input":"Key","add-entity-type":"Add entity type","key-field-input-required":"Key is required.","number-floating-point-field-input":"Number of digits after floating point","number-floating-point-field-input-hint":"Use 0 to convert result to integer","add-to-message-field-input":"Add to message","add-to-metadata-field-input":"Add to metadata","custom-expression-field-input":"Mathematical Expression","custom-expression-field-input-required":"Mathematical expression is required","custom-expression-field-input-hint":"Specify a mathematical expression to evaluate. Default expression demonstrates how to transform Fahrenheit to Celsius","retained-message":"Retained","attributes-mapping":"Attributes mapping","latest-telemetry-mapping":"Latest telemetry mapping","add-mapped-attribute-to":"Add mapped attributes to","add-mapped-latest-telemetry-to":"Add mapped latest telemetry to","add-mapped-fields-to":"Add mapped fields to","add-selected-details-to":"Add selected details to","clear-selected-types":"Clear selected types","clear-selected-details":"Clear selected details","clear-selected-fields":"Clear selected fields","clear-selected-keys":"Clear selected keys","geofence-configuration":"Geofence configuration","coordinate-field-names":"Coordinate field names","coordinate-field-hint":"Rule node tries to retrieve the specified fields from the message. If they are not present, it will look them up in the metadata.","fetch-credentials-to":"Fetch credentials to","add-originator-attributes-to":"Add originator attributes to","originator-attributes":"Originator attributes","fetch-latest-telemetry-with-timestamp":"Fetch latest telemetry with timestamp","fetch-latest-telemetry-with-timestamp-tooltip":'If selected, latest telemetry values will be added to the outbound metadata with timestamp, e.g: "{{latestTsKeyName}}": "{"ts":1574329385897, "value":42}"',"tell-failure":"Tell failure if any of the attributes are missing","tell-failure-tooltip":'If at least one selected key doesn\'t exist the outbound message will report "Failure".',"created-time":"Created time","chip-help":"Press 'Enter' to complete {{inputName}} input. \nPress 'Backspace' to delete {{inputName}}. \nMultiple values supported.",detail:"detail","field-name":"field name","device-profile":"device profile","entity-type":"entity type","message-type":"message type","timeseries-key":"timeseries key",type:"Type","first-name":"First name","last-name":"Last name",label:"Label","originator-fields-mapping":"Originator fields mapping","add-mapped-originator-fields-to":"Add mapped originator fields to",fields:"Fields","skip-empty-fields":"Skip empty fields","skip-empty-fields-tooltip":"Fields with empty values will not be added to the output message/output metadata.","fetch-interval":"Fetch interval","fetch-strategy":"Fetch strategy","fetch-timeseries-from-to":"Fetch timeseries from {{startInterval}} {{startIntervalTimeUnit}} ago to {{endInterval}} {{endIntervalTimeUnit}} ago.","fetch-timeseries-from-to-invalid":'Fetch timeseries invalid ("Interval start" should be less than "Interval end").',"use-metadata-dynamic-interval-tooltip":"If selected, the rule node will use dynamic interval start and end based on the message and metadata patterns.","all-mode-hint":'If selected fetch mode "All" rule node will retrieve telemetry from the fetch interval with configurable query parameters.',"first-mode-hint":'If selected fetch mode "First" rule node will retrieve the closest telemetry to the fetch interval\'s start.',"last-mode-hint":'If selected fetch mode "Last" rule node will retrieve the closest telemetry to the fetch interval\'s end.',ascending:"Ascending",descending:"Descending",min:"Min",max:"Max",average:"Average",sum:"Sum",count:"Count",none:"None","last-level-relation-tooltip":"If selected, the rule node will search related entities only on the level set in the max relation level.","last-level-device-relation-tooltip":"If selected, the rule node will search related devices only on the level set in the max relation level.","data-to-fetch":"Data to fetch","mapping-of-customers":"Mapping of customer's","map-fields-required":"All mapping fields are required.",attributes:"Attributes","related-device-attributes":"Related device attributes","add-selected-attributes-to":"Add selected attributes to","device-profiles":"Device profiles","mapping-of-tenant":"Mapping of tenant's","add-attribute-key":"Add attribute key","message-template":"Message template","message-template-required":"Message template is required","use-system-slack-settings":"Use system slack settings","slack-api-token":"Slack API token","slack-api-token-required":"Slack API token is required"},"key-val":{key:"Key",value:"Value","see-examples":"See examples.","remove-entry":"Remove entry","remove-mapping-entry":"Remove mapping entry","add-mapping-entry":"Add mapping","add-entry":"Add entry","unique-key-value-pair-error":"'{{valText}}' must be different from the current '{{keyText}}'"},"mail-body-type":{"plain-text":"Plain Text",html:"HTML",dynamic:"Dynamic"}}},!0)}(e)}}e("RuleNodeCoreConfigModule",br),br.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:br,deps:[{token:j.TranslateService}],target:t.ɵɵFactoryTarget.NgModule}),br.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.10",ngImport:t,type:br,declarations:[Ze],imports:[K,I],exports:[Nn,ir,Rn,Wn,fr,xr,Ze]}),br.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:br,imports:[K,I,Nn,ir,Rn,Wn,fr,xr]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:br,decorators:[{type:l,args:[{declarations:[Ze],imports:[K,I],exports:[Nn,ir,Rn,Wn,fr,xr,Ze]}]}],ctorParameters:function(){return[{type:j.TranslateService}]}})}}}));//# sourceMappingURL=rulenode-core-config.js.map From c021c64eab84f1e54b645bca014fafa0c524af53 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 9 Nov 2023 15:04:51 +0200 Subject: [PATCH 072/252] Update pkg.upgradeVersion --- msa/tb/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/msa/tb/pom.xml b/msa/tb/pom.xml index 4f0d14aabb..c67de4b7dc 100644 --- a/msa/tb/pom.xml +++ b/msa/tb/pom.xml @@ -38,7 +38,7 @@ tb-postgres tb-cassandra /usr/share/${pkg.name} - 3.6.0 + 3.6.1 From 0a9c769afe1ac64fd64585b91af562b3aa3d228b Mon Sep 17 00:00:00 2001 From: Dmitriymush Date: Thu, 9 Nov 2023 15:14:35 +0200 Subject: [PATCH 073/252] UI: added default value to colorSettings type and default case for colorProcessor --- .../lib/settings/common/color-settings-panel.component.ts | 2 +- ui-ngx/src/app/shared/models/widget-settings.models.ts | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings-panel.component.ts index 26d838abc0..6ae04ec94e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings-panel.component.ts @@ -65,7 +65,7 @@ export class ColorSettingsPanelComponent extends PageComponent implements OnInit ngOnInit(): void { this.colorSettingsFormGroup = this.fb.group( { - type: [this.colorSettings?.type, []], + type: [this.colorSettings?.type || ColorType.constant, []], color: [this.colorSettings?.color, []], rangeList: [this.colorSettings?.rangeList, []], colorFunction: [this.colorSettings?.colorFunction, []] diff --git a/ui-ngx/src/app/shared/models/widget-settings.models.ts b/ui-ngx/src/app/shared/models/widget-settings.models.ts index 165a4369bc..27b32621aa 100644 --- a/ui-ngx/src/app/shared/models/widget-settings.models.ts +++ b/ui-ngx/src/app/shared/models/widget-settings.models.ts @@ -161,6 +161,8 @@ export abstract class ColorProcessor { return new RangeColorProcessor(settings); case ColorType.function: return new FunctionColorProcessor(settings); + default: + return new ConstantColorProcessor(settings); } } From 7d1e12d4b18a46350585971d9006932514f5b1c7 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Thu, 9 Nov 2023 15:15:12 +0200 Subject: [PATCH 074/252] fixed save method to not put into cache --- .../dao/asset/AssetProfileServiceImpl.java | 9 +++-- .../dao/device/DeviceProfileServiceImpl.java | 8 ++-- .../server/dao/device/DeviceServiceImpl.java | 6 ++- .../dao/entityview/EntityViewServiceImpl.java | 4 +- .../server/dao/service/AssetServiceTest.java | 39 +++++++++++++++++++ .../server/dao/service/DeviceServiceTest.java | 29 ++++++++++++++ 6 files changed, 83 insertions(+), 12 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileServiceImpl.java index 9240bbc223..fd17c484d7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileServiceImpl.java @@ -126,8 +126,8 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService idComparator = new IdComparator<>(); @@ -75,6 +91,29 @@ public class AssetServiceTest extends AbstractServiceTest { assetService.deleteAsset(tenantId, savedAsset.getId()); } + @Test + public void testShouldNotPutInCacheRolledbackAssetProfile() { + AssetProfile assetProfile = new AssetProfile(); + assetProfile.setName(StringUtils.randomAlphabetic(10)); + assetProfile.setTenantId(tenantId); + + Asset asset = new Asset(); + asset.setName("My asset" + StringUtils.randomAlphabetic(15)); + asset.setType(assetProfile.getName()); + asset.setTenantId(tenantId); + + DefaultTransactionDefinition def = new DefaultTransactionDefinition(); + TransactionStatus status = platformTransactionManager.getTransaction(def); + try { + assetProfileService.saveAssetProfile(assetProfile); + assetService.saveAsset(asset); + } finally { + platformTransactionManager.rollback(status); + } + AssetProfile assetProfileByName = assetProfileService.findAssetProfileByName(tenantId, assetProfile.getName()); + Assert.assertNull(assetProfileByName); + } + @Test public void testSaveAssetWithEmptyName() { Asset asset = new Asset(); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/DeviceServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/DeviceServiceTest.java index 1073f20be4..3e404f8d1d 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/DeviceServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/DeviceServiceTest.java @@ -22,6 +22,9 @@ import org.junit.Before; import org.junit.Test; import org.junit.jupiter.api.Assertions; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.support.DefaultTransactionDefinition; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceInfo; @@ -32,6 +35,8 @@ import org.thingsboard.server.common.data.OtaPackage; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.TenantProfile; +import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; @@ -72,6 +77,8 @@ public class DeviceServiceTest extends AbstractServiceTest { OtaPackageService otaPackageService; @Autowired TenantProfileService tenantProfileService; + @Autowired + private PlatformTransactionManager platformTransactionManager; private IdComparator idComparator = new IdComparator<>(); private TenantId anotherTenantId; @@ -305,6 +312,28 @@ public class DeviceServiceTest extends AbstractServiceTest { }); } + @Test + public void testShouldNotPutInCacheRolledbackDeviceProfile() { + DeviceProfile deviceProfile = createDeviceProfile(tenantId, "New device Profile" + StringUtils.randomAlphabetic(5)); + + + Device device = new Device(); + device.setType(deviceProfile.getName()); + device.setTenantId(tenantId); + device.setName("My device"+ StringUtils.randomAlphabetic(5)); + + DefaultTransactionDefinition def = new DefaultTransactionDefinition(); + TransactionStatus status = platformTransactionManager.getTransaction(def); + try { + deviceProfileService.saveDeviceProfile(deviceProfile); + deviceService.saveDevice(device); + } finally { + platformTransactionManager.rollback(status); + } + DeviceProfile deviceProfileByName = deviceProfileService.findDeviceProfileByName(tenantId, deviceProfile.getName()); + Assert.assertNull(deviceProfileByName); + } + @Test public void testAssignDeviceToNonExistentCustomer() { Device device = new Device(); From d52f745909854eb319231a275380d00aaf8b71f9 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Thu, 9 Nov 2023 15:37:49 +0200 Subject: [PATCH 075/252] UI: Convert widget style to camel case --- ui-ngx/src/app/core/utils.ts | 4 ++++ .../home/models/dashboard-component.models.ts | 17 +++++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/ui-ngx/src/app/core/utils.ts b/ui-ngx/src/app/core/utils.ts index ec02a0d117..b810689b69 100644 --- a/ui-ngx/src/app/core/utils.ts +++ b/ui-ngx/src/app/core/utils.ts @@ -817,3 +817,7 @@ export const getOS = (): string => { export const camelCase = (str: string): string => { return _.camelCase(str); }; + +export const convertKeysToCamelCase = (obj: Record): Record => { + return _.mapKeys(obj, (value, key) => _.camelCase(key)); +}; diff --git a/ui-ngx/src/app/modules/home/models/dashboard-component.models.ts b/ui-ngx/src/app/modules/home/models/dashboard-component.models.ts index f49a5454f1..cb9bd0b6f0 100644 --- a/ui-ngx/src/app/modules/home/models/dashboard-component.models.ts +++ b/ui-ngx/src/app/modules/home/models/dashboard-component.models.ts @@ -27,7 +27,15 @@ import { WidgetLayout, WidgetLayouts } from '@app/shared/models/dashboard.models import { IDashboardWidget, WidgetAction, WidgetContext, WidgetHeaderAction } from './widget-component.models'; import { Timewindow } from '@shared/models/time/time.models'; import { Observable, of, Subject } from 'rxjs'; -import { formattedDataFormDatasourceData, guid, isDefined, isEqual, isUndefined } from '@app/core/utils'; +import { + convertKeysToCamelCase, + formattedDataFormDatasourceData, + guid, + isDefined, + isEmpty, + isEqual, + isUndefined +} from '@app/core/utils'; import { IterableDiffer, KeyValueDiffer } from '@angular/core'; import { IAliasController, IStateController } from '@app/core/api/widget-api.models'; import { enumerable } from '@shared/decorators/enumerable'; @@ -479,9 +487,10 @@ export class DashboardWidget implements GridsterItem, IDashboardWidget { backgroundColor: this.backgroundColor, padding: this.padding, margin: this.margin, - borderRadius: this.borderRadius || 'unset' }; - if (this.widget.config.widgetStyle) { - this.style = {...this.style, ...this.widget.config.widgetStyle}; + borderRadius: this.borderRadius }; + if (this.widget.config.widgetStyle && !isEmpty(this.widget.config.widgetStyle)) { + const convertedStyle = convertKeysToCamelCase(this.widget.config.widgetStyle); + this.style = {...this.style, ...convertedStyle}; } this.showWidgetTitlePanel = this.widgetContext.hideTitlePanel ? false : From 694f9a0dd48a6b5628842eb1dc9f047e838b7cbb Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Thu, 9 Nov 2023 15:50:41 +0200 Subject: [PATCH 076/252] UI: Refactoring --- .../app/modules/home/models/dashboard-component.models.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/app/modules/home/models/dashboard-component.models.ts b/ui-ngx/src/app/modules/home/models/dashboard-component.models.ts index cb9bd0b6f0..b218504ad1 100644 --- a/ui-ngx/src/app/modules/home/models/dashboard-component.models.ts +++ b/ui-ngx/src/app/modules/home/models/dashboard-component.models.ts @@ -488,9 +488,8 @@ export class DashboardWidget implements GridsterItem, IDashboardWidget { padding: this.padding, margin: this.margin, borderRadius: this.borderRadius }; - if (this.widget.config.widgetStyle && !isEmpty(this.widget.config.widgetStyle)) { - const convertedStyle = convertKeysToCamelCase(this.widget.config.widgetStyle); - this.style = {...this.style, ...convertedStyle}; + if (!isEmpty(this.widget.config.widgetStyle)) { + this.style = {...this.style, ...convertKeysToCamelCase(this.widget.config.widgetStyle)}; } this.showWidgetTitlePanel = this.widgetContext.hideTitlePanel ? false : From c0c649b2d8b55b15bc1b64a20df78e277ea9a294 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 9 Nov 2023 16:28:20 +0200 Subject: [PATCH 077/252] UI: Minor fixes --- .../basic/chart/doughnut-basic-config.component.ts | 6 ++---- .../widget/lib/chart/doughnut-widget.component.ts | 2 +- .../widget/lib/chart/doughnut-widget.models.ts | 2 ++ .../chart/doughnut-widget-settings.component.ts | 10 +++------- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/doughnut-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/doughnut-basic-config.component.ts index 77e86a697c..f8149bc97d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/doughnut-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/doughnut-basic-config.component.ts @@ -40,7 +40,7 @@ import { doughnutLayoutImages, doughnutLayouts, doughnutLayoutTranslations, - DoughnutLegendPosition, + doughnutLegendPositions, doughnutLegendPositionTranslations, DoughnutTooltipValueType, doughnutTooltipValueTypes, @@ -83,7 +83,7 @@ export class DoughnutBasicConfigComponent extends BasicWidgetConfigComponent { doughnutLayoutImageMap: Map; - doughnutLegendPositions: DoughnutLegendPosition[]; + doughnutLegendPositions = doughnutLegendPositions; doughnutLegendPositionTranslationMap = doughnutLegendPositionTranslations; @@ -116,8 +116,6 @@ export class DoughnutBasicConfigComponent extends BasicWidgetConfigComponent { const params = widgetConfig.typeParameters as any; this.horizontal = isDefinedAndNotNull(params.horizontal) ? params.horizontal : false; this.doughnutLayoutImageMap = this.horizontal ? horizontalDoughnutLayoutImages : doughnutLayoutImages; - this.doughnutLegendPositions = this.horizontal ? [DoughnutLegendPosition.left, DoughnutLegendPosition.right] : - [DoughnutLegendPosition.top, DoughnutLegendPosition.bottom]; super.setupConfig(widgetConfig); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/doughnut-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/doughnut-widget.component.ts index 281a429adc..1595c59110 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/doughnut-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/doughnut-widget.component.ts @@ -264,7 +264,7 @@ export class DoughnutWidgetComponent implements OnInit, OnDestroy, AfterViewInit if (tsValue && isDefinedAndNotNull(tsValue[1]) && isNumeric(tsValue[1])) { value = tsValue[1]; dataItem.hasValue = true; - dataItem.value = value; + dataItem.value = Number(value); } else { dataItem.hasValue = false; dataItem.value = 0; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/doughnut-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/doughnut-widget.models.ts index 4fb192c4bd..1771dbbb8a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/doughnut-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/doughnut-widget.models.ts @@ -57,6 +57,8 @@ export enum DoughnutLegendPosition { right = 'right' } +export const doughnutLegendPositions = Object.keys(DoughnutLegendPosition) as DoughnutLegendPosition[]; + export const doughnutLegendPositionTranslations = new Map( [ [DoughnutLegendPosition.top, 'widgets.doughnut.legend-position-top'], diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/doughnut-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/doughnut-widget-settings.component.ts index a018557aba..220fc7c320 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/doughnut-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/doughnut-widget-settings.component.ts @@ -14,20 +14,19 @@ /// limitations under the License. /// -import { Component, Injector } from '@angular/core'; +import { Component } from '@angular/core'; import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { formatValue, isDefinedAndNotNull } from '@core/utils'; -import { getDataKey } from '@shared/models/widget-settings.models'; import { doughnutDefaultSettings, DoughnutLayout, doughnutLayoutImages, doughnutLayouts, doughnutLayoutTranslations, - DoughnutLegendPosition, + doughnutLegendPositions, doughnutLegendPositionTranslations, DoughnutTooltipValueType, doughnutTooltipValueTypes, @@ -56,7 +55,7 @@ export class DoughnutWidgetSettingsComponent extends WidgetSettingsComponent { doughnutLayoutImageMap: Map; - doughnutLegendPositions: DoughnutLegendPosition[]; + doughnutLegendPositions = doughnutLegendPositions; doughnutLegendPositionTranslationMap = doughnutLegendPositionTranslations; @@ -71,7 +70,6 @@ export class DoughnutWidgetSettingsComponent extends WidgetSettingsComponent { tooltipValuePreviewFn = this._tooltipValuePreviewFn.bind(this); constructor(protected store: Store, - private $injector: Injector, private fb: UntypedFormBuilder) { super(store); } @@ -84,8 +82,6 @@ export class DoughnutWidgetSettingsComponent extends WidgetSettingsComponent { const params = widgetConfig.typeParameters as any; this.horizontal = isDefinedAndNotNull(params.horizontal) ? params.horizontal : false; this.doughnutLayoutImageMap = this.horizontal ? horizontalDoughnutLayoutImages : doughnutLayoutImages; - this.doughnutLegendPositions = this.horizontal ? [DoughnutLegendPosition.left, DoughnutLegendPosition.right] : - [DoughnutLegendPosition.top, DoughnutLegendPosition.bottom]; } protected defaultSettings(): WidgetSettings { From e8bcf292eecf3fe99082bdb992fb249c29ae084e Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Thu, 9 Nov 2023 17:01:14 +0200 Subject: [PATCH 078/252] UI: Fixed updated show value and toltip and liquid level widget --- .../widget/lib/indicator/liquid-level-widget.component.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/indicator/liquid-level-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/liquid-level-widget.component.ts index 9bb7053c3e..baf9f018c0 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/indicator/liquid-level-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/liquid-level-widget.component.ts @@ -541,13 +541,13 @@ export class LiquidLevelWidgetComponent implements OnInit {
`; } - private convertInputData(value: number): number { + private convertInputData(value: any): number { if (this.settings.datasourceUnits !== CapacityUnits.percent) { - return (convertLiters(value, this.settings.datasourceUnits, ConversionType.to) / + return (convertLiters(Number(value), this.settings.datasourceUnits, ConversionType.to) / convertLiters(this.volume, this.settings.volumeUnits, ConversionType.to)) * 100; } - return value; + return Number(value); } private convertOutputData(value: number): number { From 8b034308de320d4ddb1f96544cc17508a0e6af0a Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Thu, 9 Nov 2023 17:13:16 +0200 Subject: [PATCH 079/252] updated docker connectivity command localhost check, updated test --- .../DeviceConnectivityControllerTest.java | 181 +++++++++++++++++- ...ectivityControllerWithDefaultPortTest.java | 159 --------------- .../dao/util/DeviceConnectivityUtil.java | 8 +- 3 files changed, 180 insertions(+), 168 deletions(-) delete mode 100644 application/src/test/java/org/thingsboard/server/controller/DeviceConnectivityControllerWithDefaultPortTest.java 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 015334296b..a4897f6bd9 100644 --- a/application/src/test/java/org/thingsboard/server/controller/DeviceConnectivityControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/DeviceConnectivityControllerTest.java @@ -24,7 +24,6 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import org.junit.jupiter.api.BeforeAll; import org.mockito.AdditionalAnswers; import org.mockito.Mockito; import org.springframework.context.annotation.Bean; @@ -100,8 +99,6 @@ public class DeviceConnectivityControllerTest extends AbstractControllerTest { "C3swkUdrR3pezg==\n" + "-----END PRIVATE KEY-----\n"; - ListeningExecutorService executor; - private Tenant savedTenant; private User tenantAdmin; private DeviceProfileId mqttDeviceProfileId; @@ -117,8 +114,6 @@ public class DeviceConnectivityControllerTest extends AbstractControllerTest { @Before public void beforeTest() throws Exception { - executor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(8, getClass())); - loginSysAdmin(); ObjectNode config = JacksonUtil.newObjectNode(); @@ -208,8 +203,6 @@ public class DeviceConnectivityControllerTest extends AbstractControllerTest { @After public void afterTest() throws Exception { - executor.shutdownNow(); - loginSysAdmin(); doDelete("/api/tenant/" + savedTenant.getId().getId()) @@ -261,6 +254,12 @@ public class DeviceConnectivityControllerTest extends AbstractControllerTest { "-t json -e \"{temperature:25}\"", credentials.getCredentialsId())); assertThat(linuxCoapCommands.get(COAPS).asText()).isEqualTo(String.format("coap-client-openssl -m POST coaps://localhost:5684/api/v1/%s/telemetry" + " -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + + JsonNode dockerCoapCommands = commands.get(COAP).get(DOCKER); + assertThat(dockerCoapCommands.get(COAP).asText()).isEqualTo(String.format("docker run --rm -it --network=host" + + " thingsboard/coap-clients coap-client -m POST coap://localhost:5683/api/v1/%s/telemetry -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + assertThat(dockerCoapCommands.get(COAPS).asText()).isEqualTo(String.format("docker run --rm -it --network=host" + + " thingsboard/coap-clients coap-client-openssl -m POST coaps://localhost:5684/api/v1/%s/telemetry -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); } @Test @@ -445,4 +444,172 @@ public class DeviceConnectivityControllerTest extends AbstractControllerTest { public void testDownloadCertWithUnknownProtocol() throws Exception { doGet("/api/device-connectivity/unknownProtocol/certificate/download").andExpect(status().isNotFound()); } + + @Test + public void testFetchPublishTelemetryCommandsForDefaultDeviceIfPortsSetToDefault() throws Exception { + loginSysAdmin(); + + ObjectNode config = JacksonUtil.newObjectNode(); + + ObjectNode http = JacksonUtil.newObjectNode(); + http.put("enabled", true); + http.put("host", ""); + http.put("port", 80); + config.set("http", http); + + ObjectNode https = JacksonUtil.newObjectNode(); + https.put("enabled", true); + https.put("host", ""); + https.put("port", 443); + config.set("https", https); + + ObjectNode mqtt = JacksonUtil.newObjectNode(); + mqtt.put("enabled", false); + mqtt.put("host", ""); + mqtt.put("port", 1883); + config.set("mqtt", mqtt); + + ObjectNode mqtts = JacksonUtil.newObjectNode(); + mqtts.put("enabled", false); + mqtts.put("host", ""); + mqtts.put("port", 8883); + config.set("mqtts", mqtts); + + ObjectNode coap = JacksonUtil.newObjectNode(); + coap.put("enabled", false); + coap.put("host", ""); + coap.put("port", 5683); + config.set("coap", coap); + + ObjectNode coaps = JacksonUtil.newObjectNode(); + coaps.put("enabled", false); + coaps.put("host", ""); + coaps.put("port", 5684); + config.set("coaps", coaps); + + AdminSettings adminSettings = doGet("/api/admin/settings/connectivity", AdminSettings.class); + adminSettings.setJsonValue(config); + doPost("/api/admin/settings", adminSettings).andExpect(status().isOk()); + + login("tenant2@thingsboard.org", "testPassword1"); + + Device device = new Device(); + device.setName("My device"); + device.setType("default"); + Device savedDevice = doPost("/api/device", device, Device.class); + JsonNode commands = + doGetTyped("/api/device-connectivity/" + savedDevice.getId().getId(), new TypeReference<>() { + }); + + DeviceCredentials credentials = + doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class); + + assertThat(commands).hasSize(1); + JsonNode httpCommands = commands.get(HTTP); + assertThat(httpCommands.get(HTTP).asText()).isEqualTo(String.format("curl -v -X POST http://localhost/api/v1/%s/telemetry " + + "--header Content-Type:application/json --data \"{temperature:25}\"", + credentials.getCredentialsId())); + assertThat(httpCommands.get(HTTPS).asText()).isEqualTo(String.format("curl -v -X POST https://localhost/api/v1/%s/telemetry " + + "--header Content-Type:application/json --data \"{temperature:25}\"", + credentials.getCredentialsId())); + } + + @Test + public void testFetchPublishTelemetryCommandsForDefaultDeviceIfHostIsNotLocalhost() throws Exception { + loginSysAdmin(); + + ObjectNode config = JacksonUtil.newObjectNode(); + + ObjectNode http = JacksonUtil.newObjectNode(); + http.put("enabled", true); + http.put("host", "test.domain"); + http.put("port", 8080); + config.set("http", http); + + ObjectNode https = JacksonUtil.newObjectNode(); + https.put("enabled", true); + https.put("host", "test.domain"); + https.put("port", 443); + config.set("https", https); + + ObjectNode mqtt = JacksonUtil.newObjectNode(); + mqtt.put("enabled", true); + mqtt.put("host", "test.domain"); + mqtt.put("port", 1883); + config.set("mqtt", mqtt); + + ObjectNode mqtts = JacksonUtil.newObjectNode(); + mqtts.put("enabled", true); + mqtts.put("host", "test.domain"); + mqtts.put("port", 8883); + config.set("mqtts", mqtts); + + ObjectNode coap = JacksonUtil.newObjectNode(); + coap.put("enabled", true); + coap.put("host", "test.domain"); + coap.put("port", 5683); + config.set("coap", coap); + + ObjectNode coaps = JacksonUtil.newObjectNode(); + coaps.put("enabled", true); + coaps.put("host", "test.domain"); + coaps.put("port", 5684); + config.set("coaps", coaps); + + AdminSettings adminSettings = doGet("/api/admin/settings/connectivity", AdminSettings.class); + adminSettings.setJsonValue(config); + doPost("/api/admin/settings", adminSettings).andExpect(status().isOk()); + + login("tenant2@thingsboard.org", "testPassword1"); + + Device device = new Device(); + device.setName("My device"); + device.setType("default"); + Device savedDevice = doPost("/api/device", device, Device.class); + JsonNode commands = + doGetTyped("/api/device-connectivity/" + savedDevice.getId().getId(), new TypeReference<>() { + }); + + DeviceCredentials credentials = + doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class); + + assertThat(commands).hasSize(3); + JsonNode httpCommands = commands.get(HTTP); + assertThat(httpCommands.get(HTTP).asText()).isEqualTo(String.format("curl -v -X POST http://test.domain:8080/api/v1/%s/telemetry " + + "--header Content-Type:application/json --data \"{temperature:25}\"", + credentials.getCredentialsId())); + assertThat(httpCommands.get(HTTPS).asText()).isEqualTo(String.format("curl -v -X POST https://test.domain/api/v1/%s/telemetry " + + "--header Content-Type:application/json --data \"{temperature:25}\"", + credentials.getCredentialsId())); + + + JsonNode mqttCommands = commands.get(MQTT); + assertThat(mqttCommands.get(MQTT).asText()).isEqualTo(String.format("mosquitto_pub -d -q 1 -h test.domain -p 1883 -t v1/devices/me/telemetry " + + "-u \"%s\" -m \"{temperature:25}\"", + credentials.getCredentialsId())); + assertThat(mqttCommands.get(MQTTS).get(0).asText()).isEqualTo("curl -f -S -o ca-root.pem http://localhost:80/api/device-connectivity/mqtts/certificate/download"); + assertThat(mqttCommands.get(MQTTS).get(1).asText()).isEqualTo(String.format("mosquitto_pub -d -q 1 --cafile ca-root.pem -h test.domain -p 8883 " + + "-t v1/devices/me/telemetry -u \"%s\" -m \"{temperature:25}\"", credentials.getCredentialsId())); + + JsonNode dockerMqttCommands = commands.get(MQTT).get(DOCKER); + assertThat(dockerMqttCommands.get(MQTT).asText()).isEqualTo(String.format("docker run --rm -it thingsboard/mosquitto-clients mosquitto_pub -d -q 1 -h test.domain" + + " -p 1883 -t v1/devices/me/telemetry -u \"%s\" -m \"{temperature:25}\"", + credentials.getCredentialsId())); + assertThat(dockerMqttCommands.get(MQTTS).asText()).isEqualTo(String.format("docker run --rm -it thingsboard/mosquitto-clients " + + "/bin/sh -c \"curl -f -S -o ca-root.pem http://localhost:80/api/device-connectivity/mqtts/certificate/download && " + + "mosquitto_pub -d -q 1 --cafile ca-root.pem -h test.domain -p 8883 -t v1/devices/me/telemetry -u \"%s\" -m \"{temperature:25}\"\"", + credentials.getCredentialsId())); + + JsonNode linuxCoapCommands = commands.get(COAP); + assertThat(linuxCoapCommands.get(COAP).asText()).isEqualTo(String.format("coap-client -m POST coap://test.domain:5683/api/v1/%s/telemetry " + + "-t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + assertThat(linuxCoapCommands.get(COAPS).asText()).isEqualTo(String.format("coap-client-openssl -m POST coaps://test.domain:5684/api/v1/%s/telemetry" + + " -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + + JsonNode dockerCoapCommands = commands.get(COAP).get(DOCKER); + assertThat(dockerCoapCommands.get(COAP).asText()).isEqualTo(String.format("docker run --rm -it " + + "thingsboard/coap-clients coap-client -m POST coap://test.domain:5683/api/v1/%s/telemetry -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + assertThat(dockerCoapCommands.get(COAPS).asText()).isEqualTo(String.format("docker run --rm -it " + + "thingsboard/coap-clients coap-client-openssl -m POST coaps://test.domain:5684/api/v1/%s/telemetry -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + } } diff --git a/application/src/test/java/org/thingsboard/server/controller/DeviceConnectivityControllerWithDefaultPortTest.java b/application/src/test/java/org/thingsboard/server/controller/DeviceConnectivityControllerWithDefaultPortTest.java deleted file mode 100644 index f2f52443c3..0000000000 --- a/application/src/test/java/org/thingsboard/server/controller/DeviceConnectivityControllerWithDefaultPortTest.java +++ /dev/null @@ -1,159 +0,0 @@ -/** - * Copyright © 2016-2023 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. - */ -package org.thingsboard.server.controller; - -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.google.common.util.concurrent.ListeningExecutorService; -import com.google.common.util.concurrent.MoreExecutors; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.AdditionalAnswers; -import org.mockito.Mockito; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Primary; -import org.springframework.test.context.ContextConfiguration; -import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.common.util.ThingsBoardExecutors; -import org.thingsboard.server.common.data.AdminSettings; -import org.thingsboard.server.common.data.Device; -import org.thingsboard.server.common.data.Tenant; -import org.thingsboard.server.common.data.User; -import org.thingsboard.server.common.data.security.Authority; -import org.thingsboard.server.common.data.security.DeviceCredentials; -import org.thingsboard.server.dao.device.DeviceDao; -import org.thingsboard.server.dao.service.DaoSqlTest; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.HTTP; -import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.HTTPS; - -@ContextConfiguration(classes = {DeviceConnectivityControllerWithDefaultPortTest.Config.class}) -@DaoSqlTest -public class DeviceConnectivityControllerWithDefaultPortTest extends AbstractControllerTest { - - ListeningExecutorService executor; - - private Tenant savedTenant; - - static class Config { - @Bean - @Primary - public DeviceDao deviceDao(DeviceDao deviceDao) { - return Mockito.mock(DeviceDao.class, AdditionalAnswers.delegatesTo(deviceDao)); - } - } - - @Before - public void beforeTest() throws Exception { - executor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(8, getClass())); - - loginSysAdmin(); - - ObjectNode config = JacksonUtil.newObjectNode(); - - ObjectNode http = JacksonUtil.newObjectNode(); - http.put("enabled", true); - http.put("host", ""); - http.put("port", 80); - config.set("http", http); - - ObjectNode https = JacksonUtil.newObjectNode(); - https.put("enabled", true); - https.put("host", ""); - https.put("port", 443); - config.set("https", https); - - ObjectNode mqtt = JacksonUtil.newObjectNode(); - mqtt.put("enabled", false); - mqtt.put("host", ""); - mqtt.put("port", 1883); - config.set("mqtt", mqtt); - - ObjectNode mqtts = JacksonUtil.newObjectNode(); - mqtts.put("enabled", false); - mqtts.put("host", ""); - mqtts.put("port", 8883); - config.set("mqtts", mqtts); - - ObjectNode coap = JacksonUtil.newObjectNode(); - coap.put("enabled", false); - coap.put("host", ""); - coap.put("port", 5683); - config.set("coap", coap); - - ObjectNode coaps = JacksonUtil.newObjectNode(); - coaps.put("enabled", false); - coaps.put("host", ""); - coaps.put("port", 5684); - config.set("coaps", coaps); - - AdminSettings adminSettings = doGet("/api/admin/settings/connectivity", AdminSettings.class); - adminSettings.setJsonValue(config); - doPost("/api/admin/settings", adminSettings).andExpect(status().isOk()); - - Tenant tenant = new Tenant(); - tenant.setTitle("My tenant"); - savedTenant = doPost("/api/tenant", tenant, Tenant.class); - Assert.assertNotNull(savedTenant); - - User tenantAdmin = new User(); - tenantAdmin.setAuthority(Authority.TENANT_ADMIN); - tenantAdmin.setTenantId(savedTenant.getId()); - tenantAdmin.setEmail("tenant2@thingsboard.org"); - tenantAdmin.setFirstName("Joe"); - tenantAdmin.setLastName("Downs"); - - createUserAndLogin(tenantAdmin, "testPassword1"); - } - - @After - public void afterTest() throws Exception { - executor.shutdownNow(); - - loginSysAdmin(); - - doDelete("/api/tenant/" + savedTenant.getId().getId()) - .andExpect(status().isOk()); - } - - @Test - public void testFetchPublishTelemetryCommandsForDefaultDevice() throws Exception { - Device device = new Device(); - device.setName("My device"); - device.setType("default"); - Device savedDevice = doPost("/api/device", device, Device.class); - JsonNode commands = - doGetTyped("/api/device-connectivity/" + savedDevice.getId().getId(), new TypeReference<>() { - }); - - DeviceCredentials credentials = - doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class); - - assertThat(commands).hasSize(1); - JsonNode httpCommands = commands.get(HTTP); - assertThat(httpCommands.get(HTTP).asText()).isEqualTo(String.format("curl -v -X POST http://localhost/api/v1/%s/telemetry " + - "--header Content-Type:application/json --data \"{temperature:25}\"", - credentials.getCredentialsId())); - assertThat(httpCommands.get(HTTPS).asText()).isEqualTo(String.format("curl -v -X POST https://localhost/api/v1/%s/telemetry " + - "--header Content-Type:application/json --data \"{temperature:25}\"", - credentials.getCredentialsId())); - } -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java b/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java index 8cb0897a98..bde018122c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java +++ b/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java @@ -20,6 +20,9 @@ import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials; import org.thingsboard.server.common.data.security.DeviceCredentials; +import java.util.Arrays; +import java.util.List; + public class DeviceConnectivityUtil { public static final String HTTP = "http"; @@ -38,6 +41,7 @@ public class DeviceConnectivityUtil { public static final String GATEWAY_DOCKER_RUN = "docker run -it "; public static final String MQTT_IMAGE = "thingsboard/mosquitto-clients "; public static final String COAP_IMAGE = "thingsboard/coap-clients "; + public static final List LOCAL_HOSTS = Arrays.asList("localhost", "127.0.0.1"); public static String getHttpPublishCommand(String protocol, String host, String port, DeviceCredentials deviceCredentials) { return String.format("curl -v -X POST %s://%s%s/api/v1/%s/telemetry --header Content-Type:application/json --data " + JSON_EXAMPLE_PAYLOAD, @@ -135,7 +139,7 @@ public class DeviceConnectivityUtil { } StringBuilder mqttDockerCommand = new StringBuilder(); - mqttDockerCommand.append(DOCKER_RUN).append("localhost".equals(host) ? "--network=host ":"").append(MQTT_IMAGE); + mqttDockerCommand.append(DOCKER_RUN).append(LOCAL_HOSTS.contains(host) ? "--network=host ":"").append(MQTT_IMAGE); if (MQTTS.equals(protocol)) { mqttDockerCommand.append("/bin/sh -c \"") @@ -167,6 +171,6 @@ public class DeviceConnectivityUtil { public static String getDockerCoapPublishCommand(String protocol, String host, String port, DeviceCredentials deviceCredentials) { String coapCommand = getCoapPublishCommand(protocol, host, port, deviceCredentials); - return coapCommand != null ? String.format("%s%s%s", DOCKER_RUN + ("localhost".equals(host) ? "--network=host ":""), COAP_IMAGE, coapCommand) : null; + return coapCommand != null ? String.format("%s%s%s", DOCKER_RUN + (LOCAL_HOSTS.contains(host) ? "--network=host ":""), COAP_IMAGE, coapCommand) : null; } } From 6358b0be90c1612c616c903ed1589b9f03b3678e Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Thu, 9 Nov 2023 18:16:40 +0200 Subject: [PATCH 080/252] Admin settings update. @author AndriiLandiak --- .../service/edge/EdgeContextComponent.java | 4 - .../service/edge/rpc/EdgeSyncCursor.java | 2 +- .../fetch/AdminSettingsEdgeEventFetcher.java | 125 ++---------------- .../relation/RelationEdgeProcessor.java | 2 + .../resource/BaseResourceProcessor.java | 22 ++- .../resource/ResourceEdgeProcessor.java | 12 +- .../edge/rpc/utils/EdgeVersionUtils.java | 36 +++++ .../queue/DefaultTbClusterService.java | 8 +- .../server/service/queue/ProtoUtils.java | 21 ++- .../server/controller/EdgeControllerTest.java | 21 ++- .../server/edge/AbstractEdgeTest.java | 38 ++++-- .../server/edge/AssetEdgeTest.java | 4 +- .../server/edge/DeviceEdgeTest.java | 4 +- .../server/edge/ResourceEdgeTest.java | 51 +++++++ common/cluster-api/src/main/proto/queue.proto | 8 +- .../thingsboard/edge/rpc/EdgeGrpcClient.java | 2 +- common/edge-api/src/main/proto/edge.proto | 5 +- 17 files changed, 199 insertions(+), 166 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/utils/EdgeVersionUtils.java diff --git a/application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java b/application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java index 7850032142..0d2b6675c9 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.service.edge; -import freemarker.template.Configuration; import lombok.Data; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; @@ -87,9 +86,6 @@ public class EdgeContextComponent { @Autowired private AdminSettingsService adminSettingsService; - @Autowired - private Configuration freemarkerConfig; - @Autowired private DeviceService deviceService; diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeSyncCursor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeSyncCursor.java index 5074d580f0..98c93e54ec 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeSyncCursor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeSyncCursor.java @@ -57,7 +57,7 @@ public class EdgeSyncCursor { fetchers.add(new TenantEdgeEventFetcher(ctx.getTenantService())); fetchers.add(new QueuesEdgeEventFetcher(ctx.getQueueService())); fetchers.add(new RuleChainsEdgeEventFetcher(ctx.getRuleChainService())); - fetchers.add(new AdminSettingsEdgeEventFetcher(ctx.getAdminSettingsService(), ctx.getFreemarkerConfig())); + fetchers.add(new AdminSettingsEdgeEventFetcher(ctx.getAdminSettingsService())); fetchers.add(new TenantAdminUsersEdgeEventFetcher(ctx.getUserService())); Customer publicCustomer = ctx.getCustomerService().findOrCreatePublicCustomer(edge.getTenantId()); fetchers.add(new CustomerEdgeEventFetcher(publicCustomer.getId())); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/AdminSettingsEdgeEventFetcher.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/AdminSettingsEdgeEventFetcher.java index e3a8c14012..e522529b8e 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/AdminSettingsEdgeEventFetcher.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/AdminSettingsEdgeEventFetcher.java @@ -1,12 +1,12 @@ /** * Copyright © 2016-2023 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 - * + *

+ * 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. @@ -15,146 +15,49 @@ */ package org.thingsboard.server.service.edge.rpc.fetch; -import com.datastax.oss.driver.api.core.uuid.Uuids; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import freemarker.template.Configuration; -import freemarker.template.Template; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.text.WordUtils; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.EdgeUtils; -import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.edge.EdgeEventType; -import org.thingsboard.server.common.data.id.AdminSettingsId; +import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.settings.AdminSettingsService; import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; @AllArgsConstructor @Slf4j public class AdminSettingsEdgeEventFetcher implements EdgeEventFetcher { private final AdminSettingsService adminSettingsService; - private final Configuration freemarkerConfig; - - private static final Pattern startPattern = Pattern.compile("

"); - private static final Pattern endPattern = Pattern.compile("
"); - - private static final List templatesNames = Arrays.asList( - "account.activated.ftl", - "account.lockout.ftl", - "activation.ftl", - "password.was.reset.ftl", - "reset.password.ftl", - "test.ftl"); - - // TODO: @voba fix format of next templates - // "state.disabled.ftl", - // "state.enabled.ftl", - // "state.warning.ftl", @Override public PageLink getPageLink(int pageSize) { return null; } - @Override - public PageData fetchEdgeEvents(TenantId tenantId, Edge edge, PageLink pageLink) throws Exception { - List result = new ArrayList<>(); - - AdminSettings systemMailSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "mail"); - result.add(EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS, - EdgeEventActionType.UPDATED, null, JacksonUtil.valueToTree(systemMailSettings))); - - AdminSettings tenantMailSettings = convertToTenantAdminSettings(tenantId, systemMailSettings.getKey(), (ObjectNode) systemMailSettings.getJsonValue()); - result.add(EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS, - EdgeEventActionType.UPDATED, null, JacksonUtil.valueToTree(tenantMailSettings))); - - AdminSettings systemMailTemplates = loadMailTemplates(tenantId); - result.add(EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS, - EdgeEventActionType.UPDATED, null, JacksonUtil.valueToTree(systemMailTemplates))); - - AdminSettings tenantMailTemplates = convertToTenantAdminSettings(tenantId, systemMailTemplates.getKey(), (ObjectNode) systemMailTemplates.getJsonValue()); - result.add(EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS, - EdgeEventActionType.UPDATED, null, JacksonUtil.valueToTree(tenantMailTemplates))); + public PageData fetchEdgeEvents(TenantId tenantId, Edge edge, PageLink pageLink) { + List result = fetchAdminSettingsForKeys(tenantId, edge.getId(), List.of("general", "mail", "connectivity", "jwt")); // return PageData object to be in sync with other fetchers return new PageData<>(result, 1, result.size(), false); } - private AdminSettings loadMailTemplates(TenantId tenantId) throws Exception { - Map mailTemplates = new HashMap<>(); - for (String templatesName : templatesNames) { - Template template = freemarkerConfig.getTemplate(templatesName); - if (template != null) { - String name = validateName(template.getName()); - Map mailTemplate = getMailTemplateFromFile(template.toString()); - if (mailTemplate != null) { - mailTemplates.put(name, mailTemplate); - } else { - log.error("[{}] Can't load mail template from file {}", tenantId, template.getName()); - } - } - } - AdminSettings adminSettings = new AdminSettings(); - adminSettings.setId(new AdminSettingsId(Uuids.timeBased())); - adminSettings.setKey("mailTemplates"); - adminSettings.setJsonValue(JacksonUtil.convertValue(mailTemplates, JsonNode.class)); - return adminSettings; - } - - private Map getMailTemplateFromFile(String stringTemplate) { - Map mailTemplate = new HashMap<>(); - Matcher start = startPattern.matcher(stringTemplate); - Matcher end = endPattern.matcher(stringTemplate); - if (start.find() && end.find()) { - String body = StringUtils.substringBetween(stringTemplate, start.group(), end.group()).replaceAll("\t", ""); - String subject = StringUtils.substringBetween(body, "

", "

"); - mailTemplate.put("subject", subject); - mailTemplate.put("body", body); - } else { - return null; - } - return mailTemplate; - } - - private String validateName(String name) throws Exception { - StringBuilder nameBuilder = new StringBuilder(); - name = name.replace(".ftl", ""); - String[] nameParts = name.split("\\."); - if (nameParts.length >= 1) { - nameBuilder.append(nameParts[0]); - for (int i = 1; i < nameParts.length; i++) { - String word = WordUtils.capitalize(nameParts[i]); - nameBuilder.append(word); - } - return nameBuilder.toString(); - } else { - throw new Exception("Error during filename validation"); + private List fetchAdminSettingsForKeys(TenantId tenantId, EdgeId edgeId, List keys) { + List result = new ArrayList<>(); + for (String key : keys) { + AdminSettings adminSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, key); + result.add(EdgeUtils.constructEdgeEvent(tenantId, edgeId, EdgeEventType.ADMIN_SETTINGS, + EdgeEventActionType.UPDATED, null, JacksonUtil.valueToTree(adminSettings))); } - } - - private AdminSettings convertToTenantAdminSettings(TenantId tenantId, String key, ObjectNode jsonValue) { - AdminSettings tenantMailSettings = new AdminSettings(); - tenantMailSettings.setTenantId(tenantId); - jsonValue.put("useSystemMailSettings", true); - tenantMailSettings.setJsonValue(jsonValue); - tenantMailSettings.setKey(key); - return tenantMailSettings; + return result; } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessor.java index fe76fed545..487267e60f 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessor.java @@ -71,10 +71,12 @@ public class RelationEdgeProcessor extends BaseRelationProcessor { if (relation == null || (relation.getFrom().getEntityType().equals(EntityType.EDGE) || relation.getTo().getEntityType().equals(EntityType.EDGE))) { return Futures.immediateFuture(null); } + EdgeId sourceEdgeId = safeGetEdgeId(edgeNotificationMsg.getOriginatorEdgeIdMSB(), edgeNotificationMsg.getOriginatorEdgeIdLSB()); Set uniqueEdgeIds = new HashSet<>(); uniqueEdgeIds.addAll(edgeService.findAllRelatedEdgeIds(tenantId, relation.getTo())); uniqueEdgeIds.addAll(edgeService.findAllRelatedEdgeIds(tenantId, relation.getFrom())); + uniqueEdgeIds.remove(sourceEdgeId); if (uniqueEdgeIds.isEmpty()) { return Futures.immediateFuture(null); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/BaseResourceProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/BaseResourceProcessor.java index 13ca012f3c..a00745d8b7 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/BaseResourceProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/BaseResourceProcessor.java @@ -18,17 +18,20 @@ package org.thingsboard.server.service.edge.rpc.processor.resource; import com.datastax.oss.driver.api.core.uuid.Uuids; import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.ResourceType; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.TbResourceInfo; import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageDataIterable; import org.thingsboard.server.gen.edge.v1.ResourceUpdateMsg; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; @Slf4j public abstract class BaseResourceProcessor extends BaseEdgeProcessor { - protected void saveOrUpdateTbResource(TenantId tenantId, TbResourceId tbResourceId, ResourceUpdateMsg resourceUpdateMsg) { + protected boolean saveOrUpdateTbResource(TenantId tenantId, TbResourceId tbResourceId, ResourceUpdateMsg resourceUpdateMsg) { + boolean resourceKeyUpdated = false; try { boolean created = false; TbResource resource = resourceService.findResourceById(tenantId, tbResourceId); @@ -42,9 +45,21 @@ public abstract class BaseResourceProcessor extends BaseEdgeProcessor { resource.setCreatedTime(Uuids.unixTimestamp(tbResourceId.getId())); created = true; } + String resourceKey = resourceUpdateMsg.getResourceKey(); + ResourceType resourceType = ResourceType.valueOf(resourceUpdateMsg.getResourceType()); + PageDataIterable resourcesIterable = new PageDataIterable<>( + link -> resourceService.findTenantResourcesByResourceTypeAndPageLink(tenantId, resourceType, link), 1024); + for (TbResource tbResource : resourcesIterable) { + if (tbResource.getResourceKey().equals(resourceUpdateMsg.getResourceKey()) && !tbResourceId.equals(tbResource.getId())) { + resourceKey = StringUtils.randomAlphabetic(15) + "_" + resourceKey; + log.warn("[{}] Resource with resource type {} and key {} already exists. Renaming resource key to {}", + tenantId, resourceType, resourceUpdateMsg.getResourceKey(), resourceKey); + resourceKeyUpdated = true; + } + } resource.setTitle(resourceUpdateMsg.getTitle()); - resource.setResourceKey(resourceUpdateMsg.getResourceKey()); - resource.setResourceType(ResourceType.valueOf(resourceUpdateMsg.getResourceType())); + resource.setResourceKey(resourceKey); + resource.setResourceType(resourceType); resource.setFileName(resourceUpdateMsg.getFileName()); resource.setData(resourceUpdateMsg.hasData() ? resourceUpdateMsg.getData() : null); resource.setEtag(resourceUpdateMsg.hasEtag() ? resourceUpdateMsg.getEtag() : null); @@ -57,5 +72,6 @@ public abstract class BaseResourceProcessor extends BaseEdgeProcessor { log.error("[{}] Failed to process resource update msg [{}]", tenantId, resourceUpdateMsg, e); throw e; } + return resourceKeyUpdated; } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessor.java index e7c5b21dbd..ad283826f8 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessor.java @@ -23,6 +23,8 @@ import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.edge.EdgeEventActionType; +import org.thingsboard.server.common.data.edge.EdgeEventType; import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.exception.DataValidationException; @@ -46,14 +48,12 @@ public class ResourceEdgeProcessor extends BaseResourceProcessor { switch (resourceUpdateMsg.getMsgType()) { case ENTITY_CREATED_RPC_MESSAGE: case ENTITY_UPDATED_RPC_MESSAGE: - super.saveOrUpdateTbResource(tenantId, tbResourceId, resourceUpdateMsg); - break; - case ENTITY_DELETED_RPC_MESSAGE: - TbResource tbResourceToDelete = resourceService.findResourceById(tenantId, tbResourceId); - if (tbResourceToDelete != null) { - resourceService.deleteResource(tenantId, tbResourceId); + boolean resourceKeyUpdated = super.saveOrUpdateTbResource(tenantId, tbResourceId, resourceUpdateMsg); + if (resourceKeyUpdated) { + saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.TB_RESOURCE, EdgeEventActionType.UPDATED, tbResourceId, null); } break; + case ENTITY_DELETED_RPC_MESSAGE: case UNRECOGNIZED: return handleUnsupportedMsgType(resourceUpdateMsg.getMsgType()); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/utils/EdgeVersionUtils.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/utils/EdgeVersionUtils.java new file mode 100644 index 0000000000..75cd430a26 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/utils/EdgeVersionUtils.java @@ -0,0 +1,36 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.utils; + +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; + +@Slf4j +public final class EdgeVersionUtils { + + public static boolean isEdgeProtoDeprecated(EdgeVersion edgeVersion) { + switch (edgeVersion) { + case V_3_3_0: + case V_3_3_3: + case V_3_4_0: + case V_3_6_0: + return true; + case V_3_6_1: + default: + return false; + } + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java index 103d2b68e2..f96e49930c 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java @@ -486,7 +486,7 @@ public class DefaultTbClusterService implements TbClusterService { } @Override - public void sendNotificationMsgToEdge(TenantId tenantId, EdgeId edgeId, EntityId entityId, String body, EdgeEventType type, EdgeEventActionType action, EdgeId sourceEdgeId) { + public void sendNotificationMsgToEdge(TenantId tenantId, EdgeId edgeId, EntityId entityId, String body, EdgeEventType type, EdgeEventActionType action, EdgeId originatorEdgeId) { if (!edgesEnabled) { return; } @@ -519,9 +519,9 @@ public class DefaultTbClusterService implements TbClusterService { if (body != null) { builder.setBody(body); } - if (sourceEdgeId != null) { - builder.setSourceEdgeIdMSB(sourceEdgeId.getId().getMostSignificantBits()); - builder.setSourceEdgeIdLSB(sourceEdgeId.getId().getLeastSignificantBits()); + if (originatorEdgeId != null) { + builder.setOriginatorEdgeIdMSB(originatorEdgeId.getId().getMostSignificantBits()); + builder.setOriginatorEdgeIdLSB(originatorEdgeId.getId().getLeastSignificantBits()); } TransportProtos.EdgeNotificationMsgProto msg = builder.build(); log.trace("[{}] sending notification to edge service {}", tenantId.getId(), msg); diff --git a/application/src/main/java/org/thingsboard/server/service/queue/ProtoUtils.java b/application/src/main/java/org/thingsboard/server/service/queue/ProtoUtils.java index 76a8e3e89a..36038568bc 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/ProtoUtils.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/ProtoUtils.java @@ -152,22 +152,29 @@ public class ProtoUtils { } private static TransportProtos.DeviceEdgeUpdateMsgProto toProto(DeviceEdgeUpdateMsg msg) { - return TransportProtos.DeviceEdgeUpdateMsgProto.newBuilder() + TransportProtos.DeviceEdgeUpdateMsgProto.Builder builder = TransportProtos.DeviceEdgeUpdateMsgProto.newBuilder() .setTenantIdMSB(msg.getTenantId().getId().getMostSignificantBits()) .setTenantIdLSB(msg.getTenantId().getId().getLeastSignificantBits()) .setDeviceIdMSB(msg.getDeviceId().getId().getMostSignificantBits()) - .setDeviceIdLSB(msg.getDeviceId().getId().getLeastSignificantBits()) - .setEdgeIdMSB(msg.getEdgeId().getId().getMostSignificantBits()) - .setEdgeIdLSB(msg.getEdgeId().getId().getLeastSignificantBits()) - .build(); + .setDeviceIdLSB(msg.getDeviceId().getId().getLeastSignificantBits()); + + if (msg.getEdgeId() != null) { + builder.setEdgeIdMSB(msg.getEdgeId().getId().getMostSignificantBits()) + .setEdgeIdLSB(msg.getEdgeId().getId().getLeastSignificantBits()); + } + + return builder.build(); } private static DeviceEdgeUpdateMsg fromProto(TransportProtos.DeviceEdgeUpdateMsgProto proto) { + EdgeId edgeId = null; + if (proto.hasEdgeIdMSB() && proto.hasEdgeIdLSB()) { + edgeId = new EdgeId(new UUID(proto.getEdgeIdMSB(), proto.getEdgeIdLSB())); + } return new DeviceEdgeUpdateMsg( TenantId.fromUUID(new UUID(proto.getTenantIdMSB(), proto.getTenantIdLSB())), new DeviceId(new UUID(proto.getDeviceIdMSB(), proto.getDeviceIdLSB())), - new EdgeId(new UUID(proto.getEdgeIdMSB(), proto.getEdgeIdLSB())) - ); + edgeId); } private static TransportProtos.DeviceNameOrTypeUpdateMsgProto toProto(DeviceNameOrTypeUpdateMsg msg) { diff --git a/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java index b9e0b8cd6c..f0c47bb53a 100644 --- a/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java @@ -54,6 +54,7 @@ import org.thingsboard.server.common.data.id.TenantProfileId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.common.data.security.model.JwtSettings; import org.thingsboard.server.dao.edge.EdgeDao; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.model.ModelConstants; @@ -843,6 +844,13 @@ public class EdgeControllerTest extends AbstractControllerTest { @Test public void testSyncEdge() throws Exception { + loginSysAdmin(); + // get jwt settings from yaml config + JwtSettings settings = doGet("/api/admin/jwtSettings", JwtSettings.class); + // save jwt settings into db + doPost("/api/admin/jwtSettings", settings).andExpect(status().isOk()); + loginTenantAdmin(); + Asset asset = new Asset(); asset.setName("Test Sync Edge Asset 1"); asset.setType("test"); @@ -904,10 +912,10 @@ public class EdgeControllerTest extends AbstractControllerTest { private void verifyFetchersMsgs(EdgeImitator edgeImitator) { Assert.assertTrue(popQueueMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Main")); Assert.assertTrue(popRuleChainMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Edge Root Rule Chain")); - Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgs(), "mail", true)); - Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgs(), "mail", false)); - Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgs(), "mailTemplates", true)); - Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgs(), "mailTemplates", false)); + Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgs(), "general")); + Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgs(), "mail")); + Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgs(), "connectivity")); + Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgs(), "jwt")); Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); @@ -953,12 +961,11 @@ public class EdgeControllerTest extends AbstractControllerTest { return false; } - private boolean popAdminSettingsMsg(List messages, String key, boolean isSystem) { + private boolean popAdminSettingsMsg(List messages, String key) { for (AbstractMessage message : messages) { if (message instanceof AdminSettingsUpdateMsg) { AdminSettingsUpdateMsg adminSettingsUpdateMsg = (AdminSettingsUpdateMsg) message; - if (key.equals(adminSettingsUpdateMsg.getKey()) - && isSystem == adminSettingsUpdateMsg.getIsSystem()) { + if (key.equals(adminSettingsUpdateMsg.getKey())) { messages.remove(message); return true; } diff --git a/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java index bfe6eb8190..3b63385bc8 100644 --- a/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java @@ -71,6 +71,7 @@ import org.thingsboard.server.common.data.query.NumericFilterPredicate; import org.thingsboard.server.common.data.queue.Queue; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainType; +import org.thingsboard.server.common.data.security.model.JwtSettings; import org.thingsboard.server.controller.AbstractControllerTest; import org.thingsboard.server.dao.edge.EdgeEventService; import org.thingsboard.server.edge.imitator.EdgeImitator; @@ -127,6 +128,13 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { @Before public void setupEdgeTest() throws Exception { + loginSysAdmin(); + + // get jwt settings from yaml config + JwtSettings settings = doGet("/api/admin/jwtSettings", JwtSettings.class); + // save jwt settings into db + doPost("/api/admin/jwtSettings", settings).andExpect(status().isOk()); + loginTenantAdmin(); installation(); @@ -237,9 +245,7 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { validateMsgsCnt(RuleChainMetadataUpdateMsg.class, 1); validateRuleChainMetadataUpdates(ruleChainUUID); - // 4 messages - // - 2 from fetcher - system level ('mail', 'mailTemplates') - // - 2 from fetcher - admin level ('mail', 'mailTemplates') + // 4 messages ('general', 'mail', 'connectivity', 'jwt) validateMsgsCnt(AdminSettingsUpdateMsg.class, 4); validateAdminSettings(); @@ -418,15 +424,23 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { Assert.assertEquals(4, adminSettingsUpdateMsgs.size()); for (AdminSettingsUpdateMsg adminSettingsUpdateMsg : adminSettingsUpdateMsgs) { + if (adminSettingsUpdateMsg.getKey().equals("general")) { + validateGeneralAdminSettings(adminSettingsUpdateMsg); + } if (adminSettingsUpdateMsg.getKey().equals("mail")) { validateMailAdminSettings(adminSettingsUpdateMsg); } - if (adminSettingsUpdateMsg.getKey().equals("mailTemplates")) { - validateMailTemplatesAdminSettings(adminSettingsUpdateMsg); + if (adminSettingsUpdateMsg.getKey().equals("connectivity")) { + validateConnectivityAdminSettings(adminSettingsUpdateMsg); } } } + private void validateGeneralAdminSettings(AdminSettingsUpdateMsg adminSettingsUpdateMsg) { + JsonNode jsonNode = JacksonUtil.toJsonNode(adminSettingsUpdateMsg.getJsonValue()); + Assert.assertNotNull(jsonNode.get("baseUrl")); + } + private void validateMailAdminSettings(AdminSettingsUpdateMsg adminSettingsUpdateMsg) { JsonNode jsonNode = JacksonUtil.toJsonNode(adminSettingsUpdateMsg.getJsonValue()); Assert.assertNotNull(jsonNode.get("mailFrom")); @@ -436,14 +450,14 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { Assert.assertNotNull(jsonNode.get("timeout")); } - private void validateMailTemplatesAdminSettings(AdminSettingsUpdateMsg adminSettingsUpdateMsg) { + private void validateConnectivityAdminSettings(AdminSettingsUpdateMsg adminSettingsUpdateMsg) { JsonNode jsonNode = JacksonUtil.toJsonNode(adminSettingsUpdateMsg.getJsonValue()); - Assert.assertNotNull(jsonNode.get("accountActivated")); - Assert.assertNotNull(jsonNode.get("accountLockout")); - Assert.assertNotNull(jsonNode.get("activation")); - Assert.assertNotNull(jsonNode.get("passwordWasReset")); - Assert.assertNotNull(jsonNode.get("resetPassword")); - Assert.assertNotNull(jsonNode.get("test")); + Assert.assertNotNull(jsonNode.get("http")); + Assert.assertNotNull(jsonNode.get("https")); + Assert.assertNotNull(jsonNode.get("mqtt")); + Assert.assertNotNull(jsonNode.get("mqtts")); + Assert.assertNotNull(jsonNode.get("coap")); + Assert.assertNotNull(jsonNode.get("coaps")); } private void validateAssetProfiles() throws Exception { diff --git a/application/src/test/java/org/thingsboard/server/edge/AssetEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/AssetEdgeTest.java index 08ea322cb8..0eb19a17fb 100644 --- a/application/src/test/java/org/thingsboard/server/edge/AssetEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/AssetEdgeTest.java @@ -173,7 +173,7 @@ public class AssetEdgeTest extends AbstractEdgeTest { assetUpdateMsgBuilder.setIdMSB(uuid.getMostSignificantBits()); assetUpdateMsgBuilder.setIdLSB(uuid.getLeastSignificantBits()); assetUpdateMsgBuilder.setName("Asset Edge 2"); - assetUpdateMsgBuilder.setType("test"); + assetUpdateMsgBuilder.setType("default"); assetUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); testAutoGeneratedCodeByProtobuf(assetUpdateMsgBuilder); uplinkMsgBuilder.addAssetUpdateMsg(assetUpdateMsgBuilder.build()); @@ -205,7 +205,7 @@ public class AssetEdgeTest extends AbstractEdgeTest { assetUpdateMsgBuilder.setIdMSB(uuid.getMostSignificantBits()); assetUpdateMsgBuilder.setIdLSB(uuid.getLeastSignificantBits()); assetUpdateMsgBuilder.setName(assetOnCloudName); - assetUpdateMsgBuilder.setType("test"); + assetUpdateMsgBuilder.setType("default"); assetUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); testAutoGeneratedCodeByProtobuf(assetUpdateMsgBuilder); uplinkMsgBuilder.addAssetUpdateMsg(assetUpdateMsgBuilder.build()); 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 de6939444d..e6fee370f1 100644 --- a/application/src/test/java/org/thingsboard/server/edge/DeviceEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/DeviceEdgeTest.java @@ -492,7 +492,7 @@ public class DeviceEdgeTest extends AbstractEdgeTest { deviceUpdateMsgBuilder.setIdMSB(uuid.getMostSignificantBits()); deviceUpdateMsgBuilder.setIdLSB(uuid.getLeastSignificantBits()); deviceUpdateMsgBuilder.setName(deviceOnCloudName); - deviceUpdateMsgBuilder.setType("test"); + deviceUpdateMsgBuilder.setType("default"); deviceUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); testAutoGeneratedCodeByProtobuf(deviceUpdateMsgBuilder); uplinkMsgBuilder.addDeviceUpdateMsg(deviceUpdateMsgBuilder.build()); @@ -541,7 +541,7 @@ public class DeviceEdgeTest extends AbstractEdgeTest { deviceUpdateMsgBuilder.setIdMSB(uuid.getMostSignificantBits()); deviceUpdateMsgBuilder.setIdLSB(uuid.getLeastSignificantBits()); deviceUpdateMsgBuilder.setName("Edge Device 2"); - deviceUpdateMsgBuilder.setType("test"); + deviceUpdateMsgBuilder.setType("default"); deviceUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); uplinkMsgBuilder.addDeviceUpdateMsg(deviceUpdateMsgBuilder.build()); diff --git a/application/src/test/java/org/thingsboard/server/edge/ResourceEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/ResourceEdgeTest.java index 6a3001f292..2ae57f7f19 100644 --- a/application/src/test/java/org/thingsboard/server/edge/ResourceEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/ResourceEdgeTest.java @@ -28,6 +28,7 @@ import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.UplinkMsg; import org.thingsboard.server.gen.edge.v1.UplinkResponseMsg; +import java.util.Optional; import java.util.UUID; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -120,4 +121,54 @@ public class ResourceEdgeTest extends AbstractEdgeTest { Assert.assertNotNull(tbResource); Assert.assertEquals("Edge Test Resource", tbResource.getName()); } + + @Test + public void testResourceToCloudWithNameThatAlreadyExistsOnCloud() throws Exception { + TbResource resource = new TbResource(); + resource.setResourceType(ResourceType.JKS); + resource.setTitle("Edge Test Resource"); + resource.setFileName(FILE_NAME); + resource.setData(TEST_DATA); + + edgeImitator.expectMessageAmount(1); + TbResource savedResource = doPost("/api/resource", resource, TbResource.class); + Assert.assertTrue(edgeImitator.waitForMessages()); + + UUID uuid = Uuids.timeBased(); + + UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); + ResourceUpdateMsg.Builder resourceUpdateMsgBuilder = ResourceUpdateMsg.newBuilder(); + resourceUpdateMsgBuilder.setIdMSB(uuid.getMostSignificantBits()); + resourceUpdateMsgBuilder.setIdLSB(uuid.getLeastSignificantBits()); + resourceUpdateMsgBuilder.setTitle("Edge Test Resource"); + resourceUpdateMsgBuilder.setResourceType(ResourceType.JKS.name()); + resourceUpdateMsgBuilder.setResourceKey(FILE_NAME); + resourceUpdateMsgBuilder.setFileName(FILE_NAME); + resourceUpdateMsgBuilder.setData(TEST_DATA); + resourceUpdateMsgBuilder.setIsSystem(false); + resourceUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); + testAutoGeneratedCodeByProtobuf(resourceUpdateMsgBuilder); + uplinkMsgBuilder.addResourceUpdateMsg(resourceUpdateMsgBuilder.build()); + + testAutoGeneratedCodeByProtobuf(uplinkMsgBuilder); + + edgeImitator.expectResponsesAmount(1); + edgeImitator.expectMessageAmount(1); + + edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build()); + + Assert.assertTrue(edgeImitator.waitForResponses()); + Assert.assertTrue(edgeImitator.waitForMessages()); + + Optional resourceUpdateMsgOpt = edgeImitator.findMessageByType(ResourceUpdateMsg.class); + Assert.assertTrue(resourceUpdateMsgOpt.isPresent()); + ResourceUpdateMsg latestResourceUpdateMsg = resourceUpdateMsgOpt.get(); + Assert.assertNotEquals(FILE_NAME, latestResourceUpdateMsg.getResourceKey()); + + Assert.assertNotEquals(savedResource.getUuidId(), uuid); + + TbResource tbResource = doGet("/api/resource/" + uuid, TbResource.class); + Assert.assertNotNull(tbResource); + Assert.assertNotEquals(FILE_NAME, tbResource.getName()); + } } diff --git a/common/cluster-api/src/main/proto/queue.proto b/common/cluster-api/src/main/proto/queue.proto index 6116411b2e..016a40f5dc 100644 --- a/common/cluster-api/src/main/proto/queue.proto +++ b/common/cluster-api/src/main/proto/queue.proto @@ -910,8 +910,8 @@ message EdgeNotificationMsgProto { string body = 10; PostTelemetryMsg postTelemetryMsg = 11; PostAttributeMsg postAttributesMsg = 12; - int64 sourceEdgeIdMSB = 13; - int64 sourceEdgeIdLSB = 14; + int64 originatorEdgeIdMSB = 13; + int64 originatorEdgeIdLSB = 14; } message EdgeEventUpdateMsgProto { @@ -945,8 +945,8 @@ message DeviceEdgeUpdateMsgProto { int64 tenantIdLSB = 2; int64 deviceIdMSB = 3; int64 deviceIdLSB = 4; - int64 edgeIdMSB = 5; - int64 edgeIdLSB = 6; + optional int64 edgeIdMSB = 5; + optional int64 edgeIdLSB = 6; } message DeviceNameOrTypeUpdateMsgProto { diff --git a/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeGrpcClient.java b/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeGrpcClient.java index 35a4ab9cd5..a9b12865a2 100644 --- a/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeGrpcClient.java +++ b/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeGrpcClient.java @@ -111,7 +111,7 @@ public class EdgeGrpcClient implements EdgeRpcClient { .setConnectRequestMsg(ConnectRequestMsg.newBuilder() .setEdgeRoutingKey(edgeKey) .setEdgeSecret(edgeSecret) - .setEdgeVersion(EdgeVersion.V_3_6_0) + .setEdgeVersion(EdgeVersion.V_3_6_1) .setMaxInboundMessageSize(maxInboundMessageSize) .build()) .build()); diff --git a/common/edge-api/src/main/proto/edge.proto b/common/edge-api/src/main/proto/edge.proto index f145ae3b98..71e84304ed 100644 --- a/common/edge-api/src/main/proto/edge.proto +++ b/common/edge-api/src/main/proto/edge.proto @@ -35,6 +35,7 @@ enum EdgeVersion { V_3_3_3 = 1; V_3_4_0 = 2; V_3_6_0 = 3; + V_3_6_1 = 4; } /** @@ -361,8 +362,8 @@ message WidgetsBundleUpdateMsg { optional bytes image = 6; bool isSystem = 7; optional string description = 8; - optional int32 order = 9; - optional string widgets = 10; + optional string widgets = 9; + optional int32 order = 10; } message WidgetTypeUpdateMsg { From 3d7b4d4c6219f5c848002a43ef3d525942e8a8f8 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Thu, 9 Nov 2023 18:21:50 +0200 Subject: [PATCH 081/252] Fixed comp error --- .../service/edge/rpc/processor/BaseEdgeProcessor.java | 10 +++++----- .../edge/rpc/processor/alarm/AlarmEdgeProcessor.java | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java index 276363e5f6..8790b5d1fd 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java @@ -467,9 +467,9 @@ public abstract class BaseEdgeProcessor { EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType()); EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction()); EntityId entityId = EntityIdFactory.getByEdgeEventTypeAndUuid(type, new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB())); - EdgeId sourceEdgeId = safeGetEdgeId(edgeNotificationMsg.getSourceEdgeIdMSB(), edgeNotificationMsg.getSourceEdgeIdLSB()); + EdgeId originatorEdgeId = safeGetEdgeId(edgeNotificationMsg.getOriginatorEdgeIdMSB(), edgeNotificationMsg.getOriginatorEdgeIdLSB()); if (type.isAllEdgesRelated()) { - return processEntityNotificationForAllEdges(tenantId, type, actionType, entityId, sourceEdgeId); + return processEntityNotificationForAllEdges(tenantId, type, actionType, entityId, originatorEdgeId); } else { JsonNode body = JacksonUtil.toJsonNode(edgeNotificationMsg.getBody()); EdgeId edgeId = safeGetEdgeId(edgeNotificationMsg.getEdgeIdMSB(), edgeNotificationMsg.getEdgeIdLSB()); @@ -481,19 +481,19 @@ public abstract class BaseEdgeProcessor { if (edgeId != null) { return saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, body); } else { - return processNotificationToRelatedEdges(tenantId, entityId, type, actionType, sourceEdgeId); + return processNotificationToRelatedEdges(tenantId, entityId, type, actionType, originatorEdgeId); } case DELETED: EdgeEventActionType deleted = EdgeEventActionType.DELETED; if (edgeId != null) { return saveEdgeEvent(tenantId, edgeId, type, deleted, entityId, body); } else { - return Futures.transform(Futures.allAsList(processActionForAllEdgesByTenantId(tenantId, type, deleted, entityId, body, sourceEdgeId)), + return Futures.transform(Futures.allAsList(processActionForAllEdgesByTenantId(tenantId, type, deleted, entityId, body, originatorEdgeId)), voids -> null, dbCallbackExecutorService); } case ASSIGNED_TO_EDGE: case UNASSIGNED_FROM_EDGE: - if (sourceEdgeId == null) { + if (originatorEdgeId == null) { ListenableFuture future = saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, body); return Futures.transformAsync(future, unused -> { if (type.equals(EdgeEventType.RULE_CHAIN)) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/AlarmEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/AlarmEdgeProcessor.java index f002b5c04e..882fd90c46 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/AlarmEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/AlarmEdgeProcessor.java @@ -71,7 +71,7 @@ public class AlarmEdgeProcessor extends BaseAlarmProcessor { public ListenableFuture processAlarmNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) { EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction()); AlarmId alarmId = new AlarmId(new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB())); - EdgeId sourceEdgeId = safeGetEdgeId(edgeNotificationMsg.getSourceEdgeIdMSB(), edgeNotificationMsg.getSourceEdgeIdLSB()); + EdgeId originatorEdgeId = safeGetEdgeId(edgeNotificationMsg.getOriginatorEdgeIdMSB(), edgeNotificationMsg.getOriginatorEdgeIdLSB()); switch (actionType) { case DELETED: Alarm deletedAlarm = JacksonUtil.fromString(edgeNotificationMsg.getBody(), Alarm.class); @@ -79,7 +79,7 @@ public class AlarmEdgeProcessor extends BaseAlarmProcessor { return Futures.immediateFuture(null); } List> delFutures = pushEventToAllRelatedEdges(tenantId, deletedAlarm.getOriginator(), - alarmId, actionType, JacksonUtil.valueToTree(deletedAlarm), sourceEdgeId); + alarmId, actionType, JacksonUtil.valueToTree(deletedAlarm), originatorEdgeId); return Futures.transform(Futures.allAsList(delFutures), voids -> null, dbCallbackExecutorService); default: ListenableFuture alarmFuture = alarmService.findAlarmByIdAsync(tenantId, alarmId); @@ -92,7 +92,7 @@ public class AlarmEdgeProcessor extends BaseAlarmProcessor { return Futures.immediateFuture(null); } List> futures = pushEventToAllRelatedEdges(tenantId, alarm.getOriginator(), - alarmId, actionType, null, sourceEdgeId); + alarmId, actionType, null, originatorEdgeId); return Futures.transform(Futures.allAsList(futures), voids -> null, dbCallbackExecutorService); }, dbCallbackExecutorService); } From d4ed67ff6dbbbe4be488745a2e45abf4dbff30ca Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Thu, 9 Nov 2023 18:28:45 +0200 Subject: [PATCH 082/252] Fixed license header --- .../rpc/fetch/AdminSettingsEdgeEventFetcher.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/AdminSettingsEdgeEventFetcher.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/AdminSettingsEdgeEventFetcher.java index e522529b8e..a292c89e9c 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/AdminSettingsEdgeEventFetcher.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/AdminSettingsEdgeEventFetcher.java @@ -1,12 +1,12 @@ /** * Copyright © 2016-2023 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 - *

+ * + * 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. @@ -55,8 +55,10 @@ public class AdminSettingsEdgeEventFetcher implements EdgeEventFetcher { List result = new ArrayList<>(); for (String key : keys) { AdminSettings adminSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, key); - result.add(EdgeUtils.constructEdgeEvent(tenantId, edgeId, EdgeEventType.ADMIN_SETTINGS, - EdgeEventActionType.UPDATED, null, JacksonUtil.valueToTree(adminSettings))); + if (adminSettings != null) { + result.add(EdgeUtils.constructEdgeEvent(tenantId, edgeId, EdgeEventType.ADMIN_SETTINGS, + EdgeEventActionType.UPDATED, null, JacksonUtil.valueToTree(adminSettings))); + } } return result; } From d06cfa9ae0799a9b1d3529b399cb75f15dcb9a6a Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Thu, 9 Nov 2023 19:19:56 +0200 Subject: [PATCH 083/252] Tests for referenced images and system image names --- application/src/main/data/images/names.json | 1108 ++++++++--------- .../install/update/SystemImagesMigrator.java | 32 + .../service/install/InstallScriptsTest.java | 55 + 3 files changed, 641 insertions(+), 554 deletions(-) diff --git a/application/src/main/data/images/names.json b/application/src/main/data/images/names.json index e9fd7c362c..e51dc621f2 100644 --- a/application/src/main/data/images/names.json +++ b/application/src/main/data/images/names.json @@ -1,556 +1,556 @@ { - "air_quality.png" : "Air quality - image", - "air_quality_card.png" : "Air quality index card - image", - "air_quality_card_with_background#background_image.png" : "Air quality index card with background - background image", - "air_quality_card_with_background.png" : "Air quality index card with background - image", - "air_quality_chart_card.png" : "Air quality index chart card - image", - "air_quality_chart_card_with_background#background_image.png" : "Air quality index chart card with background - background image", - "air_quality_chart_card_with_background.png" : "Air quality index chart card with background - image", - "alarm_count.png" : "Alarm count - image", - "alarm_widgets.alarms_table.png" : "Alarms table - image", - "alarm_widgets.png" : "Alarm widgets - image", - "analogue_gauges.analogue_compass.png" : "Compass - image", - "analogue_gauges.png" : "Analogue gauges - image", - "analogue_gauges.radial_gauge_canvas_gauges.png" : "Radial gauge - image", - "analogue_gauges.speed_gauge_canvas_gauges.png" : "Speed gauge - image", - "analogue_gauges.temperature_gauge_canvas_gauges.png" : "Thermometer scale - image", - "analogue_gauges.temperature_radial_gauge_canvas_gauges.png" : "Temperature radial gauge - image", - "battery_level.png" : "Battery level - image", - "cards.aggregated_value_card.png" : "Value and chart card - image", - "cards.attributes_card.png" : "Attributes card - image", - "cards.dashboard_state_widget.png" : "Dashboard state widget - image", - "cards.entities_hierarchy.png" : "Entities hierarchy - image", - "cards.entities_table.png" : "Entities table - image", - "cards.horizontal_value_card.png" : "Horizontal value card - image", - "cards.html_card.png" : "HTML Card - image", - "cards.html_value_card.png" : "HTML Value Card - image", - "cards.label_widget#background_image.svg" : "Label widget - background image", - "cards.label_widget.png" : "Label widget - image", - "cards.markdown_card.png" : "Markdown/HTML Card - image", - "cards.png" : "Cards - image", - "cards.qr_code.png" : "QR Code - image", - "cards.simple_card.png" : "Simple card - image", - "cards.timeseries_table.png" : "Timeseries table - image", - "cards.value_card.png" : "Value card - image", - "charts.bars.png" : "Bars - image", - "charts.basic_timeseries.png" : "Timeseries Line Chart - image", - "charts.doughnut_chart_js.png" : "Doughnut - image", - "charts.pie.png" : "Pie - Flot - image", - "charts.pie_chart_js.png" : "Pie - Chart.js - image", - "charts.png" : "Charts - image", - "charts.polar_area_chart_js.png" : "Polar Area - image", - "charts.radar_chart_js.png" : "Radar - image", - "charts.state_chart.png" : "State Chart - image", - "charts.timeseries_bars_flot.png" : "Timeseries Bar Chart - image", - "co2_card.png" : "CO2 card - image", - "co2_card_with_background#background_image.png" : "CO2 card with background - background image", - "co2_card_with_background.png" : "CO2 card with background - image", - "co2_chart_card.png" : "CO2 chart card - image", - "co2_chart_card_with_background#background_image.png" : "CO2 chart card with background - background image", - "co2_chart_card_with_background.png" : "CO2 chart card with background - image", - "control_widgets.knob_control.png" : "Knob Control - image", - "control_widgets.led_indicator.png" : "Led indicator - image", - "control_widgets.persistent_table.png" : "Persistent RPC table - image", - "control_widgets.png" : "Control widgets - image", - "control_widgets.round_switch.png" : "Round switch - image", - "control_widgets.rpc_debug_terminal.png" : "RPC debug terminal - image", - "control_widgets.rpc_remote_shell.png" : "RPC remote shell - image", - "control_widgets.rpcbutton.png" : "RPC Button - image", - "control_widgets.slide_toggle_control.png" : "Slide Toggle Control - image", - "control_widgets.switch_control.png" : "Switch Control - image", - "control_widgets.update_attributes.png" : "Update device attribute - image", - "count_widgets.png" : "Count widgets - image", - "date.date_range_navigator.png" : "Date-range-navigator - image", - "date.png" : "Date - image", - "digital_gauges.digital_bar.png" : "Digital horizontal bar - image", - "digital_gauges.digital_speedometer.png" : "Digital speedometer - image", - "digital_gauges.digital_thermometer.png" : "Digital thermometer - image", - "digital_gauges.digital_vertical_bar.png" : "Digital vertical bar - image", - "digital_gauges.gauge_justgage.png" : "Gauge - image", - "digital_gauges.horizontal_bar_justgage.png" : "Horizontal bar - image", - "digital_gauges.lcd_bar_gauge.png" : "LCD bar gauge - image", - "digital_gauges.lcd_gauge.png" : "LCD gauge - image", - "digital_gauges.mini_gauge_justgage.png" : "Mini gauge - image", - "digital_gauges.neon_gauge_justgage.png" : "Neon gauge - image", - "digital_gauges.png" : "Digital gauges - image", - "digital_gauges.simple_gauge_justgage.png" : "Simple gauge - image", - "digital_gauges.simple_neon_gauge_justgage.png" : "Simple neon gauge - image", - "digital_gauges.vertical_bar_justgage.png" : "Vertical bar - image", - "doughnut.png" : "Doughnut - image", - "edge_widgets.edges_overview.png" : "Edge Quick Overview - image", - "edge_widgets.png" : "Edge widgets - image", - "entity_admin_widgets.asset_admin_table.png" : "Asset admin table - image", - "entity_admin_widgets.device_admin_table.png" : "Device admin table - image", - "entity_admin_widgets.png" : "Entity admin widgets - image", - "entity_count.png" : "Entity count - image", - "entity_widgets.png" : "Entity widgets - image", - "flooding_level_card.png" : "Flooding level card - image", - "flooding_level_card_with_background#background_image.png" : "Flooding level card with background - background image", - "flooding_level_card_with_background.png" : "Flooding level card with background - image", - "flooding_level_chart_card.png" : "Flooding level chart card - image", - "flooding_level_chart_card_with_background#background_image.png" : "Flooding level chart card with background - background image", - "flooding_level_chart_card_with_background.png" : "Flooding level chart card with background - image", - "flooding_level_progress_bar.png" : "Flooding level progress bar - image", - "flooding_level_progress_bar_with_background#background_image.png" : "Flooding level progress bar with background - background image", - "flooding_level_progress_bar_with_background.png" : "Flooding level progress bar with background - image", - "gateway_widgets.attributes_card.png" : "Gateway events - image", - "gateway_widgets.config_form_latest.png" : "Gateway configuration (Single device) - image", - "gateway_widgets.gateway_configuration.png" : "Gateway Configuration - image", - "gateway_widgets.gateway_connectors.png" : "Gateway connectors - image", - "gateway_widgets.gateway_custom_statistics.png" : "Gateway custom statistics - image", - "gateway_widgets.gateway_general_chart_statistics.png" : "Gateway general chart statistics - image", - "gateway_widgets.gateway_general_configuration.png" : "Gateway general configuration - image", - "gateway_widgets.gateway_logs.png" : "Gateway logs - image", - "gateway_widgets.png" : "Gateway widgets - image", - "gateway_widgets.service_rpc.png" : "Service RPC - image", - "gpio_widgets.basic_gpio_control.png" : "Basic GPIO Control - image", - "gpio_widgets.gpio_panel.png" : "Basic GPIO Panel - image", - "gpio_widgets.png" : "GPIO widgets - image", - "gpio_widgets.raspberry_pi_gpio_control.png" : "Raspberry Pi GPIO Control - image", - "gpio_widgets.raspberry_pi_gpio_panel.png" : "Raspberry Pi GPIO Panel - image", - "ground_temperature_card.png" : "Ground temperature card - image", - "ground_temperature_card_with_background#background_image.png" : "Ground temperature card with background - background image", - "ground_temperature_card_with_background.png" : "Ground temperature card with background - image", - "ground_temperature_chart_card.png" : "Ground temperature chart card - image", - "ground_temperature_chart_card_with_background#background_image.png" : "Ground temperature chart card with background - background image", - "ground_temperature_chart_card_with_background.png" : "Ground temperature chart card with background - image", - "home_page_widgets.dashboards.png" : "Dashboards - image", - "home_page_widgets.documentation_links.png" : "Documentation links - image", - "home_page_widgets.getting_started.png" : "Getting started - image", - "home_page_widgets.png" : "Home page widgets - image", - "home_page_widgets.quick_links.png" : "Quick links - image", - "home_page_widgets.usage_info.png" : "Usage info - image", - "horizontal_2_1_elliptical_tank.png" : "Horizontal 2:1 elliptical tank - image", - "horizontal_air_quality_card.png" : "Horizontal air quality index card - image", - "horizontal_air_quality_card_with_background#background_image.png" : "Horizontal air quality index card with background - background image", - "horizontal_air_quality_card_with_background.png" : "Horizontal air quality index card with background - image", - "horizontal_capsule_tank.png" : "Horizontal capsule tank - image", - "horizontal_co2_card.png" : "Horizontal CO2 card - image", - "horizontal_co2_card_with_background#background_image.png" : "Horizontal CO2 card with background - background image", - "horizontal_co2_card_with_background.png" : "Horizontal CO2 card with background - image", - "horizontal_cylinder_tank.png" : "Horizontal cylinder tank - image", - "horizontal_dish_ends_tank.png" : "Horizontal dish ends tank - image", - "horizontal_doughnut.png" : "Horizontal doughnut - image", - "horizontal_ellipse_tank.png" : "Horizontal ellipse tank - image", - "horizontal_flooding_level_card.png" : "Horizontal flooding level card - image", - "horizontal_flooding_level_card_with_background#background_image.png" : "Horizontal flooding level card with background - background image", - "horizontal_flooding_level_card_with_background.png" : "Horizontal flooding level card with background - image", - "horizontal_ground_temperature_card.png" : "Horizontal ground temperature card - image", - "horizontal_ground_temperature_card_with_background#background_image.png" : "Horizontal ground temperature card with background - background image", - "horizontal_ground_temperature_card_with_background.png" : "Horizontal ground temperature card with background - image", - "horizontal_humidity_card.png" : "Horizontal humidity card - image", - "horizontal_humidity_card_with_background#background_image.png" : "Horizontal humidity card with background - background image", - "horizontal_humidity_card_with_background.png" : "Horizontal humidity card with background - image", - "horizontal_illuminance_card.png" : "Horizontal illuminance card - image", - "horizontal_illuminance_card_with_background#background_image.png" : "Horizontal illuminance card with background - background image", - "horizontal_illuminance_card_with_background.png" : "Horizontal illuminance card with background - image", - "horizontal_leaf_wetness_card.png" : "Horizontal leaf wetness card - image", - "horizontal_leaf_wetness_card_with_background#background_image.png" : "Horizontal leaf wetness card with background - background image", - "horizontal_leaf_wetness_card_with_background.png" : "Horizontal leaf wetness card with background - image", - "horizontal_noise_level_card.png" : "Horizontal noise level card - image", - "horizontal_noise_level_card_with_background#background_image.png" : "Horizontal noise level card with background - background image", - "horizontal_noise_level_card_with_background.png" : "Horizontal noise level card with background - image", - "horizontal_oval_tank.png" : "Horizontal oval tank - image", - "horizontal_pm10_card.png" : "Horizontal PM10 card - image", - "horizontal_pm10_card_with_background#background_image.png" : "Horizontal PM10 card with background - background image", - "horizontal_pm10_card_with_background.png" : "Horizontal PM10 card with background - image", - "horizontal_pm2_5_card.png" : "Horizontal PM2.5 card - image", - "horizontal_pm2_5_card_with_background#background_image.png" : "Horizontal PM2.5 card with background - background image", - "horizontal_pm2_5_card_with_background.png" : "Horizontal PM2.5 card with background - image", - "horizontal_pressure_card.png" : "Horizontal pressure card - image", - "horizontal_pressure_card_with_background#background_image.png" : "Horizontal pressure card with background - background image", - "horizontal_pressure_card_with_background.png" : "Horizontal pressure card with background - image", - "horizontal_radon_level_card.png" : "Horizontal radon level card - image", - "horizontal_radon_level_card_with_background#background_image.png" : "Horizontal radon level card with background - background image", - "horizontal_radon_level_card_with_background.png" : "Horizontal radon level card with background - image", - "horizontal_rainfall_card.png" : "Horizontal rainfall card - image", - "horizontal_rainfall_card_with_background#background_image.png" : "Horizontal rainfall card with background - background image", - "horizontal_rainfall_card_with_background.png" : "Horizontal rainfall card with background - image", - "horizontal_snow_depth_card.png" : "Horizontal snow depth card - image", - "horizontal_snow_depth_card_with_background#background_image.png" : "Horizontal snow depth card with background - background image", - "horizontal_snow_depth_card_with_background.png" : "Horizontal snow depth card with background - image", - "horizontal_soil_moisture_card.png" : "Horizontal soil moisture card - image", - "horizontal_soil_moisture_card_with_background#background_image.png" : "Horizontal soil moisture card with background - background image", - "horizontal_soil_moisture_card_with_background.png" : "Horizontal soil moisture card with background - image", - "horizontal_solar_radiation_card.png" : "Horizontal solar radiation card - image", - "horizontal_solar_radiation_card_with_background#background_image.png" : "Horizontal solar radiation card with background - background image", - "horizontal_solar_radiation_card_with_background.png" : "Horizontal solar radiation card with background - image", - "horizontal_temperature_card.png" : "Horizontal temperature card - image", - "horizontal_temperature_card_with_background#background_image.png" : "Horizontal temperature card with background - background image", - "horizontal_temperature_card_with_background.png" : "Horizontal temperature card with background - image", - "horizontal_uv_index_card.png" : "Horizontal UV Index card - image", - "horizontal_uv_index_card_with_background#background_image.png" : "Horizontal UV Index card with background - background image", - "horizontal_uv_index_card_with_background.png" : "Horizontal UV Index card with background - image", - "horizontal_vibration_card.png" : "Horizontal vibration card - image", - "horizontal_vibration_card_with_background#background_image.png" : "Horizontal vibration card with background - background image", - "horizontal_vibration_card_with_background.png" : "Horizontal vibration card with background - image", - "horizontal_visibility_card.png" : "Horizontal visibility card - image", - "horizontal_visibility_card_with_background#background_image.png" : "Horizontal visibility card with background - background image", - "horizontal_visibility_card_with_background.png" : "Horizontal visibility card with background - image", - "horizontal_volatile_organic_compounds_card.png" : "Horizontal volatile organic compounds card - image", - "horizontal_volatile_organic_compounds_card_with_background#background_image.png" : "Horizontal volatile organic compounds card with background - background image", - "horizontal_volatile_organic_compounds_card_with_background.png" : "Horizontal volatile organic compounds card with background - image", - "horizontal_wind_speed_card.png" : "Horizontal wind speed card - image", - "horizontal_wind_speed_card_with_background#background_image.png" : "Horizontal wind speed card with background - background image", - "horizontal_wind_speed_card_with_background.png" : "Horizontal wind speed card with background - image", - "html_widgets.png" : "HTML widgets - image", - "humidity_card.png" : "Humidity card - image", - "humidity_card_with_background#background_image.png" : "Humidity card with background - background image", - "humidity_card_with_background.png" : "Humidity card with background - image", - "humidity_chart_card.png" : "Humidity chart card - image", - "humidity_chart_card_with_background#background_image.png" : "Humidity chart card with background - background image", - "humidity_chart_card_with_background.png" : "Humidity chart card with background - image", - "humidity_progress_bar.png" : "Humidity progress bar - image", - "humidity_progress_bar_with_background#background_image.png" : "Humidity progress bar with background - background image", - "humidity_progress_bar_with_background.png" : "Humidity progress bar with background - image", - "illuminance_card.png" : "Illuminance card - image", - "illuminance_card_with_background#background_image.png" : "Illuminance card with background - background image", - "illuminance_card_with_background.png" : "Illuminance card with background - image", - "illuminance_chart_card.png" : "Illuminance chart card - image", - "illuminance_chart_card_with_background#background_image.png" : "Illuminance chart card with background - background image", - "illuminance_chart_card_with_background.png" : "Illuminance chart card with background - image", - "illuminance_progress_bar.png" : "Illuminance progress bar - image", - "illuminance_progress_bar_with_background#background_image.png" : "Illuminance progress bar with background - background image", - "illuminance_progress_bar_with_background.png" : "Illuminance progress bar with background - image", - "indoor_co2_card.png" : "Indoor CO2 card - image", - "indoor_co2_card_with_background#background_image.png" : "Indoor CO2 card with background - background image", - "indoor_co2_card_with_background.png" : "Indoor CO2 card with background - image", - "indoor_co2_chart_card.png" : "Indoor CO2 chart card - image", - "indoor_co2_chart_card_with_background#background_image.png" : "Indoor CO2 chart card with background - background image", - "indoor_co2_chart_card_with_background.png" : "Indoor CO2 chart card with background - image", - "indoor_environment.png" : "Indoor Environment - image", - "indoor_horizontal_co2_card.png" : "Indoor horizontal CO2 card - image", - "indoor_horizontal_co2_card_with_background#background_image.png" : "Indoor horizontal CO2 card with background - background image", - "indoor_horizontal_co2_card_with_background.png" : "Indoor horizontal CO2 card with background - image", - "indoor_horizontal_humidity_card.png" : "Indoor horizontal humidity card - image", - "indoor_horizontal_humidity_card_with_background#background_image.png" : "Indoor horizontal humidity card with background - background image", - "indoor_horizontal_humidity_card_with_background.png" : "Indoor horizontal humidity card with background - image", - "indoor_horizontal_illuminance_card.png" : "Indoor horizontal illuminance card - image", - "indoor_horizontal_illuminance_card_with_background#background_image.png" : "Indoor horizontal illuminance card with background - background image", - "indoor_horizontal_illuminance_card_with_background.png" : "Indoor horizontal illuminance card with background - image", - "indoor_horizontal_pm10_card.png" : "Indoor horizontal PM10 card - image", - "indoor_horizontal_pm10_card_with_background#background_image.png" : "Indoor horizontal PM10 card with background - background image", - "indoor_horizontal_pm10_card_with_background.png" : "Indoor horizontal PM10 card with background - image", - "indoor_horizontal_pm2_5_card.png" : "Indoor horizontal PM2.5 card - image", - "indoor_horizontal_pm2_5_card_with_background#background_image.png" : "Indoor horizontal PM2.5 card with background - background image", - "indoor_horizontal_pm2_5_card_with_background.png" : "Indoor horizontal PM2.5 card with background - image", - "indoor_horizontal_temperature_card.png" : "Indoor horizontal temperature card - image", - "indoor_horizontal_temperature_card_with_background#background_image.png" : "Indoor horizontal temperature card with background - background image", - "indoor_horizontal_temperature_card_with_background.png" : "Indoor horizontal temperature card with background - image", - "indoor_humidity_card.png" : "Indoor humidity card - image", - "indoor_humidity_card_with_background#background_image.png" : "Indoor humidity card with background - background image", - "indoor_humidity_card_with_background.png" : "Indoor humidity card with background - image", - "indoor_humidity_chart_card.png" : "Indoor humidity chart card - image", - "indoor_humidity_chart_card_with_background#background_image.png" : "Indoor humidity chart card with background - background image", - "indoor_humidity_chart_card_with_background.png" : "Indoor humidity chart card with background - image", - "indoor_humidity_progress_bar.png" : "Indoor humidity progress bar - image", - "indoor_humidity_progress_bar_with_background#background_image.png" : "Indoor humidity progress bar with background - background image", - "indoor_humidity_progress_bar_with_background.png" : "Indoor humidity progress bar with background - image", - "indoor_illuminance_card.png" : "Indoor illuminance card - image", - "indoor_illuminance_card_with_background#background_image.png" : "Indoor illuminance card with background - background image", - "indoor_illuminance_card_with_background.png" : "Indoor illuminance card with background - image", - "indoor_illuminance_chart_card.png" : "Indoor illuminance chart card - image", - "indoor_illuminance_chart_card_with_background#background_image.png" : "Indoor illuminance chart card with background - background image", - "indoor_illuminance_chart_card_with_background.png" : "Indoor illuminance chart card with background - image", - "indoor_illuminance_progress_bar.png" : "Indoor illuminance progress bar - image", - "indoor_illuminance_progress_bar_with_background#background_image.png" : "Indoor illuminance progress bar with background - background image", - "indoor_illuminance_progress_bar_with_background.png" : "Indoor illuminance progress bar with background - image", - "indoor_pm10_card.png" : "Indoor PM10 card - image", - "indoor_pm10_card_with_background#background_image.png" : "Indoor PM10 card with background - background image", - "indoor_pm10_card_with_background.png" : "Indoor PM10 card with background - image", - "indoor_pm10_chart_card.png" : "Indoor PM10 chart card - image", - "indoor_pm10_chart_card_with_background#background_image.png" : "Indoor PM10 chart card with background - background image", - "indoor_pm10_chart_card_with_background.png" : "Indoor PM10 chart card with background - image", - "indoor_pm2_5_card.png" : "Indoor PM2.5 card - image", - "indoor_pm2_5_card_with_background#background_image.png" : "Indoor PM2.5 card with background - background image", - "indoor_pm2_5_card_with_background.png" : "Indoor PM2.5 card with background - image", - "indoor_pm2_5_chart_card.png" : "Indoor PM2.5 chart card - image", - "indoor_pm2_5_chart_card_with_background#background_image.png" : "Indoor PM2.5 chart card with background - background image", - "indoor_pm2_5_chart_card_with_background.png" : "Indoor PM2.5 chart card with background - image", - "indoor_simple_co2_chart_card.png" : "Indoor simple CO2 chart card - image", - "indoor_simple_co2_chart_card_with_background#background_image.png" : "Indoor simple CO2 chart card with background - background image", - "indoor_simple_co2_chart_card_with_background.png" : "Indoor simple CO2 chart card with background - image", - "indoor_simple_humidity_chart_card.png" : "Indoor simple humidity chart card - image", - "indoor_simple_humidity_chart_card_with_background#background_image.png" : "Indoor simple humidity chart card with background - background image", - "indoor_simple_humidity_chart_card_with_background.png" : "Indoor simple humidity chart card with background - image", - "indoor_simple_illuminance_chart_card.png" : "Indoor simple illuminance chart card - image", - "indoor_simple_illuminance_chart_card_with_background#background_image.png" : "Indoor simple illuminance chart card with background - background image", - "indoor_simple_illuminance_chart_card_with_background.png" : "Indoor simple illuminance chart card with background - image", - "indoor_simple_pm10_chart_card.png" : "Indoor simple PM10 chart card - image", - "indoor_simple_pm10_chart_card_with_background#background_image.png" : "Indoor simple PM10 chart card with background - background image", - "indoor_simple_pm10_chart_card_with_background.png" : "Indoor simple PM10 chart card with background - image", - "indoor_simple_pm2_5_chart_card.png" : "Indoor simple PM2.5 chart card - image", - "indoor_simple_pm2_5_chart_card_with_background#background_image.png" : "Indoor simple PM2.5 chart card with background - background image", - "indoor_simple_pm2_5_chart_card_with_background.png" : "Indoor simple PM2.5 chart card with background - image", - "indoor_simple_temperature_chart_card.png" : "Indoor simple temperature chart card - image", - "indoor_simple_temperature_chart_card_with_background#background_image.png" : "Indoor simple temperature chart card with background - background image", - "indoor_simple_temperature_chart_card_with_background.png" : "Indoor simple temperature chart card with background - image", - "indoor_temperature_card.png" : "Indoor temperature card - image", - "indoor_temperature_card_with_background#background_image.png" : "Indoor temperature card with background - background image", - "indoor_temperature_card_with_background.png" : "Indoor temperature card with background - image", - "indoor_temperature_chart_card.png" : "Indoor temperature chart card - image", - "indoor_temperature_chart_card_with_background#background_image.png" : "Indoor temperature chart card with background - background image", - "indoor_temperature_chart_card_with_background.png" : "Indoor temperature chart card with background - image", - "indoor_temperature_progress_bar.png" : "Indoor temperature progress bar - image", - "indoor_temperature_progress_bar_with_background#background_image.png" : "Indoor temperature progress bar with background - background image", - "indoor_temperature_progress_bar_with_background.png" : "Indoor temperature progress bar with background - image", - "input_widgets.device_claiming_widget.png" : "Device claiming widget - image", - "input_widgets.markers_placement_google_maps.png" : "Markers Placement - Google Maps - image", - "input_widgets.markers_placement_image_map#map_image.svg" : "Markers Placement - Image Map - map image", - "input_widgets.markers_placement_image_map.png" : "Markers Placement - Image Map - image", - "input_widgets.markers_placement_openstreetmap.png" : "Markers Placement - OpenStreetMap - image", - "input_widgets.png" : "Input widgets - image", - "input_widgets.update_boolean_timeseries.png" : "Update boolean timeseries - image", - "input_widgets.update_double_timeseries.png" : "Update double timeseries - image", - "input_widgets.update_integer_timeseries.png" : "Update integer timeseries - image", - "input_widgets.update_json_attribute.png" : "Update JSON attribute - image", - "input_widgets.update_location_timeseries.png" : "Update location timeseries - image", - "input_widgets.update_multiple_attributes.png" : "Update Multiple Attributes - image", - "input_widgets.update_server_boolean_attribute.png" : "Update server boolean attribute - image", - "input_widgets.update_server_date_attribute.png" : "Update server date attribute - image", - "input_widgets.update_server_double_attribute.png" : "Update server double attribute - image", - "input_widgets.update_server_image_attribute.png" : "Update server image attribute - image", - "input_widgets.update_server_integer_attribute.png" : "Update server integer attribute - image", - "input_widgets.update_server_location_attribute.png" : "Update server location attribute - image", - "input_widgets.update_server_string_attribute.png" : "Update server string attribute - image", - "input_widgets.update_shared_boolean_attribute.png" : "Update shared boolean attribute - image", - "input_widgets.update_shared_date_attribute.png" : "Update shared date attribute - image", - "input_widgets.update_shared_double_attribute.png" : "Update shared double attribute - image", - "input_widgets.update_shared_image_attribute.png" : "Update shared image attribute - image", - "input_widgets.update_shared_integer_attribute.png" : "Update shared integer attribute - image", - "input_widgets.update_shared_location_attribute.png" : "Update shared location attribute - image", - "input_widgets.update_shared_string_attribute.png" : "Update shared string attribute - image", - "input_widgets.update_string_timeseries.png" : "Update string timeseries - image", - "input_widgets.web_camera_input.png" : "Photo camera input - image", - "leaf_wetness_card.png" : "Leaf wetness card - image", - "leaf_wetness_card_with_background#background_image.png" : "Leaf wetness card with background - background image", - "leaf_wetness_card_with_background.png" : "Leaf wetness card with background - image", - "leaf_wetness_chart_card.png" : "Leaf wetness chart card - image", - "leaf_wetness_chart_card_with_background#background_image.png" : "Leaf wetness chart card with background - background image", - "leaf_wetness_chart_card_with_background.png" : "Leaf wetness chart card with background - image", - "leaf_wetness_progress_bar.png" : "Leaf wetness progress bar - image", - "leaf_wetness_progress_bar_with_background#background_image.png" : "Leaf wetness progress bar with background - background image", - "leaf_wetness_progress_bar_with_background.png" : "Leaf wetness progress bar with background - image", - "liquid_level_tanks.png" : "Liquid level - image", - "maps_v2.google_maps#marker_image_1.png" : "Google Map - marker image 1", - "maps_v2.google_maps#marker_image_2.png" : "Google Map - marker image 2", - "maps_v2.google_maps#marker_image_3.png" : "Google Map - marker image 3", - "maps_v2.google_maps#marker_image_4.png" : "Google Map - marker image 4", - "maps_v2.google_maps.png" : "Google Map - image", - "maps_v2.here_map#map_image.svg" : "HERE Map - map image", - "maps_v2.here_map#marker_image_1.png" : "HERE Map - marker image 1", - "maps_v2.here_map#marker_image_2.png" : "HERE Map - marker image 2", - "maps_v2.here_map#marker_image_3.png" : "HERE Map - marker image 3", - "maps_v2.here_map#marker_image_4.png" : "HERE Map - marker image 4", - "maps_v2.here_map.png" : "HERE Map - image", - "maps_v2.image_map#map_image.svg" : "Image Map - map image", - "maps_v2.image_map#marker_image_1.png" : "Image Map - marker image 1", - "maps_v2.image_map#marker_image_2.png" : "Image Map - marker image 2", - "maps_v2.image_map#marker_image_3.png" : "Image Map - marker image 3", - "maps_v2.image_map#marker_image_4.png" : "Image Map - marker image 4", - "maps_v2.image_map.png" : "Image Map - image", - "maps_v2.openstreetmap#marker_image_1.png" : "OpenStreet Map - marker image 1", - "maps_v2.openstreetmap#marker_image_2.png" : "OpenStreet Map - marker image 2", - "maps_v2.openstreetmap#marker_image_3.png" : "OpenStreet Map - marker image 3", - "maps_v2.openstreetmap#marker_image_4.png" : "OpenStreet Map - marker image 4", - "maps_v2.openstreetmap.png" : "OpenStreet Map - image", - "maps_v2.png" : "Maps - image", - "maps_v2.route_map#marker_image_1.png" : "Route Map - Google - marker image 1", - "maps_v2.route_map#marker_image_2.png" : "Route Map - Google - marker image 2", - "maps_v2.route_map#marker_image_3.png" : "Route Map - Google - marker image 3", - "maps_v2.route_map.png" : "Route Map - Google - image", - "maps_v2.route_map_openstreetmap#marker_image_1.png" : "Route Map - OpenStreet - marker image 1", - "maps_v2.route_map_openstreetmap#marker_image_2.png" : "Route Map - OpenStreet - marker image 2", - "maps_v2.route_map_openstreetmap#marker_image_3.png" : "Route Map - OpenStreet - marker image 3", - "maps_v2.route_map_openstreetmap.png" : "Route Map - OpenStreet - image", - "maps_v2.route_map_tencent_maps#marker_image_1.png" : "Route Map - Tencent - marker image 1", - "maps_v2.route_map_tencent_maps#marker_image_2.png" : "Route Map - Tencent - marker image 2", - "maps_v2.route_map_tencent_maps#marker_image_3.png" : "Route Map - Tencent - marker image 3", - "maps_v2.route_map_tencent_maps.png" : "Route Map - Tencent - image", - "maps_v2.tencent_maps#marker_image_1.png" : "Tencent Map - marker image 1", - "maps_v2.tencent_maps#marker_image_2.png" : "Tencent Map - marker image 2", - "maps_v2.tencent_maps#marker_image_3.png" : "Tencent Map - marker image 3", - "maps_v2.tencent_maps#marker_image_4.png" : "Tencent Map - marker image 4", - "maps_v2.tencent_maps.png" : "Tencent Map - image", - "maps_v2.test.png" : "Trip Animation - image", - "navigation_widgets.navigation_card.png" : "Navigation card - image", - "navigation_widgets.navigation_cards.png" : "Navigation cards - image", - "navigation_widgets.png" : "Navigation widgets - image", - "noise_level_card.png" : "Noise level card - image", - "noise_level_card_with_background#background_image.png" : "Noise level card with background - background image", - "noise_level_card_with_background.png" : "Noise level card with background - image", - "noise_level_chart_card.png" : "Noise level chart card - image", - "noise_level_chart_card_with_background#background_image.png" : "Noise level chart card with background - background image", - "noise_level_chart_card_with_background.png" : "Noise level chart card with background - image", - "outdoor_environment.png" : "Outdoor Environment - image", - "pm10_card.png" : "PM10 card - image", - "pm10_card_with_background#background_image.png" : "PM10 card with background - background image", - "pm10_card_with_background.png" : "PM10 card with background - image", - "pm10_chart_card.png" : "PM10 chart card - image", - "pm10_chart_card_with_background#background_image.png" : "PM10 chart card with background - background image", - "pm10_chart_card_with_background.png" : "PM10 chart card with background - image", - "pm2_5_card.png" : "PM2.5 card - image", - "pm2_5_card_with_background#background_image.png" : "PM2.5 card with background - background image", - "pm2_5_card_with_background.png" : "PM2.5 card with background - image", - "pm2_5_chart_card.png" : "PM2.5 chart card - image", - "pm2_5_chart_card_with_background#background_image.png" : "PM2.5 chart card with background - background image", - "pm2_5_chart_card_with_background.png" : "PM2.5 chart card with background - image", - "pressure_card.png" : "Pressure card - image", - "pressure_card_with_background#background_image.png" : "Pressure card with background - background image", - "pressure_card_with_background.png" : "Pressure card with background - image", - "pressure_chart_card.png" : "Pressure chart card - image", - "pressure_chart_card_with_background#background_image.png" : "Pressure chart card with background - background image", - "pressure_chart_card_with_background.png" : "Pressure chart card with background - image", - "pressure_progress_bar.png" : "Pressure progress bar - image", - "pressure_progress_bar_with_background#background_image.png" : "Pressure progress bar with background - background image", - "pressure_progress_bar_with_background.png" : "Pressure progress bar with background - image", - "progress_bar.png" : "Progress bar - image", - "radon_level_card.png" : "Radon level card - image", - "radon_level_card_with_background#background_image.png" : "Radon level card with background - background image", - "radon_level_card_with_background.png" : "Radon level card with background - image", - "radon_level_chart_card.png" : "Radon level chart card - image", - "radon_level_chart_card_with_background#background_image.png" : "Radon level chart card with background - background image", - "radon_level_chart_card_with_background.png" : "Radon level chart card with background - image", - "rainfall_card.png" : "Rainfall card - image", - "rainfall_card_with_background#background_image.png" : "Rainfall card with background - background image", - "rainfall_card_with_background.png" : "Rainfall card with background - image", - "rainfall_chart_card.png" : "Rainfall chart card - image", - "rainfall_chart_card_with_background#background_image.png" : "Rainfall chart card with background - background image", - "rainfall_chart_card_with_background.png" : "Rainfall chart card with background - image", - "rectangle_tank.png" : "Rectangle tank - image", - "signal_strength.png" : "Signal strength - image", - "simple_air_quality_chart_card.png" : "Simple air quality index chart card - image", - "simple_air_quality_chart_card_with_background#background_image.png" : "Simple air quality index chart card with background - background image", - "simple_air_quality_chart_card_with_background.png" : "Simple air quality index chart card with background - image", - "simple_co2_chart_card.png" : "Simple CO2 chart card - image", - "simple_co2_chart_card_with_background#background_image.png" : "Simple CO2 chart card with background - background image", - "simple_co2_chart_card_with_background.png" : "Simple CO2 chart card with background - image", - "simple_flooding_level_chart_card.png" : "Simple flooding level chart card - image", - "simple_flooding_level_chart_card_with_background#background_image.png" : "Simple flooding level chart card with background - background image", - "simple_flooding_level_chart_card_with_background.png" : "Simple flooding level chart card with background - image", - "simple_ground_temperature_chart_card.png" : "Simple ground temperature chart card - image", - "simple_ground_temperature_chart_card_with_background#background_image.png" : "Simple ground temperature chart card with background - background image", - "simple_ground_temperature_chart_card_with_background.png" : "Simple ground temperature chart card with background - image", - "simple_humidity_chart_card.png" : "Simple humidity chart card - image", - "simple_humidity_chart_card_with_background#background_image.png" : "Simple humidity chart card with background - background image", - "simple_humidity_chart_card_with_background.png" : "Simple humidity chart card with background - image", - "simple_illuminance_chart_card.png" : "Simple illuminance chart card - image", - "simple_illuminance_chart_card_with_background#background_image.png" : "Simple illuminance chart card with background - background image", - "simple_illuminance_chart_card_with_background.png" : "Simple illuminance chart card with background - image", - "simple_leaf_wetness_chart_card.png" : "Simple leaf wetness chart card - image", - "simple_leaf_wetness_chart_card_with_background#background_image.png" : "Simple leaf wetness chart card with background - background image", - "simple_leaf_wetness_chart_card_with_background.png" : "Simple leaf wetness chart card with background - image", - "simple_noise_level_chart_card.png" : "Simple noise level chart card - image", - "simple_noise_level_chart_card_with_background#background_image.png" : "Simple noise level chart card with background - background image", - "simple_noise_level_chart_card_with_background.png" : "Simple noise level chart card with background - image", - "simple_pm10_chart_card.png" : "Simple PM10 chart card - image", - "simple_pm10_chart_card_with_background#background_image.png" : "Simple PM10 chart card with background - background image", - "simple_pm10_chart_card_with_background.png" : "Simple PM10 chart card with background - image", - "simple_pm2_5_chart_card.png" : "Simple PM2.5 chart card - image", - "simple_pm2_5_chart_card_with_background#background_image.png" : "Simple PM2.5 chart card with background - background image", - "simple_pm2_5_chart_card_with_background.png" : "Simple PM2.5 chart card with background - image", - "simple_pressure_chart_card.png" : "Simple pressure chart card - image", - "simple_pressure_chart_card_with_background#background_image.png" : "Simple pressure chart card with background - background image", - "simple_pressure_chart_card_with_background.png" : "Simple pressure chart card with background - image", - "simple_radon_level_chart_card.png" : "Simple radon level chart card - image", - "simple_radon_level_chart_card_with_background#background_image.png" : "Simple radon level chart card with background - background image", - "simple_radon_level_chart_card_with_background.png" : "Simple radon level chart card with background - image", - "simple_rainfall_chart_card.png" : "Simple rainfall chart card - image", - "simple_rainfall_chart_card_with_background#background_image.png" : "Simple rainfall chart card with background - background image", - "simple_rainfall_chart_card_with_background.png" : "Simple rainfall chart card with background - image", - "simple_snow_depth_chart_card.png" : "Simple snow depth chart card - image", - "simple_snow_depth_chart_card_with_background#background_image.png" : "Simple snow depth chart card with background - background image", - "simple_snow_depth_chart_card_with_background.png" : "Simple snow depth chart card with background - image", - "simple_soil_moisture_chart_card.png" : "Simple soil moisture chart card - image", - "simple_soil_moisture_chart_card_with_background#background_image.png" : "Simple soil moisture chart card with background - background image", - "simple_soil_moisture_chart_card_with_background.png" : "Simple soil moisture chart card with background - image", - "simple_solar_radiation_chart_card.png" : "Simple solar radiation chart card - image", - "simple_solar_radiation_chart_card_with_background#background_image.png" : "Simple solar radiation chart card with background - background image", - "simple_solar_radiation_chart_card_with_background.png" : "Simple solar radiation chart card with background - image", - "simple_temperature_chart_card.png" : "Simple temperature chart card - image", - "simple_temperature_chart_card_with_background#background_image.png" : "Simple temperature chart card with background - background image", - "simple_temperature_chart_card_with_background.png" : "Simple temperature chart card with background - image", - "simple_uv_index_chart_card.png" : "Simple UV Index chart card - image", - "simple_uv_index_chart_card_with_background#background_image.png" : "Simple UV Index chart card with background - background image", - "simple_uv_index_chart_card_with_background.png" : "Simple UV Index chart card with background - image", - "simple_value_and_chart_card.png" : "Simple Value and chart card - image", - "simple_vibration_chart_card.png" : "Simple vibration chart card - image", - "simple_vibration_chart_card_with_background#background_image.png" : "Simple vibration chart card with background - background image", - "simple_vibration_chart_card_with_background.png" : "Simple vibration chart card with background - image", - "simple_visibility_chart_card.png" : "Simple visibility chart card - image", - "simple_visibility_chart_card_with_background#background_image.png" : "Simple visibility chart card with background - background image", - "simple_visibility_chart_card_with_background.png" : "Simple visibility chart card with background - image", - "simple_volatile_organic_compounds_chart_card.png" : "Simple volatile organic compounds chart card - image", - "simple_volatile_organic_compounds_chart_card_with_background#background_image.png" : "Simple volatile organic compounds chart card with background - background image", - "simple_volatile_organic_compounds_chart_card_with_background.png" : "Simple volatile organic compounds chart card with background - image", - "simple_wind_speed_chart_card.png" : "Simple wind speed chart card - image", - "simple_wind_speed_chart_card_with_background#background_image.png" : "Simple wind speed chart card with background - background image", - "simple_wind_speed_chart_card_with_background.png" : "Simple wind speed chart card with background - image", - "snow_depth_card.png" : "Snow depth card - image", - "snow_depth_card_with_background#background_image.png" : "Snow depth card with background - background image", - "snow_depth_card_with_background.png" : "Snow depth card with background - image", - "snow_depth_chart_card.png" : "Snow depth chart card - image", - "snow_depth_chart_card_with_background#background_image.png" : "Snow depth chart card with background - background image", - "snow_depth_chart_card_with_background.png" : "Snow depth chart card with background - image", - "soil_moisture_card.png" : "Soil moisture card - image", - "soil_moisture_card_with_background#background_image.png" : "Soil moisture card with background - background image", - "soil_moisture_card_with_background.png" : "Soil moisture card with background - image", - "soil_moisture_chart_card.png" : "Soil moisture chart card - image", - "soil_moisture_chart_card_with_background#background_image.png" : "Soil moisture chart card with background - background image", - "soil_moisture_chart_card_with_background.png" : "Soil moisture chart card with background - image", - "soil_moisture_progress_bar.png" : "Soil moisture progress bar - image", - "soil_moisture_progress_bar_with_background#background_image.png" : "Soil moisture progress bar with background - background image", - "soil_moisture_progress_bar_with_background.png" : "Soil moisture progress bar with background - image", - "solar_radiation_card.png" : "Solar radiation card - image", - "solar_radiation_card_with_background#background_image.png" : "Solar radiation card with background - background image", - "solar_radiation_card_with_background.png" : "Solar radiation card with background - image", - "solar_radiation_chart_card.png" : "Solar radiation chart card - image", - "solar_radiation_chart_card_with_background#background_image.png" : "Solar radiation chart card with background - background image", - "solar_radiation_chart_card_with_background.png" : "Solar radiation chart card with background - image", - "status_indicators.png" : "Status indicators - image", - "tables.png" : "Tables - image", - "temperature_card.png" : "Temperature card - image", - "temperature_card_with_background#background_image.png" : "Temperature card with background - background image", - "temperature_card_with_background.png" : "Temperature card with background - image", - "temperature_chart_card.png" : "Temperature chart card - image", - "temperature_chart_card_with_background#background_image.png" : "Temperature chart card with background - background image", - "temperature_chart_card_with_background.png" : "Temperature chart card with background - image", - "thermostats.maps_v2.openstreetmap#marker_image_1.svg" : "Thermostat maps - marker image 1", - "thermostats.maps_v2.openstreetmap#marker_image_2.svg" : "Thermostat maps - marker image 2", - "uv_index_card.png" : "UV Index card - image", - "uv_index_card_with_background#background_image.png" : "UV Index card with background - background image", - "uv_index_card_with_background.png" : "UV Index card with background - image", - "uv_index_chart_card.png" : "UV Index chart card - image", - "uv_index_chart_card_with_background#background_image.png" : "UV Index chart card with background - background image", - "uv_index_chart_card_with_background.png" : "UV Index chart card with background - image", - "vertical_capsule_tank.png" : "Vertical capsule tank - image", - "vertical_cylinder_tank.png" : "Vertical cylinder tank - image", - "vertical_oval_tank.png" : "Vertical oval tank - image", - "vibration_card.png" : "Vibration card - image", - "vibration_card_with_background#background_image.png" : "Vibration card with background - background image", - "vibration_card_with_background.png" : "Vibration card with background - image", - "vibration_chart_card.png" : "Vibration chart card - image", - "vibration_chart_card_with_background#background_image.png" : "Vibration chart card with background - background image", - "vibration_chart_card_with_background.png" : "Vibration chart card with background - image", - "visibility_card.png" : "Visibility card - image", - "visibility_card_with_background#background_image.png" : "Visibility card with background - background image", - "visibility_card_with_background.png" : "Visibility card with background - image", - "visibility_chart_card.png" : "Visibility chart card - image", - "visibility_chart_card_with_background#background_image.png" : "Visibility chart card with background - background image", - "visibility_chart_card_with_background.png" : "Visibility chart card with background - image", - "volatile_organic_compounds_card.png" : "Volatile organic compounds card - image", - "volatile_organic_compounds_card_with_background#background_image.png" : "Volatile organic compounds card with background - background image", - "volatile_organic_compounds_card_with_background.png" : "Volatile organic compounds card with background - image", - "volatile_organic_compounds_chart_card.png" : "Volatile organic compounds chart card - image", - "volatile_organic_compounds_chart_card_with_background#background_image.png" : "Volatile organic compounds chart card with background - background image", - "volatile_organic_compounds_chart_card_with_background.png" : "Volatile organic compounds chart card with background - image", - "wind_speed_and_direction.png" : "Wind speed and direction - image", - "wind_speed_and_direction_with_background#background_image.png" : "Wind speed and direction with background - background image", - "wind_speed_and_direction_with_background.png" : "Wind speed and direction with background - image", - "wind_speed_card.png" : "Wind speed card - image", - "wind_speed_card_with_background#background_image.png" : "Wind speed card with background - background image", - "wind_speed_card_with_background.png" : "Wind speed card with background - image", - "wind_speed_chart_card.png" : "Wind speed chart card - image", - "wind_speed_chart_card_with_background#background_image.png" : "Wind speed chart card with background - background image", - "wind_speed_chart_card_with_background.png" : "Wind speed chart card with background - image" + "air_quality.png": "Air quality - image", + "air_quality_card.png": "Air quality index card - image", + "air_quality_card_with_background#background_image.png": "Air quality index card with background - background image", + "air_quality_card_with_background.png": "Air quality index card with background - image", + "air_quality_chart_card.png": "Air quality index chart card - image", + "air_quality_chart_card_with_background#background_image.png": "Air quality index chart card with background - background image", + "air_quality_chart_card_with_background.png": "Air quality index chart card with background - image", + "alarm_count.png": "Alarm count - image", + "alarm_widgets.alarms_table.png": "Alarms table - image", + "alarm_widgets.png": "Alarm widgets - image", + "analogue_gauges.analogue_compass.png": "Compass - image", + "analogue_gauges.png": "Analogue gauges - image", + "analogue_gauges.radial_gauge_canvas_gauges.png": "Radial gauge - image", + "analogue_gauges.speed_gauge_canvas_gauges.png": "Speed gauge - image", + "analogue_gauges.temperature_gauge_canvas_gauges.png": "Thermometer scale - image", + "analogue_gauges.temperature_radial_gauge_canvas_gauges.png": "Temperature radial gauge - image", + "battery_level.png": "Battery level - image", + "cards.aggregated_value_card.png": "Value and chart card - image", + "cards.attributes_card.png": "Attributes card - image", + "cards.dashboard_state_widget.png": "Dashboard state widget - image", + "cards.entities_hierarchy.png": "Entities hierarchy - image", + "cards.entities_table.png": "Entities table - image", + "cards.horizontal_value_card.png": "Horizontal value card - image", + "cards.html_card.png": "HTML Card - image", + "cards.html_value_card.png": "HTML Value Card - image", + "cards.label_widget#background_image.svg": "Label widget - background image", + "cards.label_widget.png": "Label widget - image", + "cards.markdown_card.png": "Markdown/HTML Card - image", + "cards.png": "Cards - image", + "cards.qr_code.png": "QR Code - image", + "cards.simple_card.png": "Simple card - image", + "cards.timeseries_table.png": "Timeseries table - image", + "cards.value_card.png": "Value card - image", + "charts.bars.png": "Bars - image", + "charts.basic_timeseries.png": "Timeseries Line Chart - image", + "charts.doughnut_chart_js.png": "Doughnut - image", + "charts.pie.png": "Pie - Flot - image", + "charts.pie_chart_js.png": "Pie - Chart.js - image", + "charts.png": "Charts - image", + "charts.polar_area_chart_js.png": "Polar Area - image", + "charts.radar_chart_js.png": "Radar - image", + "charts.state_chart.png": "State Chart - image", + "charts.timeseries_bars_flot.png": "Timeseries Bar Chart - image", + "co2_card.png": "CO2 card - image", + "co2_card_with_background#background_image.png": "CO2 card with background - background image", + "co2_card_with_background.png": "CO2 card with background - image", + "co2_chart_card.png": "CO2 chart card - image", + "co2_chart_card_with_background#background_image.png": "CO2 chart card with background - background image", + "co2_chart_card_with_background.png": "CO2 chart card with background - image", + "control_widgets.knob_control.png": "Knob Control - image", + "control_widgets.led_indicator.png": "Led indicator - image", + "control_widgets.persistent_table.png": "Persistent RPC table - image", + "control_widgets.png": "Control widgets - image", + "control_widgets.round_switch.png": "Round switch - image", + "control_widgets.rpc_debug_terminal.png": "RPC debug terminal - image", + "control_widgets.rpc_remote_shell.png": "RPC remote shell - image", + "control_widgets.rpcbutton.png": "RPC Button - image", + "control_widgets.slide_toggle_control.png": "Slide Toggle Control - image", + "control_widgets.switch_control.png": "Switch Control - image", + "control_widgets.update_attributes.png": "Update device attribute - image", + "count_widgets.png": "Count widgets - image", + "date.date_range_navigator.png": "Date-range-navigator - image", + "date.png": "Date - image", + "digital_gauges.digital_bar.png": "Digital horizontal bar - image", + "digital_gauges.digital_speedometer.png": "Digital speedometer - image", + "digital_gauges.digital_thermometer.png": "Digital thermometer - image", + "digital_gauges.digital_vertical_bar.png": "Digital vertical bar - image", + "digital_gauges.gauge_justgage.png": "Gauge - image", + "digital_gauges.horizontal_bar_justgage.png": "Horizontal bar - image", + "digital_gauges.lcd_bar_gauge.png": "LCD bar gauge - image", + "digital_gauges.lcd_gauge.png": "LCD gauge - image", + "digital_gauges.mini_gauge_justgage.png": "Mini gauge - image", + "digital_gauges.neon_gauge_justgage.png": "Neon gauge - image", + "digital_gauges.png": "Digital gauges - image", + "digital_gauges.simple_gauge_justgage.png": "Simple gauge - image", + "digital_gauges.simple_neon_gauge_justgage.png": "Simple neon gauge - image", + "digital_gauges.vertical_bar_justgage.png": "Vertical bar - image", + "doughnut.png": "Doughnut - image", + "edge_widgets.edges_overview.png": "Edge Quick Overview - image", + "edge_widgets.png": "Edge widgets - image", + "entity_admin_widgets.asset_admin_table.png": "Asset admin table - image", + "entity_admin_widgets.device_admin_table.png": "Device admin table - image", + "entity_admin_widgets.png": "Entity admin widgets - image", + "entity_count.png": "Entity count - image", + "entity_widgets.png": "Entity widgets - image", + "flooding_level_card.png": "Flooding level card - image", + "flooding_level_card_with_background#background_image.png": "Flooding level card with background - background image", + "flooding_level_card_with_background.png": "Flooding level card with background - image", + "flooding_level_chart_card.png": "Flooding level chart card - image", + "flooding_level_chart_card_with_background#background_image.png": "Flooding level chart card with background - background image", + "flooding_level_chart_card_with_background.png": "Flooding level chart card with background - image", + "flooding_level_progress_bar.png": "Flooding level progress bar - image", + "flooding_level_progress_bar_with_background#background_image.png": "Flooding level progress bar with background - background image", + "flooding_level_progress_bar_with_background.png": "Flooding level progress bar with background - image", + "gateway_widgets.attributes_card.png": "Gateway events - image", + "gateway_widgets.config_form_latest.png": "Gateway configuration (Single device) - image", + "gateway_widgets.gateway_configuration.png": "Gateway Configuration - image", + "gateway_widgets.gateway_connectors.png": "Gateway connectors - image", + "gateway_widgets.gateway_custom_statistics.png": "Gateway custom statistics - image", + "gateway_widgets.gateway_general_chart_statistics.png": "Gateway general chart statistics - image", + "gateway_widgets.gateway_general_configuration.png": "Gateway general configuration - image", + "gateway_widgets.gateway_logs.png": "Gateway logs - image", + "gateway_widgets.png": "Gateway widgets - image", + "gateway_widgets.service_rpc.png": "Service RPC - image", + "gpio_widgets.basic_gpio_control.png": "Basic GPIO Control - image", + "gpio_widgets.gpio_panel.png": "Basic GPIO Panel - image", + "gpio_widgets.png": "GPIO widgets - image", + "gpio_widgets.raspberry_pi_gpio_control.png": "Raspberry Pi GPIO Control - image", + "gpio_widgets.raspberry_pi_gpio_panel.png": "Raspberry Pi GPIO Panel - image", + "ground_temperature_card.png": "Ground temperature card - image", + "ground_temperature_card_with_background#background_image.png": "Ground temperature card with background - background image", + "ground_temperature_card_with_background.png": "Ground temperature card with background - image", + "ground_temperature_chart_card.png": "Ground temperature chart card - image", + "ground_temperature_chart_card_with_background#background_image.png": "Ground temperature chart card with background - background image", + "ground_temperature_chart_card_with_background.png": "Ground temperature chart card with background - image", + "home_page_widgets.dashboards.png": "Dashboards - image", + "home_page_widgets.documentation_links.png": "Documentation links - image", + "home_page_widgets.getting_started.png": "Getting started - image", + "home_page_widgets.png": "Home page widgets - image", + "home_page_widgets.quick_links.png": "Quick links - image", + "home_page_widgets.usage_info.png": "Usage info - image", + "horizontal_2_1_elliptical_tank.png": "Horizontal 2:1 elliptical tank - image", + "horizontal_air_quality_card.png": "Horizontal air quality index card - image", + "horizontal_air_quality_card_with_background#background_image.png": "Horizontal air quality index card with background - background image", + "horizontal_air_quality_card_with_background.png": "Horizontal air quality index card with background - image", + "horizontal_capsule_tank.png": "Horizontal capsule tank - image", + "horizontal_co2_card.png": "Horizontal CO2 card - image", + "horizontal_co2_card_with_background#background_image.png": "Horizontal CO2 card with background - background image", + "horizontal_co2_card_with_background.png": "Horizontal CO2 card with background - image", + "horizontal_cylinder_tank.png": "Horizontal cylinder tank - image", + "horizontal_dish_ends_tank.png": "Horizontal dish ends tank - image", + "horizontal_doughnut.png": "Horizontal doughnut - image", + "horizontal_ellipse_tank.png": "Horizontal ellipse tank - image", + "horizontal_flooding_level_card.png": "Horizontal flooding level card - image", + "horizontal_flooding_level_card_with_background#background_image.png": "Horizontal flooding level card with background - background image", + "horizontal_flooding_level_card_with_background.png": "Horizontal flooding level card with background - image", + "horizontal_ground_temperature_card.png": "Horizontal ground temperature card - image", + "horizontal_ground_temperature_card_with_background#background_image.png": "Horizontal ground temperature card with background - background image", + "horizontal_ground_temperature_card_with_background.png": "Horizontal ground temperature card with background - image", + "horizontal_humidity_card.png": "Horizontal humidity card - image", + "horizontal_humidity_card_with_background#background_image.png": "Horizontal humidity card with background - background image", + "horizontal_humidity_card_with_background.png": "Horizontal humidity card with background - image", + "horizontal_illuminance_card.png": "Horizontal illuminance card - image", + "horizontal_illuminance_card_with_background#background_image.png": "Horizontal illuminance card with background - background image", + "horizontal_illuminance_card_with_background.png": "Horizontal illuminance card with background - image", + "horizontal_leaf_wetness_card.png": "Horizontal leaf wetness card - image", + "horizontal_leaf_wetness_card_with_background#background_image.png": "Horizontal leaf wetness card with background - background image", + "horizontal_leaf_wetness_card_with_background.png": "Horizontal leaf wetness card with background - image", + "horizontal_noise_level_card.png": "Horizontal noise level card - image", + "horizontal_noise_level_card_with_background#background_image.png": "Horizontal noise level card with background - background image", + "horizontal_noise_level_card_with_background.png": "Horizontal noise level card with background - image", + "horizontal_oval_tank.png": "Horizontal oval tank - image", + "horizontal_pm10_card.png": "Horizontal PM10 card - image", + "horizontal_pm10_card_with_background#background_image.png": "Horizontal PM10 card with background - background image", + "horizontal_pm10_card_with_background.png": "Horizontal PM10 card with background - image", + "horizontal_pm2_5_card.png": "Horizontal PM2.5 card - image", + "horizontal_pm2_5_card_with_background#background_image.png": "Horizontal PM2.5 card with background - background image", + "horizontal_pm2_5_card_with_background.png": "Horizontal PM2.5 card with background - image", + "horizontal_pressure_card.png": "Horizontal pressure card - image", + "horizontal_pressure_card_with_background#background_image.png": "Horizontal pressure card with background - background image", + "horizontal_pressure_card_with_background.png": "Horizontal pressure card with background - image", + "horizontal_radon_level_card.png": "Horizontal radon level card - image", + "horizontal_radon_level_card_with_background#background_image.png": "Horizontal radon level card with background - background image", + "horizontal_radon_level_card_with_background.png": "Horizontal radon level card with background - image", + "horizontal_rainfall_card.png": "Horizontal rainfall card - image", + "horizontal_rainfall_card_with_background#background_image.png": "Horizontal rainfall card with background - background image", + "horizontal_rainfall_card_with_background.png": "Horizontal rainfall card with background - image", + "horizontal_snow_depth_card.png": "Horizontal snow depth card - image", + "horizontal_snow_depth_card_with_background#background_image.png": "Horizontal snow depth card with background - background image", + "horizontal_snow_depth_card_with_background.png": "Horizontal snow depth card with background - image", + "horizontal_soil_moisture_card.png": "Horizontal soil moisture card - image", + "horizontal_soil_moisture_card_with_background#background_image.png": "Horizontal soil moisture card with background - background image", + "horizontal_soil_moisture_card_with_background.png": "Horizontal soil moisture card with background - image", + "horizontal_solar_radiation_card.png": "Horizontal solar radiation card - image", + "horizontal_solar_radiation_card_with_background#background_image.png": "Horizontal solar radiation card with background - background image", + "horizontal_solar_radiation_card_with_background.png": "Horizontal solar radiation card with background - image", + "horizontal_temperature_card.png": "Horizontal temperature card - image", + "horizontal_temperature_card_with_background#background_image.png": "Horizontal temperature card with background - background image", + "horizontal_temperature_card_with_background.png": "Horizontal temperature card with background - image", + "horizontal_uv_index_card.png": "Horizontal UV Index card - image", + "horizontal_uv_index_card_with_background#background_image.png": "Horizontal UV Index card with background - background image", + "horizontal_uv_index_card_with_background.png": "Horizontal UV Index card with background - image", + "horizontal_vibration_card.png": "Horizontal vibration card - image", + "horizontal_vibration_card_with_background#background_image.png": "Horizontal vibration card with background - background image", + "horizontal_vibration_card_with_background.png": "Horizontal vibration card with background - image", + "horizontal_visibility_card.png": "Horizontal visibility card - image", + "horizontal_visibility_card_with_background#background_image.png": "Horizontal visibility card with background - background image", + "horizontal_visibility_card_with_background.png": "Horizontal visibility card with background - image", + "horizontal_volatile_organic_compounds_card.png": "Horizontal volatile organic compounds card - image", + "horizontal_volatile_organic_compounds_card_with_background#background_image.png": "Horizontal volatile organic compounds card with background - background image", + "horizontal_volatile_organic_compounds_card_with_background.png": "Horizontal volatile organic compounds card with background - image", + "horizontal_wind_speed_card.png": "Horizontal wind speed card - image", + "horizontal_wind_speed_card_with_background#background_image.png": "Horizontal wind speed card with background - background image", + "horizontal_wind_speed_card_with_background.png": "Horizontal wind speed card with background - image", + "html_widgets.png": "HTML widgets - image", + "humidity_card.png": "Humidity card - image", + "humidity_card_with_background#background_image.png": "Humidity card with background - background image", + "humidity_card_with_background.png": "Humidity card with background - image", + "humidity_chart_card.png": "Humidity chart card - image", + "humidity_chart_card_with_background#background_image.png": "Humidity chart card with background - background image", + "humidity_chart_card_with_background.png": "Humidity chart card with background - image", + "humidity_progress_bar.png": "Humidity progress bar - image", + "humidity_progress_bar_with_background#background_image.png": "Humidity progress bar with background - background image", + "humidity_progress_bar_with_background.png": "Humidity progress bar with background - image", + "illuminance_card.png": "Illuminance card - image", + "illuminance_card_with_background#background_image.png": "Illuminance card with background - background image", + "illuminance_card_with_background.png": "Illuminance card with background - image", + "illuminance_chart_card.png": "Illuminance chart card - image", + "illuminance_chart_card_with_background#background_image.png": "Illuminance chart card with background - background image", + "illuminance_chart_card_with_background.png": "Illuminance chart card with background - image", + "illuminance_progress_bar.png": "Illuminance progress bar - image", + "illuminance_progress_bar_with_background#background_image.png": "Illuminance progress bar with background - background image", + "illuminance_progress_bar_with_background.png": "Illuminance progress bar with background - image", + "indoor_co2_card.png": "Indoor CO2 card - image", + "indoor_co2_card_with_background#background_image.png": "Indoor CO2 card with background - background image", + "indoor_co2_card_with_background.png": "Indoor CO2 card with background - image", + "indoor_co2_chart_card.png": "Indoor CO2 chart card - image", + "indoor_co2_chart_card_with_background#background_image.png": "Indoor CO2 chart card with background - background image", + "indoor_co2_chart_card_with_background.png": "Indoor CO2 chart card with background - image", + "indoor_environment.png": "Indoor Environment - image", + "indoor_horizontal_co2_card.png": "Indoor horizontal CO2 card - image", + "indoor_horizontal_co2_card_with_background#background_image.png": "Indoor horizontal CO2 card with background - background image", + "indoor_horizontal_co2_card_with_background.png": "Indoor horizontal CO2 card with background - image", + "indoor_horizontal_humidity_card.png": "Indoor horizontal humidity card - image", + "indoor_horizontal_humidity_card_with_background#background_image.png": "Indoor horizontal humidity card with background - background image", + "indoor_horizontal_humidity_card_with_background.png": "Indoor horizontal humidity card with background - image", + "indoor_horizontal_illuminance_card.png": "Indoor horizontal illuminance card - image", + "indoor_horizontal_illuminance_card_with_background#background_image.png": "Indoor horizontal illuminance card with background - background image", + "indoor_horizontal_illuminance_card_with_background.png": "Indoor horizontal illuminance card with background - image", + "indoor_horizontal_pm10_card.png": "Indoor horizontal PM10 card - image", + "indoor_horizontal_pm10_card_with_background#background_image.png": "Indoor horizontal PM10 card with background - background image", + "indoor_horizontal_pm10_card_with_background.png": "Indoor horizontal PM10 card with background - image", + "indoor_horizontal_pm2_5_card.png": "Indoor horizontal PM2.5 card - image", + "indoor_horizontal_pm2_5_card_with_background#background_image.png": "Indoor horizontal PM2.5 card with background - background image", + "indoor_horizontal_pm2_5_card_with_background.png": "Indoor horizontal PM2.5 card with background - image", + "indoor_horizontal_temperature_card.png": "Indoor horizontal temperature card - image", + "indoor_horizontal_temperature_card_with_background#background_image.png": "Indoor horizontal temperature card with background - background image", + "indoor_horizontal_temperature_card_with_background.png": "Indoor horizontal temperature card with background - image", + "indoor_humidity_card.png": "Indoor humidity card - image", + "indoor_humidity_card_with_background#background_image.png": "Indoor humidity card with background - background image", + "indoor_humidity_card_with_background.png": "Indoor humidity card with background - image", + "indoor_humidity_chart_card.png": "Indoor humidity chart card - image", + "indoor_humidity_chart_card_with_background#background_image.png": "Indoor humidity chart card with background - background image", + "indoor_humidity_chart_card_with_background.png": "Indoor humidity chart card with background - image", + "indoor_humidity_progress_bar.png": "Indoor humidity progress bar - image", + "indoor_humidity_progress_bar_with_background#background_image.png": "Indoor humidity progress bar with background - background image", + "indoor_humidity_progress_bar_with_background.png": "Indoor humidity progress bar with background - image", + "indoor_illuminance_card.png": "Indoor illuminance card - image", + "indoor_illuminance_card_with_background#background_image.png": "Indoor illuminance card with background - background image", + "indoor_illuminance_card_with_background.png": "Indoor illuminance card with background - image", + "indoor_illuminance_chart_card.png": "Indoor illuminance chart card - image", + "indoor_illuminance_chart_card_with_background#background_image.png": "Indoor illuminance chart card with background - background image", + "indoor_illuminance_chart_card_with_background.png": "Indoor illuminance chart card with background - image", + "indoor_illuminance_progress_bar.png": "Indoor illuminance progress bar - image", + "indoor_illuminance_progress_bar_with_background#background_image.png": "Indoor illuminance progress bar with background - background image", + "indoor_illuminance_progress_bar_with_background.png": "Indoor illuminance progress bar with background - image", + "indoor_pm10_card.png": "Indoor PM10 card - image", + "indoor_pm10_card_with_background#background_image.png": "Indoor PM10 card with background - background image", + "indoor_pm10_card_with_background.png": "Indoor PM10 card with background - image", + "indoor_pm10_chart_card.png": "Indoor PM10 chart card - image", + "indoor_pm10_chart_card_with_background#background_image.png": "Indoor PM10 chart card with background - background image", + "indoor_pm10_chart_card_with_background.png": "Indoor PM10 chart card with background - image", + "indoor_pm2_5_card.png": "Indoor PM2.5 card - image", + "indoor_pm2_5_card_with_background#background_image.png": "Indoor PM2.5 card with background - background image", + "indoor_pm2_5_card_with_background.png": "Indoor PM2.5 card with background - image", + "indoor_pm2_5_chart_card.png": "Indoor PM2.5 chart card - image", + "indoor_pm2_5_chart_card_with_background#background_image.png": "Indoor PM2.5 chart card with background - background image", + "indoor_pm2_5_chart_card_with_background.png": "Indoor PM2.5 chart card with background - image", + "indoor_simple_co2_chart_card.png": "Indoor simple CO2 chart card - image", + "indoor_simple_co2_chart_card_with_background#background_image.png": "Indoor simple CO2 chart card with background - background image", + "indoor_simple_co2_chart_card_with_background.png": "Indoor simple CO2 chart card with background - image", + "indoor_simple_humidity_chart_card.png": "Indoor simple humidity chart card - image", + "indoor_simple_humidity_chart_card_with_background#background_image.png": "Indoor simple humidity chart card with background - background image", + "indoor_simple_humidity_chart_card_with_background.png": "Indoor simple humidity chart card with background - image", + "indoor_simple_illuminance_chart_card.png": "Indoor simple illuminance chart card - image", + "indoor_simple_illuminance_chart_card_with_background#background_image.png": "Indoor simple illuminance chart card with background - background image", + "indoor_simple_illuminance_chart_card_with_background.png": "Indoor simple illuminance chart card with background - image", + "indoor_simple_pm10_chart_card.png": "Indoor simple PM10 chart card - image", + "indoor_simple_pm10_chart_card_with_background#background_image.png": "Indoor simple PM10 chart card with background - background image", + "indoor_simple_pm10_chart_card_with_background.png": "Indoor simple PM10 chart card with background - image", + "indoor_simple_pm2_5_chart_card.png": "Indoor simple PM2.5 chart card - image", + "indoor_simple_pm2_5_chart_card_with_background#background_image.png": "Indoor simple PM2.5 chart card with background - background image", + "indoor_simple_pm2_5_chart_card_with_background.png": "Indoor simple PM2.5 chart card with background - image", + "indoor_simple_temperature_chart_card.png": "Indoor simple temperature chart card - image", + "indoor_simple_temperature_chart_card_with_background#background_image.png": "Indoor simple temperature chart card with background - background image", + "indoor_simple_temperature_chart_card_with_background.png": "Indoor simple temperature chart card with background - image", + "indoor_temperature_card.png": "Indoor temperature card - image", + "indoor_temperature_card_with_background#background_image.png": "Indoor temperature card with background - background image", + "indoor_temperature_card_with_background.png": "Indoor temperature card with background - image", + "indoor_temperature_chart_card.png": "Indoor temperature chart card - image", + "indoor_temperature_chart_card_with_background#background_image.png": "Indoor temperature chart card with background - background image", + "indoor_temperature_chart_card_with_background.png": "Indoor temperature chart card with background - image", + "indoor_temperature_progress_bar.png": "Indoor temperature progress bar - image", + "indoor_temperature_progress_bar_with_background#background_image.png": "Indoor temperature progress bar with background - background image", + "indoor_temperature_progress_bar_with_background.png": "Indoor temperature progress bar with background - image", + "input_widgets.device_claiming_widget.png": "Device claiming widget - image", + "input_widgets.markers_placement_google_maps.png": "Markers Placement - Google Maps - image", + "input_widgets.markers_placement_image_map#map_image.svg": "Markers Placement - Image Map - map image", + "input_widgets.markers_placement_image_map.png": "Markers Placement - Image Map - image", + "input_widgets.markers_placement_openstreetmap.png": "Markers Placement - OpenStreetMap - image", + "input_widgets.png": "Input widgets - image", + "input_widgets.update_boolean_timeseries.png": "Update boolean timeseries - image", + "input_widgets.update_double_timeseries.png": "Update double timeseries - image", + "input_widgets.update_integer_timeseries.png": "Update integer timeseries - image", + "input_widgets.update_json_attribute.png": "Update JSON attribute - image", + "input_widgets.update_location_timeseries.png": "Update location timeseries - image", + "input_widgets.update_multiple_attributes.png": "Update Multiple Attributes - image", + "input_widgets.update_server_boolean_attribute.png": "Update server boolean attribute - image", + "input_widgets.update_server_date_attribute.png": "Update server date attribute - image", + "input_widgets.update_server_double_attribute.png": "Update server double attribute - image", + "input_widgets.update_server_image_attribute.png": "Update server image attribute - image", + "input_widgets.update_server_integer_attribute.png": "Update server integer attribute - image", + "input_widgets.update_server_location_attribute.png": "Update server location attribute - image", + "input_widgets.update_server_string_attribute.png": "Update server string attribute - image", + "input_widgets.update_shared_boolean_attribute.png": "Update shared boolean attribute - image", + "input_widgets.update_shared_date_attribute.png": "Update shared date attribute - image", + "input_widgets.update_shared_double_attribute.png": "Update shared double attribute - image", + "input_widgets.update_shared_image_attribute.png": "Update shared image attribute - image", + "input_widgets.update_shared_integer_attribute.png": "Update shared integer attribute - image", + "input_widgets.update_shared_location_attribute.png": "Update shared location attribute - image", + "input_widgets.update_shared_string_attribute.png": "Update shared string attribute - image", + "input_widgets.update_string_timeseries.png": "Update string timeseries - image", + "input_widgets.web_camera_input.png": "Photo camera input - image", + "leaf_wetness_card.png": "Leaf wetness card - image", + "leaf_wetness_card_with_background#background_image.png": "Leaf wetness card with background - background image", + "leaf_wetness_card_with_background.png": "Leaf wetness card with background - image", + "leaf_wetness_chart_card.png": "Leaf wetness chart card - image", + "leaf_wetness_chart_card_with_background#background_image.png": "Leaf wetness chart card with background - background image", + "leaf_wetness_chart_card_with_background.png": "Leaf wetness chart card with background - image", + "leaf_wetness_progress_bar.png": "Leaf wetness progress bar - image", + "leaf_wetness_progress_bar_with_background#background_image.png": "Leaf wetness progress bar with background - background image", + "leaf_wetness_progress_bar_with_background.png": "Leaf wetness progress bar with background - image", + "liquid_level_tanks.png": "Liquid level - image", + "maps_v2.google_maps#marker_image_1.png": "Google Map - marker image 1", + "maps_v2.google_maps#marker_image_2.png": "Google Map - marker image 2", + "maps_v2.google_maps#marker_image_3.png": "Google Map - marker image 3", + "maps_v2.google_maps#marker_image_4.png": "Google Map - marker image 4", + "maps_v2.google_maps.png": "Google Map - image", + "maps_v2.here_map#map_image.svg": "HERE Map - map image", + "maps_v2.here_map#marker_image_1.png": "HERE Map - marker image 1", + "maps_v2.here_map#marker_image_2.png": "HERE Map - marker image 2", + "maps_v2.here_map#marker_image_3.png": "HERE Map - marker image 3", + "maps_v2.here_map#marker_image_4.png": "HERE Map - marker image 4", + "maps_v2.here_map.png": "HERE Map - image", + "maps_v2.image_map#map_image.svg": "Image Map - map image", + "maps_v2.image_map#marker_image_1.png": "Image Map - marker image 1", + "maps_v2.image_map#marker_image_2.png": "Image Map - marker image 2", + "maps_v2.image_map#marker_image_3.png": "Image Map - marker image 3", + "maps_v2.image_map#marker_image_4.png": "Image Map - marker image 4", + "maps_v2.image_map.png": "Image Map - image", + "maps_v2.openstreetmap#marker_image_1.png": "OpenStreet Map - marker image 1", + "maps_v2.openstreetmap#marker_image_2.png": "OpenStreet Map - marker image 2", + "maps_v2.openstreetmap#marker_image_3.png": "OpenStreet Map - marker image 3", + "maps_v2.openstreetmap#marker_image_4.png": "OpenStreet Map - marker image 4", + "maps_v2.openstreetmap.png": "OpenStreet Map - image", + "maps_v2.png": "Maps - image", + "maps_v2.route_map#marker_image_1.png": "Route Map - Google - marker image 1", + "maps_v2.route_map#marker_image_2.png": "Route Map - Google - marker image 2", + "maps_v2.route_map#marker_image_3.png": "Route Map - Google - marker image 3", + "maps_v2.route_map.png": "Route Map - Google - image", + "maps_v2.route_map_openstreetmap#marker_image_1.png": "Route Map - OpenStreet - marker image 1", + "maps_v2.route_map_openstreetmap#marker_image_2.png": "Route Map - OpenStreet - marker image 2", + "maps_v2.route_map_openstreetmap#marker_image_3.png": "Route Map - OpenStreet - marker image 3", + "maps_v2.route_map_openstreetmap.png": "Route Map - OpenStreet - image", + "maps_v2.route_map_tencent_maps#marker_image_1.png": "Route Map - Tencent - marker image 1", + "maps_v2.route_map_tencent_maps#marker_image_2.png": "Route Map - Tencent - marker image 2", + "maps_v2.route_map_tencent_maps#marker_image_3.png": "Route Map - Tencent - marker image 3", + "maps_v2.route_map_tencent_maps.png": "Route Map - Tencent - image", + "maps_v2.tencent_maps#marker_image_1.png": "Tencent Map - marker image 1", + "maps_v2.tencent_maps#marker_image_2.png": "Tencent Map - marker image 2", + "maps_v2.tencent_maps#marker_image_3.png": "Tencent Map - marker image 3", + "maps_v2.tencent_maps#marker_image_4.png": "Tencent Map - marker image 4", + "maps_v2.tencent_maps.png": "Tencent Map - image", + "maps_v2.test.png": "Trip Animation - image", + "navigation_widgets.navigation_card.png": "Navigation card - image", + "navigation_widgets.navigation_cards.png": "Navigation cards - image", + "navigation_widgets.png": "Navigation widgets - image", + "noise_level_card.png": "Noise level card - image", + "noise_level_card_with_background#background_image.png": "Noise level card with background - background image", + "noise_level_card_with_background.png": "Noise level card with background - image", + "noise_level_chart_card.png": "Noise level chart card - image", + "noise_level_chart_card_with_background#background_image.png": "Noise level chart card with background - background image", + "noise_level_chart_card_with_background.png": "Noise level chart card with background - image", + "outdoor_environment.png": "Outdoor Environment - image", + "pm10_card.png": "PM10 card - image", + "pm10_card_with_background#background_image.png": "PM10 card with background - background image", + "pm10_card_with_background.png": "PM10 card with background - image", + "pm10_chart_card.png": "PM10 chart card - image", + "pm10_chart_card_with_background#background_image.png": "PM10 chart card with background - background image", + "pm10_chart_card_with_background.png": "PM10 chart card with background - image", + "pm2_5_card.png": "PM2.5 card - image", + "pm2_5_card_with_background#background_image.png": "PM2.5 card with background - background image", + "pm2_5_card_with_background.png": "PM2.5 card with background - image", + "pm2_5_chart_card.png": "PM2.5 chart card - image", + "pm2_5_chart_card_with_background#background_image.png": "PM2.5 chart card with background - background image", + "pm2_5_chart_card_with_background.png": "PM2.5 chart card with background - image", + "pressure_card.png": "Pressure card - image", + "pressure_card_with_background#background_image.png": "Pressure card with background - background image", + "pressure_card_with_background.png": "Pressure card with background - image", + "pressure_chart_card.png": "Pressure chart card - image", + "pressure_chart_card_with_background#background_image.png": "Pressure chart card with background - background image", + "pressure_chart_card_with_background.png": "Pressure chart card with background - image", + "pressure_progress_bar.png": "Pressure progress bar - image", + "pressure_progress_bar_with_background#background_image.png": "Pressure progress bar with background - background image", + "pressure_progress_bar_with_background.png": "Pressure progress bar with background - image", + "progress_bar.png": "Progress bar - image", + "radon_level_card.png": "Radon level card - image", + "radon_level_card_with_background#background_image.png": "Radon level card with background - background image", + "radon_level_card_with_background.png": "Radon level card with background - image", + "radon_level_chart_card.png": "Radon level chart card - image", + "radon_level_chart_card_with_background#background_image.png": "Radon level chart card with background - background image", + "radon_level_chart_card_with_background.png": "Radon level chart card with background - image", + "rainfall_card.png": "Rainfall card - image", + "rainfall_card_with_background#background_image.png": "Rainfall card with background - background image", + "rainfall_card_with_background.png": "Rainfall card with background - image", + "rainfall_chart_card.png": "Rainfall chart card - image", + "rainfall_chart_card_with_background#background_image.png": "Rainfall chart card with background - background image", + "rainfall_chart_card_with_background.png": "Rainfall chart card with background - image", + "rectangle_tank.png": "Rectangle tank - image", + "signal_strength.png": "Signal strength - image", + "simple_air_quality_chart_card.png": "Simple air quality index chart card - image", + "simple_air_quality_chart_card_with_background#background_image.png": "Simple air quality index chart card with background - background image", + "simple_air_quality_chart_card_with_background.png": "Simple air quality index chart card with background - image", + "simple_co2_chart_card.png": "Simple CO2 chart card - image", + "simple_co2_chart_card_with_background#background_image.png": "Simple CO2 chart card with background - background image", + "simple_co2_chart_card_with_background.png": "Simple CO2 chart card with background - image", + "simple_flooding_level_chart_card.png": "Simple flooding level chart card - image", + "simple_flooding_level_chart_card_with_background#background_image.png": "Simple flooding level chart card with background - background image", + "simple_flooding_level_chart_card_with_background.png": "Simple flooding level chart card with background - image", + "simple_ground_temperature_chart_card.png": "Simple ground temperature chart card - image", + "simple_ground_temperature_chart_card_with_background#background_image.png": "Simple ground temperature chart card with background - background image", + "simple_ground_temperature_chart_card_with_background.png": "Simple ground temperature chart card with background - image", + "simple_humidity_chart_card.png": "Simple humidity chart card - image", + "simple_humidity_chart_card_with_background#background_image.png": "Simple humidity chart card with background - background image", + "simple_humidity_chart_card_with_background.png": "Simple humidity chart card with background - image", + "simple_illuminance_chart_card.png": "Simple illuminance chart card - image", + "simple_illuminance_chart_card_with_background#background_image.png": "Simple illuminance chart card with background - background image", + "simple_illuminance_chart_card_with_background.png": "Simple illuminance chart card with background - image", + "simple_leaf_wetness_chart_card.png": "Simple leaf wetness chart card - image", + "simple_leaf_wetness_chart_card_with_background#background_image.png": "Simple leaf wetness chart card with background - background image", + "simple_leaf_wetness_chart_card_with_background.png": "Simple leaf wetness chart card with background - image", + "simple_noise_level_chart_card.png": "Simple noise level chart card - image", + "simple_noise_level_chart_card_with_background#background_image.png": "Simple noise level chart card with background - background image", + "simple_noise_level_chart_card_with_background.png": "Simple noise level chart card with background - image", + "simple_pm10_chart_card.png": "Simple PM10 chart card - image", + "simple_pm10_chart_card_with_background#background_image.png": "Simple PM10 chart card with background - background image", + "simple_pm10_chart_card_with_background.png": "Simple PM10 chart card with background - image", + "simple_pm2_5_chart_card.png": "Simple PM2.5 chart card - image", + "simple_pm2_5_chart_card_with_background#background_image.png": "Simple PM2.5 chart card with background - background image", + "simple_pm2_5_chart_card_with_background.png": "Simple PM2.5 chart card with background - image", + "simple_pressure_chart_card.png": "Simple pressure chart card - image", + "simple_pressure_chart_card_with_background#background_image.png": "Simple pressure chart card with background - background image", + "simple_pressure_chart_card_with_background.png": "Simple pressure chart card with background - image", + "simple_radon_level_chart_card.png": "Simple radon level chart card - image", + "simple_radon_level_chart_card_with_background#background_image.png": "Simple radon level chart card with background - background image", + "simple_radon_level_chart_card_with_background.png": "Simple radon level chart card with background - image", + "simple_rainfall_chart_card.png": "Simple rainfall chart card - image", + "simple_rainfall_chart_card_with_background#background_image.png": "Simple rainfall chart card with background - background image", + "simple_rainfall_chart_card_with_background.png": "Simple rainfall chart card with background - image", + "simple_snow_depth_chart_card.png": "Simple snow depth chart card - image", + "simple_snow_depth_chart_card_with_background#background_image.png": "Simple snow depth chart card with background - background image", + "simple_snow_depth_chart_card_with_background.png": "Simple snow depth chart card with background - image", + "simple_soil_moisture_chart_card.png": "Simple soil moisture chart card - image", + "simple_soil_moisture_chart_card_with_background#background_image.png": "Simple soil moisture chart card with background - background image", + "simple_soil_moisture_chart_card_with_background.png": "Simple soil moisture chart card with background - image", + "simple_solar_radiation_chart_card.png": "Simple solar radiation chart card - image", + "simple_solar_radiation_chart_card_with_background#background_image.png": "Simple solar radiation chart card with background - background image", + "simple_solar_radiation_chart_card_with_background.png": "Simple solar radiation chart card with background - image", + "simple_temperature_chart_card.png": "Simple temperature chart card - image", + "simple_temperature_chart_card_with_background#background_image.png": "Simple temperature chart card with background - background image", + "simple_temperature_chart_card_with_background.png": "Simple temperature chart card with background - image", + "simple_uv_index_chart_card.png": "Simple UV Index chart card - image", + "simple_uv_index_chart_card_with_background#background_image.png": "Simple UV Index chart card with background - background image", + "simple_uv_index_chart_card_with_background.png": "Simple UV Index chart card with background - image", + "simple_value_and_chart_card.png": "Simple Value and chart card - image", + "simple_vibration_chart_card.png": "Simple vibration chart card - image", + "simple_vibration_chart_card_with_background#background_image.png": "Simple vibration chart card with background - background image", + "simple_vibration_chart_card_with_background.png": "Simple vibration chart card with background - image", + "simple_visibility_chart_card.png": "Simple visibility chart card - image", + "simple_visibility_chart_card_with_background#background_image.png": "Simple visibility chart card with background - background image", + "simple_visibility_chart_card_with_background.png": "Simple visibility chart card with background - image", + "simple_volatile_organic_compounds_chart_card.png": "Simple volatile organic compounds chart card - image", + "simple_volatile_organic_compounds_chart_card_with_background#background_image.png": "Simple volatile organic compounds chart card with background - background image", + "simple_volatile_organic_compounds_chart_card_with_background.png": "Simple volatile organic compounds chart card with background - image", + "simple_wind_speed_chart_card.png": "Simple wind speed chart card - image", + "simple_wind_speed_chart_card_with_background#background_image.png": "Simple wind speed chart card with background - background image", + "simple_wind_speed_chart_card_with_background.png": "Simple wind speed chart card with background - image", + "snow_depth_card.png": "Snow depth card - image", + "snow_depth_card_with_background#background_image.png": "Snow depth card with background - background image", + "snow_depth_card_with_background.png": "Snow depth card with background - image", + "snow_depth_chart_card.png": "Snow depth chart card - image", + "snow_depth_chart_card_with_background#background_image.png": "Snow depth chart card with background - background image", + "snow_depth_chart_card_with_background.png": "Snow depth chart card with background - image", + "soil_moisture_card.png": "Soil moisture card - image", + "soil_moisture_card_with_background#background_image.png": "Soil moisture card with background - background image", + "soil_moisture_card_with_background.png": "Soil moisture card with background - image", + "soil_moisture_chart_card.png": "Soil moisture chart card - image", + "soil_moisture_chart_card_with_background#background_image.png": "Soil moisture chart card with background - background image", + "soil_moisture_chart_card_with_background.png": "Soil moisture chart card with background - image", + "soil_moisture_progress_bar.png": "Soil moisture progress bar - image", + "soil_moisture_progress_bar_with_background#background_image.png": "Soil moisture progress bar with background - background image", + "soil_moisture_progress_bar_with_background.png": "Soil moisture progress bar with background - image", + "solar_radiation_card.png": "Solar radiation card - image", + "solar_radiation_card_with_background#background_image.png": "Solar radiation card with background - background image", + "solar_radiation_card_with_background.png": "Solar radiation card with background - image", + "solar_radiation_chart_card.png": "Solar radiation chart card - image", + "solar_radiation_chart_card_with_background#background_image.png": "Solar radiation chart card with background - background image", + "solar_radiation_chart_card_with_background.png": "Solar radiation chart card with background - image", + "status_indicators.png": "Status indicators - image", + "tables.png": "Tables - image", + "temperature_card.png": "Temperature card - image", + "temperature_card_with_background#background_image.png": "Temperature card with background - background image", + "temperature_card_with_background.png": "Temperature card with background - image", + "temperature_chart_card.png": "Temperature chart card - image", + "temperature_chart_card_with_background#background_image.png": "Temperature chart card with background - background image", + "temperature_chart_card_with_background.png": "Temperature chart card with background - image", + "thermostats.maps_v2.openstreetmap#marker_image_1.svg": "Thermostat maps - marker image 1", + "thermostats.maps_v2.openstreetmap#marker_image_2.svg": "Thermostat maps - marker image 2", + "uv_index_card.png": "UV Index card - image", + "uv_index_card_with_background#background_image.png": "UV Index card with background - background image", + "uv_index_card_with_background.png": "UV Index card with background - image", + "uv_index_chart_card.png": "UV Index chart card - image", + "uv_index_chart_card_with_background#background_image.png": "UV Index chart card with background - background image", + "uv_index_chart_card_with_background.png": "UV Index chart card with background - image", + "vertical_capsule_tank.png": "Vertical capsule tank - image", + "vertical_cylinder_tank.png": "Vertical cylinder tank - image", + "vertical_oval_tank.png": "Vertical oval tank - image", + "vibration_card.png": "Vibration card - image", + "vibration_card_with_background#background_image.png": "Vibration card with background - background image", + "vibration_card_with_background.png": "Vibration card with background - image", + "vibration_chart_card.png": "Vibration chart card - image", + "vibration_chart_card_with_background#background_image.png": "Vibration chart card with background - background image", + "vibration_chart_card_with_background.png": "Vibration chart card with background - image", + "visibility_card.png": "Visibility card - image", + "visibility_card_with_background#background_image.png": "Visibility card with background - background image", + "visibility_card_with_background.png": "Visibility card with background - image", + "visibility_chart_card.png": "Visibility chart card - image", + "visibility_chart_card_with_background#background_image.png": "Visibility chart card with background - background image", + "visibility_chart_card_with_background.png": "Visibility chart card with background - image", + "volatile_organic_compounds_card.png": "Volatile organic compounds card - image", + "volatile_organic_compounds_card_with_background#background_image.png": "Volatile organic compounds card with background - background image", + "volatile_organic_compounds_card_with_background.png": "Volatile organic compounds card with background - image", + "volatile_organic_compounds_chart_card.png": "Volatile organic compounds chart card - image", + "volatile_organic_compounds_chart_card_with_background#background_image.png": "Volatile organic compounds chart card with background - background image", + "volatile_organic_compounds_chart_card_with_background.png": "Volatile organic compounds chart card with background - image", + "wind_speed_and_direction.png": "Wind speed and direction - image", + "wind_speed_and_direction_with_background#background_image.png": "Wind speed and direction with background - background image", + "wind_speed_and_direction_with_background.png": "Wind speed and direction with background - image", + "wind_speed_card.png": "Wind speed card - image", + "wind_speed_card_with_background#background_image.png": "Wind speed card with background - background image", + "wind_speed_card_with_background.png": "Wind speed card with background - image", + "wind_speed_chart_card.png": "Wind speed chart card - image", + "wind_speed_chart_card_with_background#background_image.png": "Wind speed chart card with background - background image", + "wind_speed_chart_card_with_background.png": "Wind speed chart card with background - image" } \ No newline at end of file diff --git a/application/src/main/java/org/thingsboard/server/service/install/update/SystemImagesMigrator.java b/application/src/main/java/org/thingsboard/server/service/install/update/SystemImagesMigrator.java index 1cbb412af4..52ac77b733 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/update/SystemImagesMigrator.java +++ b/application/src/main/java/org/thingsboard/server/service/install/update/SystemImagesMigrator.java @@ -19,19 +19,24 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.hash.Hashing; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.util.MediaTypeUtils; +import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.Base64; +import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.TreeMap; +import java.util.stream.Collectors; @Slf4j public class SystemImagesMigrator { // TEMPORARY @@ -45,6 +50,7 @@ public class SystemImagesMigrator { // TEMPORARY private static final Path demoDashboardsDir = dataDir.resolve("json").resolve("demo").resolve("dashboards"); private static final Map imageNames = new TreeMap<>(); + private static final Map imageHashes = new HashMap<>(); public static void main(String[] args) throws Exception { Files.createDirectories(imagesDir); @@ -196,6 +202,32 @@ public class SystemImagesMigrator { // TEMPORARY return "/api/images/system/" + imageKey; } + private static void checkDuplicates() throws IOException { + Files.walk(imagesDir) + .filter(path -> path.toFile().isFile()) + .filter(path -> !path.getFileName().toString().endsWith("json")) + .forEach(imageFile -> { + try { + byte[] data = Files.readAllBytes(imageFile); + String hash = Hashing.sha256().hashBytes(data).toString(); + imageHashes.put(imageFile.getFileName().toString(), hash); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + imageHashes.values().stream() + .distinct() + .forEach(hash -> { + List sameImages = imageHashes.entrySet().stream() + .filter(entry -> entry.getValue().equals(hash)) + .map(Map.Entry::getKey) + .collect(Collectors.toList()); + if (sameImages.size() > 1) { + System.err.println("Duplicated images (hash " + hash + "):\n" + String.join("\n", sameImages) + "\n"); + } + }); + } + private static String getText(JsonNode jsonNode, String field) { return Optional.ofNullable(jsonNode.get(field)) .filter(JsonNode::isTextual) diff --git a/application/src/test/java/org/thingsboard/server/service/install/InstallScriptsTest.java b/application/src/test/java/org/thingsboard/server/service/install/InstallScriptsTest.java index 26eead0ec7..ee6c7f8b50 100644 --- a/application/src/test/java/org/thingsboard/server/service/install/InstallScriptsTest.java +++ b/application/src/test/java/org/thingsboard/server/service/install/InstallScriptsTest.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.service.install; +import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.BeforeEach; @@ -36,12 +37,19 @@ import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.usagerecord.ApiLimitService; import org.thingsboard.server.dao.widget.WidgetTypeService; import org.thingsboard.server.dao.widget.WidgetsBundleService; +import org.thingsboard.server.service.install.update.ImagesUpdater; import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.UUID; +import java.util.regex.Pattern; +import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -63,6 +71,8 @@ class InstallScriptsTest { OAuth2ConfigTemplateService oAuth2TemplateService; @MockBean ResourceService resourceService; + @MockBean + ImagesUpdater imagesUpdater; @SpyBean InstallScripts installScripts; @@ -116,4 +126,49 @@ class InstallScriptsTest { .containsExactlyInAnyOrderElementsOf(Collections.emptyList()); } + @Test + public void testSystemImageNames() throws IOException { + Path imagesDir = Path.of(installScripts.getDataDir(), InstallScripts.IMAGES_DIR); + Path imageNamesFile = imagesDir.resolve("names.json"); + Map imageNames = JacksonUtil.OBJECT_MAPPER.readValue(imageNamesFile.toFile(), new TypeReference<>() {}); + + assertThat(imageNames.keySet()).containsAll(getSystemImages()); + } + + @Test + public void testReferencedImages() throws IOException { + Path jsonDir = Path.of(installScripts.getDataDir(), InstallScripts.JSON_DIR); + List files = Files.walk(jsonDir) + .filter(path -> path.toFile().isFile()) + .filter(path -> path.getFileName().toString().endsWith(".json")) + .collect(Collectors.toList()); + + Set referencedImages = new HashSet<>(); + Pattern imageUrlPattern = Pattern.compile("/api/images/system/(.+?)\\\\?\""); + for (Path file : files) { + String content = Files.readString(file); + imageUrlPattern.matcher(content).results() + .map(matchResult -> matchResult.group(1)) + .forEach(referencedImages::add); + } + assertThat(getSystemImages()).containsAll(referencedImages); + } + + private List getSystemImages() throws IOException { + String dataDir = installScripts.getDataDir(); + Path imagesDir = Path.of(dataDir, InstallScripts.IMAGES_DIR); + + return Files.list(imagesDir) + .filter(path -> path.toFile().isDirectory()) + .flatMap(dir -> { + try { + return Files.list(dir); + } catch (IOException e) { + throw new RuntimeException(e); + } + }) + .map(path -> path.getFileName().toString()) + .collect(Collectors.toList()); + } + } From 037820c9fae74930912870ea4605920add09fe85 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Fri, 10 Nov 2023 08:45:17 +0200 Subject: [PATCH 084/252] Improve stability of edge tests --- .../server/edge/AbstractEdgeTest.java | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java index 3b63385bc8..8a3dbf6ecb 100644 --- a/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java @@ -99,6 +99,7 @@ import java.util.List; import java.util.Optional; import java.util.TreeMap; import java.util.UUID; +import java.util.concurrent.TimeUnit; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -181,7 +182,7 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { } catch (Exception ignored) {} } - private void installation() { + private void installation() throws Exception { thermostatDeviceProfile = this.createDeviceProfile(THERMOSTAT_DEVICE_PROFILE_NAME, createMqttDeviceProfileTransportConfiguration(new JsonTransportPayloadConfiguration(), false)); extendDeviceProfileData(thermostatDeviceProfile); @@ -197,6 +198,9 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { + "/device/" + savedDevice.getUuidId(), Device.class); doPost("/api/edge/" + edge.getUuidId() + "/asset/" + savedAsset.getUuidId(), Asset.class); + + // wait until assign device and asset events are fully processed by edge notification service + TimeUnit.MILLISECONDS.sleep(500); } protected void extendDeviceProfileData(DeviceProfile deviceProfile) { @@ -247,21 +251,21 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { // 4 messages ('general', 'mail', 'connectivity', 'jwt) validateMsgsCnt(AdminSettingsUpdateMsg.class, 4); - validateAdminSettings(); + validateAdminSettings(4); // 4 messages // - 1 from default profile fetcher // - 2 from device profile fetcher (default and thermostat) // - 1 from device fetcher validateMsgsCnt(DeviceProfileUpdateMsg.class, 4); - validateDeviceProfiles(); + validateDeviceProfiles(4); // 3 messages // - 1 from default profile fetcher // - 1 message from asset profile fetcher // - 1 message from asset fetcher validateMsgsCnt(AssetProfileUpdateMsg.class, 3); - validateAssetProfiles(); + validateAssetProfiles(3); // 1 from device fetcher validateMsgsCnt(DeviceUpdateMsg.class, 1); @@ -332,13 +336,13 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { testAutoGeneratedCodeByProtobuf(tenantProfileUpdateMsg); } - private void validateDeviceProfiles() throws Exception { + private void validateDeviceProfiles(int expectedMsgCnt) throws Exception { List deviceProfileUpdateMsgList = edgeImitator.findAllMessagesByType(DeviceProfileUpdateMsg.class); // default msg default device profile from fetcher // default msg device profile from fetcher // thermostat msg from device profile fetcher // thermostat msg from device fetcher - Assert.assertEquals(4, deviceProfileUpdateMsgList.size()); + Assert.assertEquals(expectedMsgCnt, deviceProfileUpdateMsgList.size()); Optional thermostatProfileUpdateMsgOpt = deviceProfileUpdateMsgList.stream().filter(dfum -> THERMOSTAT_DEVICE_PROFILE_NAME.equals(dfum.getName())).findAny(); Assert.assertTrue(thermostatProfileUpdateMsgOpt.isPresent()); @@ -419,9 +423,9 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { Assert.assertEquals(expectedRuleChainUUID, ruleChainUUID); } - private void validateAdminSettings() { + private void validateAdminSettings(int expectedMsgCnt) { List adminSettingsUpdateMsgs = edgeImitator.findAllMessagesByType(AdminSettingsUpdateMsg.class); - Assert.assertEquals(4, adminSettingsUpdateMsgs.size()); + Assert.assertEquals(expectedMsgCnt, adminSettingsUpdateMsgs.size()); for (AdminSettingsUpdateMsg adminSettingsUpdateMsg : adminSettingsUpdateMsgs) { if (adminSettingsUpdateMsg.getKey().equals("general")) { @@ -460,9 +464,9 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { Assert.assertNotNull(jsonNode.get("coaps")); } - private void validateAssetProfiles() throws Exception { + private void validateAssetProfiles(int expectedMsgCnt) throws Exception { List assetProfileUpdateMsgs = edgeImitator.findAllMessagesByType(AssetProfileUpdateMsg.class); - Assert.assertEquals(3, assetProfileUpdateMsgs.size()); + Assert.assertEquals(expectedMsgCnt, assetProfileUpdateMsgs.size()); AssetProfileUpdateMsg assetProfileUpdateMsg = assetProfileUpdateMsgs.get(0); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, assetProfileUpdateMsg.getMsgType()); UUID assetProfileUUID = new UUID(assetProfileUpdateMsg.getIdMSB(), assetProfileUpdateMsg.getIdLSB()); From 4a1793c47f4f06072c5608c9ae384d53cac38be6 Mon Sep 17 00:00:00 2001 From: Yevhen Popok Date: Fri, 10 Nov 2023 10:14:04 +0200 Subject: [PATCH 085/252] packaging: remove dupplicated "adduser --quiet" option in deb preinst adduser command (from Debian "preinst" file) contains the duplicated option "--quiet" --- packaging/java/scripts/control/deb/preinst | 3 +-- packaging/js/scripts/control/deb/preinst | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/packaging/java/scripts/control/deb/preinst b/packaging/java/scripts/control/deb/preinst index eebe378588..10a9b92679 100644 --- a/packaging/java/scripts/control/deb/preinst +++ b/packaging/java/scripts/control/deb/preinst @@ -10,7 +10,6 @@ if ! getent passwd ${pkg.user} >/dev/null; then adduser --quiet \ --system \ --ingroup ${pkg.user} \ - --quiet \ --disabled-login \ --disabled-password \ --home ${pkg.installFolder} \ @@ -19,4 +18,4 @@ if ! getent passwd ${pkg.user} >/dev/null; then ${pkg.user} fi -exit 0 \ No newline at end of file +exit 0 diff --git a/packaging/js/scripts/control/deb/preinst b/packaging/js/scripts/control/deb/preinst index d2ebea46d7..e51297c6c1 100644 --- a/packaging/js/scripts/control/deb/preinst +++ b/packaging/js/scripts/control/deb/preinst @@ -8,7 +8,6 @@ if ! getent passwd ${pkg.user} >/dev/null; then adduser --quiet \ --system \ --ingroup ${pkg.user} \ - --quiet \ --disabled-login \ --disabled-password \ --home ${pkg.installFolder} \ From 709329150bafcd6a243f8e2c3a20720a3363d856 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Fri, 10 Nov 2023 11:56:15 +0200 Subject: [PATCH 086/252] added fix for test cleanup method --- .../server/controller/AbstractWebTest.java | 38 ++++++++++++++----- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java index 151e779ae0..61836fef1c 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java @@ -25,9 +25,11 @@ import io.jsonwebtoken.Header; import io.jsonwebtoken.Jwt; import io.jsonwebtoken.Jwts; import lombok.extern.slf4j.Slf4j; +import org.apache.http.HttpStatus; import org.awaitility.Awaitility; import org.hamcrest.Matcher; import org.hibernate.exception.ConstraintViolationException; +import org.jetbrains.annotations.NotNull; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -177,6 +179,7 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { * and {@link org.springframework.mock.web.MockAsyncContext#getTimeout()} */ private static final long DEFAULT_TIMEOUT = -1L; + private static final int CLEANUP_TENANT_RETRIES_COUNT = 3; protected MediaType contentType = MediaType.APPLICATION_JSON; @@ -330,10 +333,6 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { log.debug("Executing web test teardown"); loginSysAdmin(); - doDelete("/api/tenant/" + tenantId.getId().toString()) - .andExpect(status().isOk()); - deleteDifferentTenant(); - verifyNoTenantsLeft(); tenantProfileService.deleteTenantProfiles(TenantId.SYS_TENANT_ID); @@ -341,7 +340,30 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { log.info("Executed web test teardown"); } - void verifyNoTenantsLeft() throws Exception { + private void verifyNoTenantsLeft() throws Exception { + List loadedTenants = getAllTenants(); + if (!loadedTenants.isEmpty()) { + loadedTenants.forEach(tenant -> deleteTenant(tenant.getId())); + loadedTenants = getAllTenants(); + } + assertThat(loadedTenants).as("All tenants expected to be deleted, but some tenants left in the database").isEmpty(); + } + + private void deleteTenant(TenantId tenantId) { + int status = 0; + int retries = 0; + while (status != HttpStatus.SC_OK && retries < CLEANUP_TENANT_RETRIES_COUNT) { + retries++; + try { + status = doDelete("/api/tenant/" + tenantId.getId().toString()) + .andReturn().getResponse().getStatus(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + @NotNull + private List getAllTenants() throws Exception { List loadedTenants = new ArrayList<>(); PageLink pageLink = new PageLink(10); PageData pageData; @@ -353,8 +375,7 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { pageLink = pageLink.nextPageLink(); } } while (pageData.hasNext()); - - assertThat(loadedTenants).as("All tenants expected to be deleted, but some tenants left in the database").isEmpty(); + return loadedTenants; } protected void loginSysAdmin() throws Exception { @@ -465,8 +486,7 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { protected void deleteDifferentTenant() throws Exception { if (savedDifferentTenant != null) { loginSysAdmin(); - doDelete("/api/tenant/" + savedDifferentTenant.getId().getId().toString()) - .andExpect(status().isOk()); + deleteTenant(savedDifferentTenant.getId()); savedDifferentTenant = null; } } From 171f4ed4899680dd87aecb7d5abd13d00d2f5487 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Fri, 10 Nov 2023 12:00:44 +0200 Subject: [PATCH 087/252] refactoring --- .../org/thingsboard/server/controller/AbstractWebTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java index 61836fef1c..35f0c52c8d 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java @@ -333,6 +333,8 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { log.debug("Executing web test teardown"); loginSysAdmin(); + deleteTenant(tenantId); + deleteDifferentTenant(); verifyNoTenantsLeft(); tenantProfileService.deleteTenantProfiles(TenantId.SYS_TENANT_ID); @@ -362,7 +364,7 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { } } } - @NotNull + private List getAllTenants() throws Exception { List loadedTenants = new ArrayList<>(); PageLink pageLink = new PageLink(10); From 2bc9c61c69dc3f3a5855029245e01988328af517 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Fri, 10 Nov 2023 11:28:19 +0100 Subject: [PATCH 088/252] fixed removing blackbox volumes --- .../server/msa/ThingsBoardDbInstaller.java | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ThingsBoardDbInstaller.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ThingsBoardDbInstaller.java index e41bbc3449..fe467ec341 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ThingsBoardDbInstaller.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ThingsBoardDbInstaller.java @@ -25,6 +25,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.StringJoiner; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -74,8 +75,8 @@ public class ThingsBoardDbInstaller { new File("./../../docker/docker-compose.yml"), new File("./../../docker/docker-compose.volumes.yml"), IS_HYBRID_MODE - ? new File("./../../docker/docker-compose.hybrid.yml") - : new File("./../../docker/docker-compose.postgres.yml"), + ? new File("./../../docker/docker-compose.hybrid.yml") + : new File("./../../docker/docker-compose.postgres.yml"), new File("./../../docker/docker-compose.postgres.volumes.yml"), resolveRedisComposeFile(), resolveRedisComposeVolumesFile() @@ -156,7 +157,7 @@ public class ThingsBoardDbInstaller { return env; } - public void createVolumes() { + public void createVolumes() { try { dockerCompose.withCommand("volume create " + postgresDataVolume); @@ -200,7 +201,7 @@ public class ThingsBoardDbInstaller { } } else if (IS_REDIS_SENTINEL) { additionalServices.append(" redis-master"); - dockerCompose.withCommand("volume create " + redisSentinelDataVolume +"-" + "master"); + dockerCompose.withCommand("volume create " + redisSentinelDataVolume + "-" + "master"); dockerCompose.invokeDocker(); additionalServices.append(" redis-slave"); @@ -226,7 +227,8 @@ public class ThingsBoardDbInstaller { try { dockerCompose.withCommand("down -v"); dockerCompose.invokeCompose(); - } catch (Exception ignored) {} + } catch (Exception ignored) { + } } } @@ -239,15 +241,24 @@ public class ThingsBoardDbInstaller { copyLogs(tbSnmpTransportLogVolume, "./target/tb-snmp-transport-logs/"); copyLogs(tbVcExecutorLogVolume, "./target/tb-vc-executor-logs/"); - dockerCompose.withCommand("volume rm -f " + postgresDataVolume + " " + tbLogVolume + - " " + tbCoapTransportLogVolume + " " + tbLwm2mTransportLogVolume + " " + tbHttpTransportLogVolume + - " " + tbMqttTransportLogVolume + " " + tbSnmpTransportLogVolume + " " + tbVcExecutorLogVolume + resolveRedisComposeVolumeLog()); - dockerCompose.invokeDocker(); + StringJoiner rmVolumesCommand = new StringJoiner(" ") + .add("volume rm -f") + .add(postgresDataVolume) + .add(tbLogVolume) + .add(tbCoapTransportLogVolume) + .add(tbLwm2mTransportLogVolume) + .add(tbHttpTransportLogVolume) + .add(tbMqttTransportLogVolume) + .add(tbSnmpTransportLogVolume) + .add(tbVcExecutorLogVolume) + .add(resolveRedisComposeVolumeLog()); + + dockerCompose.withCommand(rmVolumesCommand.toString()); } private String resolveRedisComposeVolumeLog() { if (IS_REDIS_CLUSTER) { - return IntStream.range(0, 6).mapToObj(i -> " " + redisClusterDataVolume + "-" + i).collect(Collectors.joining()); + return IntStream.range(0, 6).mapToObj(i -> redisClusterDataVolume + "-" + i).collect(Collectors.joining()); } if (IS_REDIS_SENTINEL) { return redisSentinelDataVolume + "-" + "master " + " " + @@ -266,7 +277,7 @@ public class ThingsBoardDbInstaller { dockerCompose.withCommand("run -d --rm --name " + logsContainerName + " -v " + volumeName + ":/root alpine tail -f /dev/null"); dockerCompose.invokeDocker(); - dockerCompose.withCommand("cp " + logsContainerName + ":/root/. "+tbLogsDir.getAbsolutePath()); + dockerCompose.withCommand("cp " + logsContainerName + ":/root/. " + tbLogsDir.getAbsolutePath()); dockerCompose.invokeDocker(); dockerCompose.withCommand("rm -f " + logsContainerName); From 6551658bfc5a07074badaa3dc91348918b315e7f Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Fri, 10 Nov 2023 13:17:23 +0200 Subject: [PATCH 089/252] updated TbTransactionalCache with method that retrieve but doesn`t put value in cache --- .../server/cache/TbTransactionalCache.java | 25 ++++++++++++++++++ .../server/dao/asset/AssetProfileService.java | 4 +++ .../dao/device/DeviceProfileService.java | 4 +++ .../dao/entityview/EntityViewService.java | 2 ++ .../dao/asset/AssetProfileServiceImpl.java | 23 +++++++++++----- .../dao/device/DeviceProfileServiceImpl.java | 26 +++++++++++++------ .../server/dao/device/DeviceServiceImpl.java | 6 ++--- .../dao/entityview/EntityViewServiceImpl.java | 13 +++++++--- 8 files changed, 80 insertions(+), 23 deletions(-) diff --git a/common/cache/src/main/java/org/thingsboard/server/cache/TbTransactionalCache.java b/common/cache/src/main/java/org/thingsboard/server/cache/TbTransactionalCache.java index fc104ef7f1..219e89299f 100644 --- a/common/cache/src/main/java/org/thingsboard/server/cache/TbTransactionalCache.java +++ b/common/cache/src/main/java/org/thingsboard/server/cache/TbTransactionalCache.java @@ -47,6 +47,18 @@ public interface TbTransactionalCache newTransactionForKeys(List keys); + default V getOrFetchFromDB(K key, Supplier dbCall, boolean cacheNullValue, boolean putToCache) { + if (putToCache) { + return getAndPutInTransaction(key, dbCall, cacheNullValue); + } else { + TbCacheValueWrapper cacheValueWrapper = get(key); + if (cacheValueWrapper != null) { + return cacheValueWrapper.get(); + } + return dbCall.get(); + } + } + default V getAndPutInTransaction(K key, Supplier dbCall, boolean cacheNullValue) { TbCacheValueWrapper cacheValueWrapper = get(key); if (cacheValueWrapper != null) { @@ -69,6 +81,19 @@ public interface TbTransactionalCache R getOrFetchFromDB(K key, Supplier dbCall, Function cacheValueToResult, Function dbValueToCacheValue, boolean cacheNullValue, boolean putToCache) { + if (putToCache) { + return getAndPutInTransaction(key, dbCall, cacheValueToResult, dbValueToCacheValue, cacheNullValue); + } else { + TbCacheValueWrapper cacheValueWrapper = get(key); + if (cacheValueWrapper != null) { + var cacheValue = cacheValueWrapper.get(); + return cacheValue == null ? null : cacheValueToResult.apply(cacheValue); + } + return dbCall.get(); + } + } + default R getAndPutInTransaction(K key, Supplier dbCall, Function cacheValueToResult, Function dbValueToCacheValue, boolean cacheNullValue) { TbCacheValueWrapper cacheValueWrapper = get(key); if (cacheValueWrapper != null) { diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/asset/AssetProfileService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/asset/AssetProfileService.java index 1ec6fb3fb5..477c352b54 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/asset/AssetProfileService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/asset/AssetProfileService.java @@ -27,8 +27,12 @@ public interface AssetProfileService extends EntityDaoService { AssetProfile findAssetProfileById(TenantId tenantId, AssetProfileId assetProfileId); + AssetProfile findAssetProfileById(TenantId tenantId, AssetProfileId assetProfileId, boolean putInCache); + AssetProfile findAssetProfileByName(TenantId tenantId, String profileName); + AssetProfile findAssetProfileByName(TenantId tenantId, String profileName, boolean putInCache); + AssetProfileInfo findAssetProfileInfoById(TenantId tenantId, AssetProfileId assetProfileId); AssetProfile saveAssetProfile(AssetProfile assetProfile, boolean doValidate); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceProfileService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceProfileService.java index f94b709c97..5d325aa1a6 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceProfileService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceProfileService.java @@ -27,8 +27,12 @@ public interface DeviceProfileService extends EntityDaoService { DeviceProfile findDeviceProfileById(TenantId tenantId, DeviceProfileId deviceProfileId); + DeviceProfile findDeviceProfileById(TenantId tenantId, DeviceProfileId deviceProfileId, boolean putInCache); + DeviceProfile findDeviceProfileByName(TenantId tenantId, String profileName); + DeviceProfile findDeviceProfileByName(TenantId tenantId, String profileName, boolean putInCache); + DeviceProfileInfo findDeviceProfileInfoById(TenantId tenantId, DeviceProfileId deviceProfileId); DeviceProfile saveDeviceProfile(DeviceProfile deviceProfile, boolean doValidate); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java index 09bad2b48d..7a020767f5 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java @@ -50,6 +50,8 @@ public interface EntityViewService extends EntityDaoService { EntityView findEntityViewById(TenantId tenantId, EntityViewId entityViewId); + EntityView findEntityViewById(TenantId tenantId, EntityViewId entityViewId, boolean putInCache); + EntityView findEntityViewByTenantIdAndName(TenantId tenantId, String name); PageData findEntityViewByTenantId(TenantId tenantId, PageLink pageLink); diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileServiceImpl.java index fd17c484d7..075975cd96 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileServiceImpl.java @@ -90,14 +90,24 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService assetProfileDao.findById(tenantId, assetProfileId.getId()), true); + return cache.getOrFetchFromDB(AssetProfileCacheKey.fromId(assetProfileId), + () -> assetProfileDao.findById(tenantId, assetProfileId.getId()), true, putInCache); } @Override public AssetProfile findAssetProfileByName(TenantId tenantId, String profileName) { + return findAssetProfileByName(tenantId, profileName, true); + } + + @Override + public AssetProfile findAssetProfileByName(TenantId tenantId, String profileName, boolean putInCache) { log.trace("Executing findAssetProfileByName [{}][{}]", tenantId, profileName); Validator.validateString(profileName, INCORRECT_ASSET_PROFILE_NAME + profileName); return cache.getAndPutInTransaction(AssetProfileCacheKey.fromName(tenantId, profileName), @@ -126,8 +136,8 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService deviceProfileDao.findById(tenantId, deviceProfileId.getId()), true); + return cache.getOrFetchFromDB(DeviceProfileCacheKey.fromId(deviceProfileId), + () -> deviceProfileDao.findById(tenantId, deviceProfileId.getId()), true, putInCache); } @Override public DeviceProfile findDeviceProfileByName(TenantId tenantId, String profileName) { + return findDeviceProfileByName(tenantId, profileName, true); + } + + @Override + public DeviceProfile findDeviceProfileByName(TenantId tenantId, String profileName, boolean putInCache) { log.trace("Executing findDeviceProfileByName [{}][{}]", tenantId, profileName); validateString(profileName, INCORRECT_DEVICE_PROFILE_NAME + profileName); - return cache.getAndPutInTransaction(DeviceProfileCacheKey.fromName(tenantId, profileName), - () -> deviceProfileDao.findByName(tenantId, profileName), true); + return cache.getOrFetchFromDB(DeviceProfileCacheKey.fromName(tenantId, profileName), + () -> deviceProfileDao.findByName(tenantId, profileName), true, putInCache); } @Override @@ -163,8 +173,8 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService entityViewDao.findById(tenantId, entityViewId.getId()) - , EntityViewCacheValue::getEntityView, v -> new EntityViewCacheValue(v, null), true); + , EntityViewCacheValue::getEntityView, v -> new EntityViewCacheValue(v, null), true, putInCache); } @Override From 7248f70517d23a117b167b26856c229fe0620443 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Fri, 10 Nov 2023 14:20:42 +0200 Subject: [PATCH 090/252] added log printing to coap commands --- .../DeviceConnectivityControllerTest.java | 20 +++++++++---------- .../dao/util/DeviceConnectivityUtil.java | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) 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 a4897f6bd9..227d37f483 100644 --- a/application/src/test/java/org/thingsboard/server/controller/DeviceConnectivityControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/DeviceConnectivityControllerTest.java @@ -250,16 +250,16 @@ public class DeviceConnectivityControllerTest extends AbstractControllerTest { credentials.getCredentialsId())); JsonNode linuxCoapCommands = commands.get(COAP); - assertThat(linuxCoapCommands.get(COAP).asText()).isEqualTo(String.format("coap-client -m POST coap://localhost:5683/api/v1/%s/telemetry " + + assertThat(linuxCoapCommands.get(COAP).asText()).isEqualTo(String.format("coap-client -v 6 -m POST coap://localhost:5683/api/v1/%s/telemetry " + "-t json -e \"{temperature:25}\"", credentials.getCredentialsId())); - assertThat(linuxCoapCommands.get(COAPS).asText()).isEqualTo(String.format("coap-client-openssl -m POST coaps://localhost:5684/api/v1/%s/telemetry" + + assertThat(linuxCoapCommands.get(COAPS).asText()).isEqualTo(String.format("coap-client-openssl -v 6 -m POST coaps://localhost:5684/api/v1/%s/telemetry" + " -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); JsonNode dockerCoapCommands = commands.get(COAP).get(DOCKER); assertThat(dockerCoapCommands.get(COAP).asText()).isEqualTo(String.format("docker run --rm -it --network=host" + - " thingsboard/coap-clients coap-client -m POST coap://localhost:5683/api/v1/%s/telemetry -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + " thingsboard/coap-clients coap-client -v 6 -m POST coap://localhost:5683/api/v1/%s/telemetry -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); assertThat(dockerCoapCommands.get(COAPS).asText()).isEqualTo(String.format("docker run --rm -it --network=host" + - " thingsboard/coap-clients coap-client-openssl -m POST coaps://localhost:5684/api/v1/%s/telemetry -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + " thingsboard/coap-clients coap-client-openssl -v 6 -m POST coaps://localhost:5684/api/v1/%s/telemetry -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); } @Test @@ -377,9 +377,9 @@ public class DeviceConnectivityControllerTest extends AbstractControllerTest { assertThat(commands).hasSize(1); JsonNode linuxCommands = commands.get(COAP); - assertThat(linuxCommands.get(COAP).asText()).isEqualTo(String.format("coap-client -m POST coap://localhost:5683/api/v1/%s/telemetry -t json -e \"{temperature:25}\"", + assertThat(linuxCommands.get(COAP).asText()).isEqualTo(String.format("coap-client -v 6 -m POST coap://localhost:5683/api/v1/%s/telemetry -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); - assertThat(linuxCommands.get(COAPS).asText()).isEqualTo(String.format("coap-client-openssl -m POST coaps://localhost:5684/api/v1/%s/telemetry -t json -e \"{temperature:25}\"", + assertThat(linuxCommands.get(COAPS).asText()).isEqualTo(String.format("coap-client-openssl -v 6 -m POST coaps://localhost:5684/api/v1/%s/telemetry -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); } @@ -601,15 +601,15 @@ public class DeviceConnectivityControllerTest extends AbstractControllerTest { credentials.getCredentialsId())); JsonNode linuxCoapCommands = commands.get(COAP); - assertThat(linuxCoapCommands.get(COAP).asText()).isEqualTo(String.format("coap-client -m POST coap://test.domain:5683/api/v1/%s/telemetry " + + assertThat(linuxCoapCommands.get(COAP).asText()).isEqualTo(String.format("coap-client -v 6 -m POST coap://test.domain:5683/api/v1/%s/telemetry " + "-t json -e \"{temperature:25}\"", credentials.getCredentialsId())); - assertThat(linuxCoapCommands.get(COAPS).asText()).isEqualTo(String.format("coap-client-openssl -m POST coaps://test.domain:5684/api/v1/%s/telemetry" + + assertThat(linuxCoapCommands.get(COAPS).asText()).isEqualTo(String.format("coap-client-openssl -v 6 -m POST coaps://test.domain:5684/api/v1/%s/telemetry" + " -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); JsonNode dockerCoapCommands = commands.get(COAP).get(DOCKER); assertThat(dockerCoapCommands.get(COAP).asText()).isEqualTo(String.format("docker run --rm -it " + - "thingsboard/coap-clients coap-client -m POST coap://test.domain:5683/api/v1/%s/telemetry -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + "thingsboard/coap-clients coap-client -v 6 -m POST coap://test.domain:5683/api/v1/%s/telemetry -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); assertThat(dockerCoapCommands.get(COAPS).asText()).isEqualTo(String.format("docker run --rm -it " + - "thingsboard/coap-clients coap-client-openssl -m POST coaps://test.domain:5684/api/v1/%s/telemetry -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + "thingsboard/coap-clients coap-client-openssl -v 6 -m POST coaps://test.domain:5684/api/v1/%s/telemetry -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java b/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java index bde018122c..aa446c12b5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java +++ b/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java @@ -162,7 +162,7 @@ public class DeviceConnectivityUtil { switch (deviceCredentials.getCredentialsType()) { case ACCESS_TOKEN: String client = COAPS.equals(protocol) ? "coap-client-openssl" : "coap-client"; - return String.format("%s -m POST %s://%s%s/api/v1/%s/telemetry -t json -e %s", + return String.format("%s -v 6 -m POST %s://%s%s/api/v1/%s/telemetry -t json -e %s", client, protocol, host, port, deviceCredentials.getCredentialsId(), JSON_EXAMPLE_PAYLOAD); default: return null; From 4d638213b92860c83841dfbac75d467460cffc3d Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 10 Nov 2023 14:33:29 +0200 Subject: [PATCH 091/252] UI: Removed connectivity command for CoAP in Mac OS --- ...e-check-connectivity-dialog.component.html | 25 ------------------- ...ice-check-connectivity-dialog.component.ts | 5 ++-- 2 files changed, 3 insertions(+), 27 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/device/device-check-connectivity-dialog.component.html b/ui-ngx/src/app/modules/home/pages/device/device-check-connectivity-dialog.component.html index 9b0b18e2c2..d36755b711 100644 --- a/ui-ngx/src/app/modules/home/pages/device/device-check-connectivity-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/device/device-check-connectivity-dialog.component.html @@ -226,31 +226,6 @@

device.connectivity.use-following-instructions
- - - - MacOS - - -
-
-
device.connectivity.install-necessary-client-tools
-
- - -
-
- - -
-
-
diff --git a/ui-ngx/src/app/modules/home/pages/device/device-check-connectivity-dialog.component.ts b/ui-ngx/src/app/modules/home/pages/device/device-check-connectivity-dialog.component.ts index eeb54381df..f2e4229314 100644 --- a/ui-ngx/src/app/modules/home/pages/device/device-check-connectivity-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/pages/device/device-check-connectivity-dialog.component.ts @@ -199,20 +199,21 @@ export class DeviceCheckConnectivityDialogComponent extends case 'android': this.httpTabIndex = 2; this.mqttTabIndex = 2; - this.coapTabIndex = 1; break; case 'macos': case 'ios': this.httpTabIndex = 1; this.mqttTabIndex = 1; + this.coapTabIndex = 1; break; case 'windows': this.httpTabIndex = 0; this.mqttTabIndex = 0; + this.coapTabIndex = 1; break; default: this.mqttTabIndex = this.commands.mqtt?.docker ? 3 : 0; - this.coapTabIndex = this.commands.coap?.docker ? 2 : 1; + this.coapTabIndex = this.commands.coap?.docker ? 1 : 0; } } From d761524fe412103d7afa4acb380fe526bfd6079b Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Fri, 10 Nov 2023 14:42:03 +0200 Subject: [PATCH 092/252] Telemetry edge processor: added check for attr/timeseries value size --- .../telemetry/TelemetryEdgeProcessor.java | 6 +++ .../telemetry/TelemetryEdgeProcessorTest.java | 42 +++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessorTest.java diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessor.java index 861d766638..647994dcd9 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessor.java @@ -37,6 +37,12 @@ public class TelemetryEdgeProcessor extends BaseTelemetryProcessor { } public DownlinkMsg convertTelemetryEventToDownlink(EdgeEvent edgeEvent) throws JsonProcessingException { + if (edgeEvent.getBody() != null && edgeEvent.getBody().toString().length() > 1000) { + log.debug("[{}][{}][{}] Conversion to a DownlinkMsg telemetry event failed due to a size limit violation. " + + "Current size is {}, but the limit is 1000. {}", edgeEvent.getTenantId(), edgeEvent.getEdgeId(), + edgeEvent.getEntityId(), edgeEvent.getBody().toString().length(), edgeEvent.getBody().toString().substring(0, 100)); + return null; + } EntityType entityType = EntityType.valueOf(edgeEvent.getType().name()); EntityDataProto entityDataProto = convertTelemetryEventToEntityDataProto( edgeEvent.getTenantId(), entityType, edgeEvent.getEntityId(), diff --git a/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessorTest.java b/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessorTest.java new file mode 100644 index 0000000000..9dbfa6ae45 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessorTest.java @@ -0,0 +1,42 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.processor.telemetry; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.extern.slf4j.Slf4j; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.gen.edge.v1.DownlinkMsg; + +@Slf4j +@RunWith(MockitoJUnitRunner.class) +public class TelemetryEdgeProcessorTest { + + @Test + public void testConvert_maxSizeLimit() throws Exception { + EdgeEvent edgeEvent = new EdgeEvent(); + ObjectNode body = JacksonUtil.newObjectNode(); + body.put("value", StringUtils.randomAlphanumeric(10000)); + edgeEvent.setBody(body); + DownlinkMsg downlinkMsg = new TelemetryEdgeProcessor().convertTelemetryEventToDownlink(edgeEvent); + Assert.assertNull(downlinkMsg); + } +} From 7559aa9fe37d32a62eca4a36ee2344d8a154cfe0 Mon Sep 17 00:00:00 2001 From: imbeacon Date: Fri, 10 Nov 2023 14:45:48 +0200 Subject: [PATCH 093/252] Added gateways dashboard creation on tenant add --- .../data/json/demo/dashboards/gateways.json | 1315 ----------------- .../dashboards/gateways.json} | 26 +- .../tenant/DefaultTbTenantService.java | 1 + .../service/install/InstallScripts.java | 9 + 4 files changed, 23 insertions(+), 1328 deletions(-) delete mode 100644 application/src/main/data/json/demo/dashboards/gateways.json rename application/src/main/data/json/{demo/dashboards/gateway.json => tenant/dashboards/gateways.json} (99%) diff --git a/application/src/main/data/json/demo/dashboards/gateways.json b/application/src/main/data/json/demo/dashboards/gateways.json deleted file mode 100644 index f4eb275c3c..0000000000 --- a/application/src/main/data/json/demo/dashboards/gateways.json +++ /dev/null @@ -1,1315 +0,0 @@ -{ - "title": "Gateways", - "image": null, - "mobileHide": false, - "mobileOrder": null, - "configuration": { - "widgets": { - "94715984-ae74-76e4-20b7-2f956b01ed80": { - "type": "latest", - "sizeX": 24, - "sizeY": 12, - "config": { - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 86400000, - "quickInterval": "CURRENT_DAY" - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1694085270425, - "endTimeMs": 1694171670425 - }, - "quickInterval": "CURRENT_DAY" - }, - "aggregation": { - "type": "NONE", - "limit": 200 - } - }, - "showTitle": true, - "backgroundColor": "rgb(255, 255, 255)", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "4px", - "settings": { - "enableSearch": true, - "displayPagination": true, - "defaultPageSize": 10, - "defaultSortOrder": "entityName", - "displayEntityName": true, - "displayEntityType": false, - "entitiesTitle": "List of gateways", - "enableSelectColumnDisplay": true, - "displayEntityLabel": false, - "entityNameColumnTitle": "Gateway Name" - }, - "title": "Devices gateway table", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400, - "padding": "5px 10px 5px 10px" - }, - "useDashboardTimewindow": false, - "showLegend": false, - "datasources": [ - { - "type": "entity", - "dataKeys": [ - { - "name": "active", - "type": "attribute", - "label": "Active", - "color": "#2196f3", - "settings": { - "columnWidth": "0px", - "useCellStyleFunction": true, - "useCellContentFunction": true, - "cellContentFunction": "value = '⬤';\nreturn value;", - "cellStyleFunction": "var color;\nif (value == 'false') {\n color = '#EB5757';\n} else {\n color = '#27AE60';\n}\nreturn {\n color: color,\n fontSize: '18px'\n};" - }, - "_hash": 0.3646047595211721 - }, - { - "name": "eventsSent", - "type": "timeseries", - "label": "Sent", - "color": "#4caf50", - "settings": { - "columnWidth": "0px", - "useCellStyleFunction": false, - "useCellContentFunction": false - }, - "_hash": 0.7235710720767985 - }, - { - "name": "eventsProduced", - "type": "timeseries", - "label": "Events", - "color": "#f44336", - "settings": { - "columnWidth": "0px", - "useCellStyleFunction": false, - "useCellContentFunction": false - }, - "_hash": 0.5085933386303254 - }, - { - "name": "LOGS", - "type": "timeseries", - "label": "Latest log", - "color": "#ffc107", - "settings": { - "columnWidth": "0px", - "useCellStyleFunction": false, - "useCellContentFunction": false - }, - "_hash": 0.3504240371585048, - "postFuncBody": "if(value) {\n return value.substring(0, 31) + \"...\";\n} else {\n return '';\n}" - }, - { - "name": "RemoteLoggingLevel", - "type": "attribute", - "label": "Log level", - "color": "#607d8b", - "settings": { - "columnWidth": "0px", - "useCellStyleFunction": false, - "useCellContentFunction": false - }, - "_hash": 0.9785994222542516 - } - ], - "entityAliasId": "3e0f533a-0db1-3292-184f-06e73535061a" - } - ], - "showTitleIcon": true, - "titleIcon": "list", - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "List device", - "widgetStyle": {}, - "displayTimewindow": true, - "actions": { - "headerButton": [ - { - "name": "Add device", - "icon": "add", - "type": "customPretty", - "customHtml": "\n \n

Add device

\n \n \n
\n \n \n
\n
\n
\n \n Device name\n \n \n Device name is required.\n \n \n
\n \n Latitude\n \n \n \n Longitude\n \n \n
\n \n Label\n \n \n
\n
\n
\n \n \n \n
\n\n", - "customCss": "", - "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\nlet attributeService = $injector.get(widgetContext.servicesMap.get('attributeService'));\n\nopenAddDeviceDialog();\n\nfunction openAddDeviceDialog() {\n customDialog.customDialog(htmlTemplate, AddDeviceDialogController).subscribe();\n}\n\nfunction AddDeviceDialogController(instance) {\n let vm = instance;\n \n vm.addDeviceFormGroup = vm.fb.group({\n deviceName: ['', [vm.validators.required]],\n deviceLabel: [''],\n attributes: vm.fb.group({\n latitude: [null],\n longitude: [null]\n }) \n });\n \n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n \n vm.save = function() {\n vm.addDeviceFormGroup.markAsPristine();\n let device = {\n additionalInfo: {gateway: true},\n name: vm.addDeviceFormGroup.get('deviceName').value,\n type: 'gateway',\n label: vm.addDeviceFormGroup.get('deviceLabel').value\n };\n deviceService.saveDevice(device).subscribe(\n function (device) {\n saveAttributes(device.id).subscribe(\n function () {\n widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n }\n );\n };\n \n function saveAttributes(entityId) {\n let attributes = vm.addDeviceFormGroup.get('attributes').value;\n let attributesArray = [];\n for (let key in attributes) {\n attributesArray.push({key: key, value: attributes[key]});\n }\n if (attributesArray.length > 0) {\n return attributeService.saveEntityAttributes(entityId, \"SERVER_SCOPE\", attributesArray);\n } else {\n return widgetContext.rxjs.of([]);\n }\n }\n}\n", - "customResources": [], - "id": "70837a9d-c3de-a9a7-03c5-dccd14998758" - } - ], - "actionCellButton": [ - { - "id": "78845501-234e-a452-6819-82b5b776e99f", - "name": "Configuration", - "icon": "settings", - "type": "openDashboardState", - "targetDashboardStateId": "__entityname__config", - "openRightLayout": false, - "setEntityId": true - }, - { - "id": "f6ffdba8-e40f-2b8d-851b-f5ecaf18606b", - "name": "Graphs", - "icon": "show_chart", - "type": "openDashboardState", - "targetDashboardStateId": "__entityname_grafic", - "setEntityId": true - }, - { - "name": "Edit device", - "icon": "edit", - "type": "customPretty", - "customHtml": "
\n \n

Edit device

\n \n \n
\n \n \n
\n
\n
\n \n Device name\n \n \n Device name is required.\n \n \n
\n \n Latitude\n \n \n \n Longitude\n \n \n
\n \n Label\n \n \n
\n
\n
\n \n \n \n
\n
\n", - "customCss": "/*=======================================================================*/\n/*========== There are two examples: for edit and add entity ==========*/\n/*=======================================================================*/\n/*======================== Edit entity example ========================*/\n/*=======================================================================*/\n/*\n.edit-entity-form md-input-container {\n padding-right: 10px;\n}\n\n.edit-entity-form .boolean-value-input {\n padding-left: 5px;\n}\n\n.edit-entity-form .boolean-value-input .checkbox-label {\n margin-bottom: 8px;\n color: rgba(0,0,0,0.54);\n font-size: 12px;\n}\n\n.relations-list .header {\n padding-right: 5px;\n padding-bottom: 5px;\n padding-left: 5px;\n}\n\n.relations-list .header .cell {\n padding-right: 5px;\n padding-left: 5px;\n font-size: 12px;\n font-weight: 700;\n color: rgba(0, 0, 0, .54);\n white-space: nowrap;\n}\n\n.relations-list .body {\n padding-right: 5px;\n padding-bottom: 15px;\n padding-left: 5px;\n}\n\n.relations-list .body .row {\n padding-top: 5px;\n}\n\n.relations-list .body .cell {\n padding-right: 5px;\n padding-left: 5px;\n}\n\n.relations-list .body md-autocomplete-wrap md-input-container {\n height: 30px;\n}\n\n.relations-list .body .md-button {\n margin: 0;\n}\n\n.relations-list.old-relations tb-entity-select tb-entity-autocomplete button {\n display: none;\n} \n*/\n/*========================================================================*/\n/*========================= Add entity example =========================*/\n/*========================================================================*/\n/*\n.add-entity-form md-input-container {\n padding-right: 10px;\n}\n\n.add-entity-form .boolean-value-input {\n padding-left: 5px;\n}\n\n.add-entity-form .boolean-value-input .checkbox-label {\n margin-bottom: 8px;\n color: rgba(0,0,0,0.54);\n font-size: 12px;\n}\n\n.relations-list .header {\n padding-right: 5px;\n padding-bottom: 5px;\n padding-left: 5px;\n}\n\n.relations-list .header .cell {\n padding-right: 5px;\n padding-left: 5px;\n font-size: 12px;\n font-weight: 700;\n color: rgba(0, 0, 0, .54);\n white-space: nowrap;\n}\n\n.relations-list .body {\n padding-right: 5px;\n padding-bottom: 15px;\n padding-left: 5px;\n}\n\n.relations-list .body .row {\n padding-top: 5px;\n}\n\n.relations-list .body .cell {\n padding-right: 5px;\n padding-left: 5px;\n}\n\n.relations-list .body md-autocomplete-wrap md-input-container {\n height: 30px;\n}\n\n.relations-list .body .md-button {\n margin: 0;\n}\n*/\n", - "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\nlet attributeService = $injector.get(widgetContext.servicesMap.get('attributeService'));\n\nopenEditDeviceDialog();\n\nfunction openEditDeviceDialog() {\n customDialog.customDialog(htmlTemplate, EditDeviceDialogController).subscribe();\n}\n\nfunction EditDeviceDialogController(instance) {\n let vm = instance;\n \n vm.device = null;\n vm.attributes = {};\n \n vm.editDeviceFormGroup = vm.fb.group({\n deviceName: ['', [vm.validators.required]],\n deviceLabel: [''],\n attributes: vm.fb.group({\n latitude: [null],\n longitude: [null]\n }) \n });\n \n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n \n vm.save = function() {\n vm.editDeviceFormGroup.markAsPristine();\n vm.device.name = vm.editDeviceFormGroup.get('deviceName').value;\n vm.device.label = vm.editDeviceFormGroup.get('deviceLabel').value;\n deviceService.saveDevice(vm.device).subscribe(\n function () {\n saveAttributes().subscribe(\n function () {\n widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n }\n );\n };\n \n getEntityInfo();\n \n function getEntityInfo() {\n deviceService.getDevice(entityId.id).subscribe(\n function (device) {\n attributeService.getEntityAttributes(entityId, 'SERVER_SCOPE',\n ['latitude', 'longitude']).subscribe(\n function (attributes) {\n for (let i = 0; i < attributes.length; i++) {\n vm.attributes[attributes[i].key] = attributes[i].value; \n }\n vm.device = device;\n vm.editDeviceFormGroup.patchValue(\n {\n deviceName: vm.device.name,\n deviceLabel: vm.device.label,\n attributes: {\n latitude: vm.attributes.latitude,\n longitude: vm.attributes.longitude\n }\n }, {emitEvent: false}\n );\n } \n );\n }\n ); \n }\n \n function saveAttributes() {\n let attributes = vm.editDeviceFormGroup.get('attributes').value;\n let attributesArray = [];\n for (let key in attributes) {\n attributesArray.push({key: key, value: attributes[key]});\n }\n if (attributesArray.length > 0) {\n return attributeService.saveEntityAttributes(entityId, 'SERVER_SCOPE', attributesArray);\n } else {\n return widgetContext.rxjs.of([]);\n }\n }\n}\n", - "customResources": [], - "id": "242671f3-76c6-6982-7acc-6f12addf0ccc" - }, - { - "name": "Delete device", - "icon": "delete", - "type": "custom", - "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet dialogs = $injector.get(widgetContext.servicesMap.get('dialogs'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenDeleteDeviceDialog();\n\nfunction openDeleteDeviceDialog() {\n let title = \"Are you sure you want to delete the device \" + entityName + \"?\";\n let content = \"Be careful, after the confirmation, the device and all related data will become unrecoverable!\";\n dialogs.confirm(title, content, 'Cancel', 'Delete').subscribe(\n function (result) {\n if (result) {\n deleteDevice();\n }\n }\n );\n}\n\nfunction deleteDevice() {\n deviceService.deleteDevice(entityId.id).subscribe(\n function () {\n widgetContext.updateAliases();\n }\n );\n}\n", - "id": "862ec2b7-fbcf-376e-f85f-b77c07f36efa" - } - ], - "rowClick": [ - { - "id": "ad5fc7e1-5e60-e056-6940-a75a383466a1", - "name": "to_entityname__config", - "icon": "more_horiz", - "type": "openDashboardState", - "targetDashboardStateId": "__entityname__config", - "setEntityId": true, - "stateEntityParamName": "" - } - ] - } - }, - "id": "94715984-ae74-76e4-20b7-2f956b01ed80", - "typeFullFqn": "system.entity_admin_widgets.device_admin_table" - }, - "eadabbc7-519e-76fc-ba10-b3fe8c18da10": { - "type": "timeseries", - "sizeX": 14, - "sizeY": 13, - "config": { - "datasources": [ - { - "type": "entity", - "dataKeys": [ - { - "name": "LOGS", - "type": "timeseries", - "label": "LOGS", - "color": "#2196f3", - "settings": { - "useCellStyleFunction": false, - "useCellContentFunction": false - }, - "_hash": 0.3496649158709739, - "postFuncBody": "return value.replace(/ - (.*) - \\[/gi, ' - $1 - [');" - } - ], - "entityAliasId": "b2487e75-2fa4-f211-142c-434dfd50c70c" - } - ], - "timewindow": { - "realtime": { - "interval": 1000, - "timewindowMs": 2592000000 - }, - "aggregation": { - "type": "NONE", - "limit": 200 - } - }, - "showTitle": true, - "backgroundColor": "rgb(255, 255, 255)", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "showTimestamp": true, - "displayPagination": true, - "defaultPageSize": 10 - }, - "title": "Debug events (logs)", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "useDashboardTimewindow": false, - "showLegend": false, - "widgetStyle": {}, - "actions": {}, - "showTitleIcon": false, - "titleIcon": null, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "displayTimewindow": true - }, - "id": "eadabbc7-519e-76fc-ba10-b3fe8c18da10", - "typeFullFqn": "system.cards.timeseries_table" - }, - "f928afc4-30d1-8d0c-e3cf-777f9f9d1155": { - "type": "timeseries", - "sizeX": 17, - "sizeY": 4, - "config": { - "datasources": [ - { - "type": "entity", - "dataKeys": [ - { - "name": "opcuaEventsProduced", - "type": "timeseries", - "label": "opcuaEventsProduced", - "color": "#2196f3", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": true, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "tooltipValueFormatter": "", - "showSeparateAxis": false, - "axisTitle": "", - "axisPosition": "left", - "axisTicksFormatter": "", - "comparisonSettings": { - "showValuesForComparison": true, - "comparisonValuesLabel": "", - "color": "" - } - }, - "_hash": 0.1477920581839779 - }, - { - "name": "opcuaEventsSent", - "type": "timeseries", - "label": "opcuaEventsSent", - "color": "#4caf50", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": true, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "tooltipValueFormatter": "", - "showSeparateAxis": false, - "axisTitle": "", - "axisPosition": "left", - "axisTicksFormatter": "", - "comparisonSettings": { - "showValuesForComparison": true, - "comparisonValuesLabel": "", - "color": "" - } - }, - "_hash": 0.6500957113784758 - } - ], - "entityAliasId": "b2487e75-2fa4-f211-142c-434dfd50c70c" - } - ], - "timewindow": { - "realtime": { - "interval": 1000, - "timewindowMs": 120000 - }, - "aggregation": { - "type": "NONE", - "limit": 25000 - }, - "hideInterval": false, - "hideAggregation": false - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454" - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - }, - "showLegend": true, - "legendConfig": { - "direction": "column", - "position": "right", - "showMin": true, - "showMax": true, - "showAvg": true, - "showTotal": true - } - }, - "title": "Real time information", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "mobileHeight": null, - "showTitleIcon": false, - "titleIcon": null, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "widgetStyle": {}, - "useDashboardTimewindow": false, - "displayTimewindow": true, - "actions": {} - }, - "id": "f928afc4-30d1-8d0c-e3cf-777f9f9d1155", - "typeFullFqn": "system.charts.basic_timeseries" - }, - "2a95b473-042d-59d0-2da2-40d0cccb6c8a": { - "type": "timeseries", - "sizeX": 7, - "sizeY": 7, - "config": { - "datasources": [ - { - "type": "entity", - "dataKeys": [ - { - "name": "eventsSent", - "type": "timeseries", - "label": "Events", - "color": "#2196f3", - "settings": { - "useCellStyleFunction": false, - "useCellContentFunction": false - }, - "_hash": 0.8156044798125357 - }, - { - "name": "eventsProduced", - "type": "timeseries", - "label": "Produced", - "color": "#4caf50", - "settings": { - "useCellStyleFunction": false, - "useCellContentFunction": false - }, - "_hash": 0.6538259344015449 - } - ], - "entityAliasId": "b2487e75-2fa4-f211-142c-434dfd50c70c" - } - ], - "timewindow": { - "realtime": { - "interval": 1000, - "timewindowMs": 604800000 - }, - "aggregation": { - "type": "NONE", - "limit": 200 - } - }, - "showTitle": true, - "backgroundColor": "rgb(255, 255, 255)", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "showTimestamp": true, - "displayPagination": true, - "defaultPageSize": 6, - "hideEmptyLines": true - }, - "title": "Total Messages", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "useDashboardTimewindow": false, - "showLegend": false, - "widgetStyle": {}, - "actions": {}, - "showTitleIcon": false, - "titleIcon": null, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "displayTimewindow": true, - "legendConfig": { - "direction": "column", - "position": "bottom", - "showMin": false, - "showMax": false, - "showAvg": true, - "showTotal": false - } - }, - "id": "2a95b473-042d-59d0-2da2-40d0cccb6c8a", - "typeFullFqn": "system.cards.timeseries_table" - }, - "aaa69366-aacc-9028-65aa-645c0f8533ec": { - "type": "timeseries", - "sizeX": 17, - "sizeY": 4, - "config": { - "datasources": [ - { - "type": "entity", - "dataKeys": [ - { - "name": "eventsSent", - "type": "timeseries", - "label": "eventsSent", - "color": "#2196f3", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": true, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "tooltipValueFormatter": "", - "showSeparateAxis": false, - "axisTitle": "", - "axisPosition": "left", - "axisTicksFormatter": "", - "comparisonSettings": { - "showValuesForComparison": true, - "comparisonValuesLabel": "", - "color": "" - } - }, - "_hash": 0.41414001784591314 - }, - { - "name": "eventsProduced", - "type": "timeseries", - "label": "eventsProduced", - "color": "#4caf50", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": true, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "tooltipValueFormatter": "", - "showSeparateAxis": false, - "axisTitle": "", - "axisPosition": "left", - "axisTicksFormatter": "", - "comparisonSettings": { - "showValuesForComparison": true, - "comparisonValuesLabel": "", - "color": "" - } - }, - "_hash": 0.7819101846284422 - } - ], - "entityAliasId": "b2487e75-2fa4-f211-142c-434dfd50c70c" - } - ], - "timewindow": { - "realtime": { - "timewindowMs": 60000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454" - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - }, - "showLegend": true, - "legendConfig": { - "direction": "column", - "position": "right", - "showMin": true, - "showMax": true, - "showAvg": true, - "showTotal": true - } - }, - "title": "History information", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "mobileHeight": null, - "showTitleIcon": false, - "titleIcon": null, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "widgetStyle": {}, - "useDashboardTimewindow": true, - "displayTimewindow": true, - "actions": {} - }, - "id": "aaa69366-aacc-9028-65aa-645c0f8533ec", - "typeFullFqn": "system.charts.basic_timeseries" - }, - "ce5c7d01-a3ef-5cf0-4578-8505135c23a0": { - "type": "timeseries", - "sizeX": 17, - "sizeY": 4, - "config": { - "datasources": [ - { - "type": "entity", - "dataKeys": [ - { - "name": "bleEventsProduced", - "type": "timeseries", - "label": "bleEventsProduced", - "color": "#2196f3", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": true, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "tooltipValueFormatter": "", - "showSeparateAxis": false, - "axisTitle": "", - "axisPosition": "left", - "axisTicksFormatter": "", - "comparisonSettings": { - "showValuesForComparison": true, - "comparisonValuesLabel": "", - "color": "" - } - }, - "_hash": 0.5625165504526104 - }, - { - "name": "bleEventsSent", - "type": "timeseries", - "label": "bleEventsSent", - "color": "#4caf50", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": true, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "tooltipValueFormatter": "", - "showSeparateAxis": false, - "axisTitle": "", - "axisPosition": "left", - "axisTicksFormatter": "", - "comparisonSettings": { - "showValuesForComparison": true, - "comparisonValuesLabel": "", - "color": "" - } - }, - "_hash": 0.6817950080745288 - } - ], - "entityAliasId": "b2487e75-2fa4-f211-142c-434dfd50c70c" - } - ], - "timewindow": { - "realtime": { - "interval": 5000, - "timewindowMs": 120000 - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454" - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - }, - "showLegend": true, - "legendConfig": { - "direction": "column", - "position": "right", - "showMin": true, - "showMax": true, - "showAvg": true, - "showTotal": true - } - }, - "title": "Real time information", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "mobileHeight": null, - "showTitleIcon": false, - "titleIcon": null, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "widgetStyle": {}, - "useDashboardTimewindow": false, - "displayTimewindow": true, - "actions": {} - }, - "id": "ce5c7d01-a3ef-5cf0-4578-8505135c23a0", - "typeFullFqn": "system.charts.basic_timeseries" - }, - "466f046d-6005-a168-b107-60fcb2469cd5": { - "type": "latest", - "sizeX": 7, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "dataKeys": [], - "entityAliasId": "b2487e75-2fa4-f211-142c-434dfd50c70c" - } - ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY" - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1694085270425, - "endTimeMs": 1694171670425 - }, - "quickInterval": "CURRENT_DAY" - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "eventsTitle": "Gateway Events Form", - "eventsReg": [ - "EventsProduced", - "EventsSent" - ] - }, - "title": "Gateway events", - "showTitleIcon": false, - "titleIcon": null, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "dropShadow": true, - "enableFullscreen": true, - "widgetStyle": {}, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "useDashboardTimewindow": true, - "displayTimewindow": true, - "showLegend": false, - "actions": {} - }, - "id": "466f046d-6005-a168-b107-60fcb2469cd5", - "typeFullFqn": "system.gateway_widgets.attributes_card" - }, - "8fc32225-164f-3258-73f7-e6b6d959cf0b": { - "type": "latest", - "sizeX": 10, - "sizeY": 9, - "config": { - "datasources": [ - { - "type": "entity", - "dataKeys": [], - "entityAliasId": "b2487e75-2fa4-f211-142c-434dfd50c70c" - } - ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY" - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1694085270425, - "endTimeMs": 1694171670425 - }, - "quickInterval": "CURRENT_DAY" - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "gatewayTitle": "Gateway configuration (Single device)", - "readOnly": false - }, - "title": "New Gateway configuration (Single device)", - "showTitleIcon": false, - "titleIcon": null, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "dropShadow": true, - "enableFullscreen": true, - "widgetStyle": {}, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "useDashboardTimewindow": true, - "displayTimewindow": true, - "showLegend": false, - "actions": {} - }, - "id": "8fc32225-164f-3258-73f7-e6b6d959cf0b", - "typeFullFqn": "system.gateway_widgets.config_form_latest" - }, - "063fc179-c9fd-f952-e714-f24e9c43c05c": { - "type": "rpc", - "sizeX": 4, - "sizeY": 2, - "config": { - "targetDeviceAliases": [], - "showTitle": false, - "backgroundColor": "#e6e7e8", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "0px", - "settings": { - "requestTimeout": 5000, - "oneWayElseTwoWay": true, - "styleButton": { - "isRaised": true, - "isPrimary": false - }, - "methodParams": "{}", - "methodName": "gateway_reboot", - "buttonText": "GATEWAY REBOOT" - }, - "title": "New RPC Button", - "dropShadow": true, - "enableFullscreen": false, - "widgetStyle": {}, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "useDashboardTimewindow": true, - "showLegend": false, - "actions": {}, - "datasources": [], - "showTitleIcon": false, - "titleIcon": null, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "displayTimewindow": true, - "targetDeviceAliasIds": [ - "b2487e75-2fa4-f211-142c-434dfd50c70c" - ] - }, - "id": "063fc179-c9fd-f952-e714-f24e9c43c05c", - "typeFullFqn": "system.control_widgets.rpcbutton" - }, - "3c2134cc-27a0-93e1-dbe1-2fa7c1ce16b7": { - "type": "rpc", - "sizeX": 4, - "sizeY": 2, - "config": { - "targetDeviceAliases": [], - "showTitle": false, - "backgroundColor": "#e6e7e8", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "0px", - "settings": { - "requestTimeout": 5000, - "oneWayElseTwoWay": true, - "styleButton": { - "isRaised": true, - "isPrimary": false - }, - "methodName": "gateway_restart", - "methodParams": "{}", - "buttonText": "GATEWAY RESTART" - }, - "title": "New RPC Button", - "dropShadow": true, - "enableFullscreen": false, - "widgetStyle": {}, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "useDashboardTimewindow": true, - "showLegend": false, - "actions": {}, - "datasources": [], - "showTitleIcon": false, - "titleIcon": null, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "displayTimewindow": true, - "targetDeviceAliasIds": [ - "b2487e75-2fa4-f211-142c-434dfd50c70c" - ] - }, - "id": "3c2134cc-27a0-93e1-dbe1-2fa7c1ce16b7", - "typeFullFqn": "system.control_widgets.rpcbutton" - }, - "6770b6ba-eff8-df05-75f8-c1f9326d4842": { - "type": "latest", - "sizeX": 6, - "sizeY": 4, - "config": { - "datasources": [ - { - "type": "entity", - "dataKeys": [ - { - "name": "latitude", - "type": "attribute", - "label": "latitude", - "color": "#2196f3", - "settings": {}, - "_hash": 0.9743324774725604 - }, - { - "name": "longitude", - "type": "attribute", - "label": "longitude", - "color": "#4caf50", - "settings": {}, - "_hash": 0.5530093635101525 - } - ], - "entityAliasId": "b2487e75-2fa4-f211-142c-434dfd50c70c" - } - ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY" - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1694085270425, - "endTimeMs": 1694171670425 - }, - "quickInterval": "CURRENT_DAY" - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "fitMapBounds": true, - "latKeyName": "latitude", - "lngKeyName": "longitude", - "showLabel": true, - "label": "${entityName}", - "tooltipPattern": "${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}

Delete", - "markerImageSize": 34, - "useColorFunction": false, - "markerImages": [], - "useMarkerImageFunction": false, - "color": "#fe7569", - "mapProvider": "OpenStreetMap.Mapnik", - "showTooltip": true, - "autocloseTooltip": true, - "defaultCenterPosition": [ - 0, - 0 - ], - "customProviderTileUrl": "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", - "showTooltipAction": "click", - "polygonKeyName": "coordinates", - "polygonOpacity": 0.5, - "polygonStrokeOpacity": 1, - "polygonStrokeWeight": 1, - "zoomOnClick": true, - "showCoverageOnHover": true, - "animate": true, - "maxClusterRadius": 80, - "removeOutsideVisibleBounds": true, - "defaultZoomLevel": 5 - }, - "title": "Gateway Location", - "dropShadow": true, - "enableFullscreen": false, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "useDashboardTimewindow": true, - "showLegend": false, - "widgetStyle": {}, - "actions": { - "tooltipAction": [ - { - "id": "54c293c4-9ca6-e34f-dc6a-0271944c1c66", - "name": "delete", - "icon": "more_horiz", - "type": "custom", - "customFunction": "var $rootScope = widgetContext.$scope.$injector.get('$rootScope');\nvar entityDatasource = widgetContext.map.subscription.datasources.filter(\n function(entity) {\n return entity.entityId === entityId.id\n });\n\nwidgetContext.map.saveMarkerLocation(entityDatasource[0],\n widgetContext.map.locations[0], {\n \"lat\": null,\n \"lng\": null\n }).then(function succes() {\n $rootScope.$broadcast('widgetForceReInit');\n });" - } - ] - }, - "showTitleIcon": false, - "titleIcon": null, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "displayTimewindow": true - }, - "id": "6770b6ba-eff8-df05-75f8-c1f9326d4842", - "typeFullFqn": "system.input_widgets.markers_placement_openstreetmap" - } - }, - "states": { - "main_gateway": { - "name": "Gateways", - "root": true, - "layouts": { - "main": { - "widgets": { - "94715984-ae74-76e4-20b7-2f956b01ed80": { - "sizeX": 24, - "sizeY": 12, - "row": 0, - "col": 0 - } - }, - "gridSettings": { - "backgroundColor": "#eeeeee", - "color": "rgba(0,0,0,0.870588)", - "columns": 24, - "backgroundSizeMode": "100%", - "autoFillHeight": true, - "mobileAutoFillHeight": false, - "mobileRowHeight": 70, - "margin": 10, - "outerMargin": true - } - } - } - }, - "__entityname__config": { - "name": "${entityName} Configuration", - "root": false, - "layouts": { - "main": { - "widgets": { - "eadabbc7-519e-76fc-ba10-b3fe8c18da10": { - "sizeX": 14, - "sizeY": 13, - "row": 0, - "col": 10 - }, - "8fc32225-164f-3258-73f7-e6b6d959cf0b": { - "sizeX": 10, - "sizeY": 9, - "row": 0, - "col": 0 - }, - "063fc179-c9fd-f952-e714-f24e9c43c05c": { - "sizeX": 4, - "sizeY": 2, - "row": 9, - "col": 0 - }, - "3c2134cc-27a0-93e1-dbe1-2fa7c1ce16b7": { - "sizeX": 4, - "sizeY": 2, - "row": 11, - "col": 0 - }, - "6770b6ba-eff8-df05-75f8-c1f9326d4842": { - "sizeX": 6, - "sizeY": 4, - "row": 9, - "col": 4 - } - }, - "gridSettings": { - "backgroundColor": "#eeeeee", - "color": "rgba(0,0,0,0.870588)", - "columns": 24, - "backgroundSizeMode": "100%", - "autoFillHeight": true, - "mobileAutoFillHeight": false, - "mobileRowHeight": 70, - "margin": 10, - "outerMargin": true - } - } - } - }, - "__entityname_grafic": { - "name": "${entityName} Details", - "root": false, - "layouts": { - "main": { - "widgets": { - "f928afc4-30d1-8d0c-e3cf-777f9f9d1155": { - "sizeX": 17, - "sizeY": 4, - "mobileHeight": null, - "row": 4, - "col": 7 - }, - "2a95b473-042d-59d0-2da2-40d0cccb6c8a": { - "sizeX": 7, - "sizeY": 7, - "row": 5, - "col": 0 - }, - "aaa69366-aacc-9028-65aa-645c0f8533ec": { - "sizeX": 17, - "sizeY": 4, - "mobileHeight": null, - "row": 0, - "col": 7 - }, - "ce5c7d01-a3ef-5cf0-4578-8505135c23a0": { - "sizeX": 17, - "sizeY": 4, - "mobileHeight": null, - "row": 8, - "col": 7 - }, - "466f046d-6005-a168-b107-60fcb2469cd5": { - "sizeX": 7, - "sizeY": 5, - "row": 0, - "col": 0 - } - }, - "gridSettings": { - "backgroundColor": "#eeeeee", - "color": "rgba(0,0,0,0.870588)", - "columns": 24, - "backgroundSizeMode": "auto 100%", - "autoFillHeight": true, - "mobileAutoFillHeight": true, - "mobileRowHeight": 70, - "margin": 10, - "outerMargin": true - } - } - } - } - }, - "entityAliases": { - "3e0f533a-0db1-3292-184f-06e73535061a": { - "id": "3e0f533a-0db1-3292-184f-06e73535061a", - "alias": "Gateways", - "filter": { - "type": "deviceType", - "resolveMultiple": true, - "deviceNameFilter": "", - "deviceTypes": [ - "gateway" - ] - } - }, - "b2487e75-2fa4-f211-142c-434dfd50c70c": { - "id": "b2487e75-2fa4-f211-142c-434dfd50c70c", - "alias": "Current Gateway", - "filter": { - "type": "stateEntity", - "resolveMultiple": false, - "stateEntityParamName": "", - "defaultStateEntity": null - } - } - }, - "timewindow": { - "realtime": { - "interval": 1000, - "timewindowMs": 86400000 - }, - "aggregation": { - "type": "NONE", - "limit": 25000 - }, - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false - }, - "settings": { - "stateControllerId": "entity", - "showTitle": true, - "showDashboardsSelect": true, - "showEntitiesSelect": true, - "showDashboardTimewindow": true, - "showDashboardExport": true, - "toolbarAlwaysOpen": true, - "titleColor": "rgba(0,0,0,0.870588)" - }, - "filters": {} - }, - "externalId": null, - "name": "Gateways" -} \ No newline at end of file diff --git a/application/src/main/data/json/demo/dashboards/gateway.json b/application/src/main/data/json/tenant/dashboards/gateways.json similarity index 99% rename from application/src/main/data/json/demo/dashboards/gateway.json rename to application/src/main/data/json/tenant/dashboards/gateways.json index b35b480ef2..972aed299f 100644 --- a/application/src/main/data/json/demo/dashboards/gateway.json +++ b/application/src/main/data/json/tenant/dashboards/gateways.json @@ -1,5 +1,5 @@ { - "title": "Gateway", + "title": "ThingsBoard IoT Gateways", "image": null, "mobileHide": false, "mobileOrder": null, @@ -40,7 +40,7 @@ "color": "rgba(0, 0, 0, 0.87)", "padding": "4px", "settings": { - "entitiesTitle": "Gateway list", + "entitiesTitle": "Gateways list", "enableSearch": true, "enableSelectColumnDisplay": false, "enableStickyHeader": true, @@ -55,7 +55,7 @@ "defaultSortOrder": "entityName", "useRowStyleFunction": false }, - "title": "New Entities table", + "title": "Gateways list", "dropShadow": true, "enableFullscreen": false, "titleStyle": { @@ -571,11 +571,11 @@ "padding": "8px", "settings": { "useMarkdownTextFunction": true, - "markdownTextFunction": "var blockData = '';\nvar connectorsIndex = ctx.actionsApi.getActionDescriptors('elementClick').findIndex(action=>action.name==\"Connecotrs\");\nvar logsIndex = ctx.actionsApi.getActionDescriptors('elementClick').findIndex(action=>action.name==\"Logs\");\nfunction generateMatHeader(index) {\n if( index !== undefined && index > -1) {\n return ``\n } else {\n return \"\" \n }\n}\nfunction createDataBlock(value, label, dividerStyle, mobile, index) {\n blockData += `\n \n
\n \n ${generateMatHeader(index)}\n ${label}\n
\n ${value}\n `;\n}\ncreateDataBlock(data[0].Status, \"Status\", data[0].Status === \"Active\"? 'divider-green' : 'divider-red');\ncreateDataBlock(data[0].Name, \"Gateway Name\", '', ctx.isMobile);\ncreateDataBlock(data[0].Type, \"Gateway Type\", '');\ncreateDataBlock(\n `${(data[1]?data[1].count:0)} `\n + \" | \" + \n `${(data[2]?data[2][\"count 2\"]:0)} `\n , \"Devices (Active | Inactive)\", '');\ncreateDataBlock(\n `${(data[0].active_connectors?JSON.parse(data[0].active_connectors).length:0)} `\n + \" | \" + \n `${(data[0].inactive_connectors?JSON.parse(data[0].inactive_connectors).length:0)} `\n , \"Connectors (Active | Inactive)\", '', '', connectorsIndex);\ncreateDataBlock(data[0].ALL_ERRORS_COUNT || 0, \"Errors\", (data[0].ALL_ERRORS_COUNT || 0) === 0 ? 'divider-green' : 'divider-red', '', logsIndex);\nreturn `
${blockData}
`;", + "markdownTextFunction": "var blockData = '';\nvar connectorsIndex = ctx.actionsApi.getActionDescriptors('elementClick').findIndex(action=>action.name==\"Connectors\");\nvar logsIndex = ctx.actionsApi.getActionDescriptors('elementClick').findIndex(action=>action.name==\"Logs\");\nfunction generateMatHeader(index) {\n if( index !== undefined && index > -1) {\n return ``\n } else {\n return \"\" \n }\n}\nfunction createDataBlock(value, label, dividerStyle, mobile, index) {\n blockData += `\n \n
\n \n ${generateMatHeader(index)}\n ${label}\n
\n ${value}\n `;\n}\ncreateDataBlock(data[0].Status, \"Status\", data[0].Status === \"Active\"? 'divider-green' : 'divider-red');\ncreateDataBlock(data[0].Name, \"Gateway Name\", '', ctx.isMobile);\ncreateDataBlock(data[0].Type, \"Gateway Type\", '');\ncreateDataBlock(\n `${(data[1]?data[1].count:0)} `\n + \" | \" + \n `${(data[2]?data[2][\"count 2\"]:0)} `\n , \"Devices (Active | Inactive)\", '');\ncreateDataBlock(\n `${(data[0].active_connectors?JSON.parse(data[0].active_connectors).length:0)} `\n + \" | \" + \n `${(data[0].inactive_connectors?JSON.parse(data[0].inactive_connectors).length:0)} `\n , \"Connectors (Active | Inactive)\", '', '', connectorsIndex);\ncreateDataBlock(data[0].ALL_ERRORS_COUNT || 0, \"Errors\", (data[0].ALL_ERRORS_COUNT || 0) === 0 ? 'divider-green' : 'divider-red', '', logsIndex);\nreturn `
${blockData}
`;", "applyDefaultMarkdownStyle": false, "markdownCss": ".divider {\n position: absolute;\n width: 3px;\n top: 8px;\n border-radius: 2px;\n bottom: 8px;\n border: 1px solid rgba(31, 70, 144, 1);\n background-color: rgba(31, 70, 144, 1);\n left: 10px;\n}\n.divider-green .divider {\n border: 1px solid rgb(25,128,56);\n background-color: rgb(25,128,56);\n}\n\n.divider-green .mat-mdc-card-content {\n color: rgb(25,128,56);\n}\n\n.divider-red .divider {\n border: 1px solid rgb(203,37,48);\n background-color: rgb(203,37,48);\n}\n\n.divider-red .mat-mdc-card-content {\n color: rgb(203,37,48);\n}\n\n.mdc-card {\n position: relative;\n padding-left: 10px;\n margin-bottom: 1px;\n}\n\n.mat-mdc-card-subtitle {\n font-weight: 400;\n font-size: 12px;\n}\n\n.mat-mdc-card-header {\n padding: 8px 16px 0;\n}\n\n.mat-mdc-card-content:last-child {\n padding-bottom: 8px;\n font-size: 16px;\n}\n\n.cards-container {\n height: calc(100% - 1px);\n justify-content: stretch;\n align-items: center;\n margin-bottom: 1px;\n}\n\n::ng-deep.tb-home-widget-link > div {\n flex-grow: 1;\n cursor: pointer;\n}\n\n .tb-home-widget-link {\n width: 100%;\n }\n\n .tb-home-widget-link:hover::after{\n color: inherit;\n }\n \n .tb-home-widget-link::after{\n content: 'arrow_forward';\n display: inline-block;\n transform: rotate(315deg);\n font-family: 'Material Icons';\n font-weight: normal;\n font-style: normal;\n font-size: 18px;\n color: rgba(0, 0, 0, 0.12);\n vertical-align: bottom;\n margin-left: 6px;\n}" }, - "title": "New Markdown/HTML Card", + "title": "Connectors", "showTitleIcon": false, "iconColor": "rgba(0, 0, 0, 0.87)", "iconSize": "24px", @@ -599,7 +599,7 @@ "actions": { "elementClick": [ { - "name": "Connecotrs", + "name": "Connectors", "icon": "more_horiz", "useShowWidgetActionFunction": null, "showWidgetActionFunction": "return true;", @@ -680,7 +680,7 @@ "defaultSortOrder": "-createdTime", "useRowStyleFunction": false }, - "title": "New Alarms table", + "title": "Alarms", "dropShadow": true, "enableFullscreen": false, "titleStyle": { @@ -1074,7 +1074,7 @@ } ] }, - "title": "New RPC remote shell", + "title": "RPC remote shell", "dropShadow": true, "enableFullscreen": true, "widgetStyle": { @@ -1485,7 +1485,7 @@ } ] }, - "title": "New RPC debug terminal", + "title": "RPC debug terminal", "dropShadow": true, "enableFullscreen": true, "widgetStyle": {}, @@ -1868,7 +1868,7 @@ "applyDefaultMarkdownStyle": false, "markdownCss": ".action-buttons-container {\r\n display: flex;\r\n flex-wrap: wrap;\r\n flex-direction: row;\r\n height: 100%;\r\n width: 100%;\r\n align-content: center;\r\n}\r\n\r\nbutton {\r\n flex-grow: 1;\r\n margin: 10px;\r\n min-width: 150px;\r\n height: auto;\r\n}" }, - "title": "New Markdown/HTML Card", + "title": "Service command", "showTitleIcon": false, "iconColor": "rgba(0, 0, 0, 0.87)", "iconSize": "24px", @@ -1980,7 +1980,7 @@ "applyDefaultMarkdownStyle": false, "markdownCss": ".action-buttons-container {\r\n display: flex;\r\n flex-wrap: wrap;\r\n flex-direction: row;\r\n height: 100%;\r\n width: 100%;\r\n align-content: start;\r\n}\r\n\r\nbutton {\r\n flex-grow: 1;\r\n margin: 10px;\r\n min-width: 150px;\r\n height: auto;\r\n}" }, - "title": "New Markdown/HTML Card", + "title": "General configuration", "showTitleIcon": false, "iconColor": "rgba(0, 0, 0, 0.87)", "iconSize": "24px", @@ -2145,7 +2145,7 @@ "applyDefaultMarkdownStyle": true, "markdownCss": ".mat-mdc-form-field-subscript-wrapper {\n display: none !important;\n}" }, - "title": "New Markdown/HTML Card", + "title": "Gateway devices", "showTitleIcon": false, "iconColor": "rgba(0, 0, 0, 0.87)", "iconSize": "24px", @@ -4939,7 +4939,7 @@ "applyDefaultMarkdownStyle": false, "markdownCss": ".action-container {\r\n display: flex;\r\n flex-wrap: wrap;\r\n flex-direction: row;\r\n height: 100%;\r\n width: 100%;\r\n}\r\n\r\nbutton {\r\n flex-grow: 1;\r\n margin: 10px;\r\n min-width: 150px;\r\n height: auto;\r\n}" }, - "title": "New Markdown/HTML Card", + "title": "Gateway commands", "showTitleIcon": false, "iconColor": "rgba(0, 0, 0, 0.87)", "iconSize": "24px", diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/DefaultTbTenantService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/DefaultTbTenantService.java index 19d4126989..3267ed1c17 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/DefaultTbTenantService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/DefaultTbTenantService.java @@ -54,6 +54,7 @@ public class DefaultTbTenantService extends AbstractTbEntityService implements T if (created) { installScripts.createDefaultRuleChains(savedTenant.getId()); installScripts.createDefaultEdgeRuleChains(savedTenant.getId()); + installScripts.createDefaultTenantDashboards(savedTenant.getId(), null); } tenantProfileCache.evict(savedTenant.getId()); notificationEntityService.notifyCreateOrUpdateTenant(savedTenant, created ? diff --git a/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java b/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java index 7f0b70cb27..96a6678789 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java +++ b/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java @@ -249,6 +249,15 @@ public class InstallScripts { public void loadDashboards(TenantId tenantId, CustomerId customerId) throws Exception { Path dashboardsDir = Paths.get(getDataDir(), JSON_DIR, DEMO_DIR, DASHBOARDS_DIR); + loadDashboardsFromDir(tenantId, customerId, dashboardsDir); + } + + public void createDefaultTenantDashboards(TenantId tenantId, CustomerId customerId) throws Exception { + Path dashboardsDir = Paths.get(getDataDir(), JSON_DIR, TENANT_DIR, DASHBOARDS_DIR); + loadDashboardsFromDir(tenantId, customerId, dashboardsDir); + } + + private void loadDashboardsFromDir(TenantId tenantId, CustomerId customerId, Path dashboardsDir) throws IOException { try (DirectoryStream dirStream = Files.newDirectoryStream(dashboardsDir, path -> path.toString().endsWith(JSON_EXT))) { dirStream.forEach( path -> { From 8dce7dcad239ddb470e9227eec6e4e0a85fef593 Mon Sep 17 00:00:00 2001 From: Dmitriymush Date: Fri, 10 Nov 2023 14:55:07 +0200 Subject: [PATCH 094/252] UI: liquid level widget added additional checkup for datasources --- .../liquid-level-widget.component.ts | 42 ++++++++++--------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/indicator/liquid-level-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/liquid-level-widget.component.ts index baf9f018c0..847937581b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/indicator/liquid-level-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/liquid-level-widget.component.ts @@ -160,26 +160,30 @@ export class LiquidLevelWidgetComponent implements OnInit { } private getData(): Observable<{ svg: string; volume: number; units: string }> { - const entityId: EntityId = { - entityType: this.ctx.datasources[0].entityType, - id: this.ctx.datasources[0].entityId - }; + if (this.ctx.datasources?.length) { + const entityId: EntityId = { + entityType: this.ctx.datasources[0].entityType, + id: this.ctx.datasources[0].entityId + }; + + return this.getShape(entityId).pipe( + switchMap(shape => { + this.shape = shape; + this.svgParams = svgMapping.get(shape); + if (this.svgParams) { + return forkJoin([ + this.resourcesService.loadJsonResource(this.svgParams.svg), + this.getTankersParams(entityId) + ]).pipe( + map(params => ({svg: params[0], ...params[1]})) + ); + } + return of(null); + }) + ); + } - return this.getShape(entityId).pipe( - switchMap(shape => { - this.shape = shape; - this.svgParams = svgMapping.get(shape); - if (this.svgParams) { - return forkJoin([ - this.resourcesService.loadJsonResource(this.svgParams.svg), - this.getTankersParams(entityId) - ]).pipe( - map(params => ({svg: params[0], ...params[1]})) - ); - } - return of(null); - }) - ); + return of(null); } public onInit() { From e6473c9c171255b17d9ef57cf6e85950e9892077 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Fri, 10 Nov 2023 15:40:56 +0200 Subject: [PATCH 095/252] fixed AssetProfileServiceImpl --- .../thingsboard/server/dao/asset/AssetProfileServiceImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileServiceImpl.java index 075975cd96..7cc251f9ec 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileServiceImpl.java @@ -110,8 +110,8 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService assetProfileDao.findByName(tenantId, profileName), false); + return cache.getOrFetchFromDB(AssetProfileCacheKey.fromName(tenantId, profileName), + () -> assetProfileDao.findByName(tenantId, profileName), false, putInCache); } @Override From 92178384eaaee61d726649b75af5c8746e391845 Mon Sep 17 00:00:00 2001 From: ShvaykaD Date: Fri, 10 Nov 2023 15:42:10 +0200 Subject: [PATCH 096/252] added TbNodeUpgradeUtils && fixed upgrade when config is null or NullNode --- .../update/DefaultDataUpdateService.java | 11 +- .../rule/DefaultTbRuleChainService.java | 11 +- .../server/utils/TbNodeUpgradeUtils.java | 39 ++++++ .../server/utils/TbNodeUpgradeUtilsTest.java | 111 ++++++++++++++++++ .../rule/engine/api/util/TbNodeUtils.java | 2 +- 5 files changed, 157 insertions(+), 17 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/utils/TbNodeUpgradeUtils.java create mode 100644 application/src/test/java/org/thingsboard/server/utils/TbNodeUpgradeUtilsTest.java diff --git a/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java b/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java index c49578c912..5375590084 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java @@ -27,7 +27,6 @@ import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.flow.TbRuleChainInputNode; import org.thingsboard.rule.engine.flow.TbRuleChainInputNodeConfiguration; import org.thingsboard.rule.engine.profile.TbDeviceProfileNode; @@ -67,7 +66,6 @@ import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.common.data.tenant.profile.TenantProfileQueueConfiguration; -import org.thingsboard.server.common.data.util.TbPair; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.alarm.AlarmDao; import org.thingsboard.server.dao.audit.AuditLogDao; @@ -90,6 +88,7 @@ import org.thingsboard.server.service.component.ComponentDiscoveryService; import org.thingsboard.server.service.component.RuleNodeClassInfo; import org.thingsboard.server.service.install.InstallScripts; import org.thingsboard.server.service.install.SystemDataLoaderService; +import org.thingsboard.server.utils.TbNodeUpgradeUtils; import java.util.ArrayList; import java.util.Collections; @@ -291,16 +290,12 @@ public class DefaultDataUpdateService implements DataUpdateService { continue; } var ruleNodeId = ruleNode.getId(); - var oldConfiguration = ruleNode.getConfiguration(); int fromVersion = ruleNode.getConfigurationVersion(); log.debug("Going to upgrade rule node with id: {} type: {} fromVersion: {} toVersion: {}", ruleNodeId, ruleNodeType, fromVersion, toVersion); try { - var tbVersionedNode = (TbNode) ruleNodeClassInfo.getClazz().getDeclaredConstructor().newInstance(); - TbPair upgradeRuleNodeConfigurationResult = tbVersionedNode.upgrade(fromVersion, oldConfiguration); - if (upgradeRuleNodeConfigurationResult.getFirst()) { - ruleNode.setConfiguration(upgradeRuleNodeConfigurationResult.getSecond()); - } + ruleNode.setConfiguration(TbNodeUpgradeUtils.upgradeRuleNodeConfiguration(ruleNode, ruleNodeClassInfo.getAnnotation(), + ruleNodeClassInfo.getClazz())); ruleNode.setConfigurationVersion(toVersion); saveFutures.add(jpaExecutorService.submit(() -> { ruleChainService.saveRuleNode(TenantId.SYS_TENANT_ID, ruleNode); diff --git a/application/src/main/java/org/thingsboard/server/service/rule/DefaultTbRuleChainService.java b/application/src/main/java/org/thingsboard/server/service/rule/DefaultTbRuleChainService.java index 56df2caa71..50f370fbb9 100644 --- a/application/src/main/java/org/thingsboard/server/service/rule/DefaultTbRuleChainService.java +++ b/application/src/main/java/org/thingsboard/server/service/rule/DefaultTbRuleChainService.java @@ -15,12 +15,10 @@ */ package org.thingsboard.server.service.rule; -import com.fasterxml.jackson.databind.JsonNode; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.flow.TbRuleChainInputNode; import org.thingsboard.rule.engine.flow.TbRuleChainInputNodeConfiguration; @@ -44,13 +42,13 @@ import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.data.rule.RuleChainUpdateResult; import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.common.data.rule.RuleNodeUpdateResult; -import org.thingsboard.server.common.data.util.TbPair; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.component.ComponentDiscoveryService; import org.thingsboard.server.service.entitiy.AbstractTbEntityService; import org.thingsboard.server.service.install.InstallScripts; +import org.thingsboard.server.utils.TbNodeUpgradeUtils; import java.util.ArrayList; import java.util.Collections; @@ -402,17 +400,14 @@ public class DefaultTbRuleChainService extends AbstractTbEntityService implement var ruleNodeClass = componentDiscoveryService.getRuleNodeInfo(ruleNodeType) .orElseThrow(() -> new RuntimeException("Rule node " + ruleNodeType + " is not supported!")); if (ruleNodeClass.isVersioned()) { - TbNode tbVersionedNode = (TbNode) ruleNodeClass.getClazz().getDeclaredConstructor().newInstance(); int fromVersion = node.getConfigurationVersion(); int toVersion = ruleNodeClass.getCurrentVersion(); if (fromVersion < toVersion) { log.debug("Going to upgrade rule node with id: {} type: {} fromVersion: {} toVersion: {}", ruleNodeId, ruleNodeType, fromVersion, toVersion); try { - TbPair upgradeResult = tbVersionedNode.upgrade(fromVersion, node.getConfiguration()); - if (upgradeResult.getFirst()) { - node.setConfiguration(upgradeResult.getSecond()); - } + node.setConfiguration(TbNodeUpgradeUtils.upgradeRuleNodeConfiguration(node, ruleNodeClass.getAnnotation(), + ruleNodeClass.getClazz())); node.setConfigurationVersion(toVersion); log.debug("Successfully upgrade rule node with id: {} type: {}, rule chain id: {} fromVersion: {} toVersion: {}", ruleNodeId, ruleNodeType, ruleChainId, fromVersion, toVersion); diff --git a/application/src/main/java/org/thingsboard/server/utils/TbNodeUpgradeUtils.java b/application/src/main/java/org/thingsboard/server/utils/TbNodeUpgradeUtils.java new file mode 100644 index 0000000000..98f5e713df --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/utils/TbNodeUpgradeUtils.java @@ -0,0 +1,39 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.utils; + +import com.fasterxml.jackson.databind.JsonNode; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.api.TbNode; +import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.common.data.util.TbPair; + +public class TbNodeUpgradeUtils { + + public static JsonNode upgradeRuleNodeConfiguration(RuleNode node, + org.thingsboard.rule.engine.api.RuleNode annotation, + Class nodeClass) throws Exception { + JsonNode oldConfiguration = node.getConfiguration(); + if (oldConfiguration == null || !oldConfiguration.isObject()) { + var configClass = annotation.configClazz(); + return JacksonUtil.valueToTree(configClass.getDeclaredConstructor().newInstance().defaultConfiguration()); + } + var tbVersionedNode = (TbNode) nodeClass.getDeclaredConstructor().newInstance(); + TbPair upgradeResult = tbVersionedNode.upgrade(node.getConfigurationVersion(), oldConfiguration); + return upgradeResult.getFirst() ? upgradeResult.getSecond() : oldConfiguration; + } + +} diff --git a/application/src/test/java/org/thingsboard/server/utils/TbNodeUpgradeUtilsTest.java b/application/src/test/java/org/thingsboard/server/utils/TbNodeUpgradeUtilsTest.java new file mode 100644 index 0000000000..1fb2ddcc25 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/utils/TbNodeUpgradeUtilsTest.java @@ -0,0 +1,111 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.utils; + +import com.fasterxml.jackson.databind.node.NullNode; +import org.assertj.core.api.Assertions; +import org.junit.Test; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.api.TbNode; +import org.thingsboard.rule.engine.metadata.TbGetAttributesNode; +import org.thingsboard.rule.engine.metadata.TbGetAttributesNodeConfiguration; +import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.common.data.util.TbPair; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class TbNodeUpgradeUtilsTest { + + @Test + public void testUpgradeRuleNodeConfigurationWithNullConfig() throws Exception { + // GIVEN + var node = mock(RuleNode.class); + var nodeClass = TbGetAttributesNode.class; + var nodeConfigClazz = TbGetAttributesNodeConfiguration.class; + + var annotation = mock(org.thingsboard.rule.engine.api.RuleNode.class); + + var defaultConfig = JacksonUtil.valueToTree(nodeConfigClazz.getDeclaredConstructor().newInstance().defaultConfiguration()); + + when(node.getConfiguration()).thenReturn(null); + when(node.getConfigurationVersion()).thenReturn(0); + when(annotation.configClazz()).thenReturn((Class) nodeConfigClazz); + // WHEN + var upgradedConfig = TbNodeUpgradeUtils.upgradeRuleNodeConfiguration(node, annotation, nodeClass); + + // THEN + Assertions.assertThat(upgradedConfig).isEqualTo(defaultConfig); + } + + @Test + public void testUpgradeRuleNodeConfigurationWithNullNodeConfig() throws Exception { + // GIVEN + var node = mock(RuleNode.class); + var nodeClass = TbGetAttributesNode.class; + var nodeConfigClazz = TbGetAttributesNodeConfiguration.class; + + var annotation = mock(org.thingsboard.rule.engine.api.RuleNode.class); + + var defaultConfig = JacksonUtil.valueToTree(nodeConfigClazz.getDeclaredConstructor().newInstance().defaultConfiguration()); + + when(node.getConfiguration()).thenReturn(NullNode.instance); + when(node.getConfigurationVersion()).thenReturn(0); + when(annotation.configClazz()).thenReturn((Class) nodeConfigClazz); + // WHEN + var upgradedConfig = TbNodeUpgradeUtils.upgradeRuleNodeConfiguration(node, annotation, nodeClass); + + // THEN + Assertions.assertThat(upgradedConfig).isEqualTo(defaultConfig); + } + + @Test + public void testUpgradeRuleNodeConfigurationWithNonNullConfig() throws Exception { + // GIVEN + var node = mock(RuleNode.class); + var nodeClass = TbGetAttributesNode.class; + var nodeConfigClazz = TbGetAttributesNodeConfiguration.class; + + var annotation = mock(org.thingsboard.rule.engine.api.RuleNode.class); + + String versionZeroDefaultConfigStr = "{\"fetchToData\":false," + + "\"clientAttributeNames\":[]," + + "\"sharedAttributeNames\":[]," + + "\"serverAttributeNames\":[]," + + "\"latestTsKeyNames\":[]," + + "\"tellFailureIfAbsent\":true," + + "\"getLatestValueWithTs\":false}"; + + var existingConfig = JacksonUtil.toJsonNode(versionZeroDefaultConfigStr); + int fromVersion = 0; + var currentDefaultConfig = JacksonUtil.valueToTree(nodeConfigClazz.getDeclaredConstructor().newInstance().defaultConfiguration()); + + when(node.getConfiguration()).thenReturn(existingConfig); + when(node.getConfigurationVersion()).thenReturn(fromVersion); + when(annotation.configClazz()).thenReturn((Class) nodeConfigClazz); + + TbNode tbVersionedNodeMock = mock(nodeClass); + + when(tbVersionedNodeMock.upgrade(fromVersion, existingConfig)).thenReturn(new TbPair<>(true, currentDefaultConfig)); + + // WHEN + var upgradedConfig = TbNodeUpgradeUtils.upgradeRuleNodeConfiguration(node, annotation, nodeClass); + + // THEN + Assertions.assertThat(upgradedConfig).isEqualTo(currentDefaultConfig); + } + +} diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/util/TbNodeUtils.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/util/TbNodeUtils.java index bbba1855c2..396d810b00 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/util/TbNodeUtils.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/util/TbNodeUtils.java @@ -15,7 +15,6 @@ */ package org.thingsboard.rule.engine.api.util; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import org.springframework.util.CollectionUtils; import org.thingsboard.common.util.JacksonUtil; @@ -85,6 +84,7 @@ public class TbNodeUtils { } } + @Deprecated(since = "3.6.1", forRemoval = true) public static List processPatterns(List patterns, TbMsgMetaData metaData) { if (!CollectionUtils.isEmpty(patterns)) { return patterns.stream().map(p -> processPattern(p, metaData)).collect(Collectors.toList()); From 28b35c9513d6a6bfc7b656bc72e3ffc2a70947b7 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Fri, 10 Nov 2023 15:57:21 +0200 Subject: [PATCH 097/252] Add backward compatibility support for widged types and tenant profile data --- .../server/service/edge/rpc/EdgeGrpcSession.java | 6 +++--- .../constructor/TenantProfileMsgConstructor.java | 8 ++++++-- .../rpc/constructor/WidgetTypeMsgConstructor.java | 11 +++++++++-- .../processor/relation/RelationEdgeProcessor.java | 4 ++-- .../rpc/processor/tenant/TenantEdgeProcessor.java | 5 +++-- .../tenant/TenantProfileEdgeProcessor.java | 5 +++-- .../processor/widget/WidgetTypeEdgeProcessor.java | 5 +++-- .../service/edge/rpc/utils/EdgeVersionUtils.java | 13 ++----------- .../thingsboard/server/edge/AbstractEdgeTest.java | 2 +- 9 files changed, 32 insertions(+), 27 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java index f9bd956706..7318b1eb24 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java @@ -641,7 +641,7 @@ public final class EdgeGrpcSession implements Closeable { case WIDGETS_BUNDLE: return ctx.getWidgetBundleProcessor().convertWidgetsBundleEventToDownlink(edgeEvent); case WIDGET_TYPE: - return ctx.getWidgetTypeProcessor().convertWidgetTypeEventToDownlink(edgeEvent); + return ctx.getWidgetTypeProcessor().convertWidgetTypeEventToDownlink(edgeEvent, this.edgeVersion); case ADMIN_SETTINGS: return ctx.getAdminSettingsProcessor().convertAdminSettingsEventToDownlink(edgeEvent); case OTA_PACKAGE: @@ -651,9 +651,9 @@ public final class EdgeGrpcSession implements Closeable { case QUEUE: return ctx.getQueueEdgeProcessor().convertQueueEventToDownlink(edgeEvent); case TENANT: - return ctx.getTenantEdgeProcessor().convertTenantEventToDownlink(edgeEvent); + return ctx.getTenantEdgeProcessor().convertTenantEventToDownlink(edgeEvent, this.getEdgeVersion()); case TENANT_PROFILE: - return ctx.getTenantProfileEdgeProcessor().convertTenantProfileEventToDownlink(edgeEvent); + return ctx.getTenantProfileEdgeProcessor().convertTenantProfileEventToDownlink(edgeEvent, this.getEdgeVersion()); default: log.warn("[{}] Unsupported edge event type [{}]", this.tenantId, edgeEvent); return null; diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/TenantProfileMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/TenantProfileMsgConstructor.java index 57655c3aeb..9fdb1bf069 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/TenantProfileMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/TenantProfileMsgConstructor.java @@ -19,10 +19,12 @@ import com.google.protobuf.ByteString; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.TenantProfile; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.DataDecodingEncodingService; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; @Component @TbCoreComponent @@ -31,7 +33,9 @@ public class TenantProfileMsgConstructor { @Autowired private DataDecodingEncodingService dataDecodingEncodingService; - public TenantProfileUpdateMsg constructTenantProfileUpdateMsg(UpdateMsgType msgType, TenantProfile tenantProfile) { + public TenantProfileUpdateMsg constructTenantProfileUpdateMsg(UpdateMsgType msgType, TenantProfile tenantProfile, EdgeVersion edgeVersion) { + ByteString profileData = EdgeVersionUtils.isEdgeVersionOlderThan(edgeVersion, EdgeVersion.V_3_6_1) ? + ByteString.empty() : ByteString.copyFrom(dataDecodingEncodingService.encode(tenantProfile.getProfileData())); TenantProfileUpdateMsg.Builder builder = TenantProfileUpdateMsg.newBuilder() .setMsgType(msgType) .setIdMSB(tenantProfile.getId().getId().getMostSignificantBits()) @@ -39,7 +43,7 @@ public class TenantProfileMsgConstructor { .setName(tenantProfile.getName()) .setDefault(tenantProfile.isDefault()) .setIsolatedRuleChain(tenantProfile.isIsolatedTbRuleEngine()) - .setProfileDataBytes(ByteString.copyFrom(dataDecodingEncodingService.encode(tenantProfile.getProfileData()))); + .setProfileDataBytes(profileData); if (tenantProfile.getDescription() != null) { builder.setDescription(tenantProfile.getDescription()); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetTypeMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetTypeMsgConstructor.java index e6f04261fb..17a66a47d4 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetTypeMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetTypeMsgConstructor.java @@ -20,9 +20,11 @@ import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.WidgetTypeId; import org.thingsboard.server.common.data.widget.WidgetTypeDetails; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.WidgetTypeUpdateMsg; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; import java.util.Arrays; @@ -30,7 +32,7 @@ import java.util.Arrays; @TbCoreComponent public class WidgetTypeMsgConstructor { - public WidgetTypeUpdateMsg constructWidgetTypeUpdateMsg(UpdateMsgType msgType, WidgetTypeDetails widgetTypeDetails) { + public WidgetTypeUpdateMsg constructWidgetTypeUpdateMsg(UpdateMsgType msgType, WidgetTypeDetails widgetTypeDetails, EdgeVersion edgeVersion) { WidgetTypeUpdateMsg.Builder builder = WidgetTypeUpdateMsg.newBuilder() .setMsgType(msgType) .setIdMSB(widgetTypeDetails.getId().getId().getMostSignificantBits()) @@ -58,7 +60,12 @@ public class WidgetTypeMsgConstructor { builder.setImage(widgetTypeDetails.getImage()); } if (widgetTypeDetails.getDescription() != null) { - builder.setDescription(widgetTypeDetails.getDescription()); + if (EdgeVersionUtils.isEdgeVersionOlderThan(edgeVersion, EdgeVersion.V_3_6_0) && + widgetTypeDetails.getDescription().length() > 255) { + builder.setDescription(widgetTypeDetails.getDescription().substring(0, 254)); + } else { + builder.setDescription(widgetTypeDetails.getDescription()); + } } builder.setDeprecated(widgetTypeDetails.isDeprecated()); if (widgetTypeDetails.getTags() != null) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessor.java index 487267e60f..b3676e8d82 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessor.java @@ -71,12 +71,12 @@ public class RelationEdgeProcessor extends BaseRelationProcessor { if (relation == null || (relation.getFrom().getEntityType().equals(EntityType.EDGE) || relation.getTo().getEntityType().equals(EntityType.EDGE))) { return Futures.immediateFuture(null); } - EdgeId sourceEdgeId = safeGetEdgeId(edgeNotificationMsg.getOriginatorEdgeIdMSB(), edgeNotificationMsg.getOriginatorEdgeIdLSB()); + EdgeId originatorEdgeId = safeGetEdgeId(edgeNotificationMsg.getOriginatorEdgeIdMSB(), edgeNotificationMsg.getOriginatorEdgeIdLSB()); Set uniqueEdgeIds = new HashSet<>(); uniqueEdgeIds.addAll(edgeService.findAllRelatedEdgeIds(tenantId, relation.getTo())); uniqueEdgeIds.addAll(edgeService.findAllRelatedEdgeIds(tenantId, relation.getFrom())); - uniqueEdgeIds.remove(sourceEdgeId); + uniqueEdgeIds.remove(originatorEdgeId); if (uniqueEdgeIds.isEmpty()) { return Futures.immediateFuture(null); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantEdgeProcessor.java index 9bd87660d2..9fd08b4447 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantEdgeProcessor.java @@ -24,6 +24,7 @@ import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg; import org.thingsboard.server.gen.edge.v1.TenantUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; @@ -35,7 +36,7 @@ import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; @TbCoreComponent public class TenantEdgeProcessor extends BaseEdgeProcessor { - public DownlinkMsg convertTenantEventToDownlink(EdgeEvent edgeEvent) { + public DownlinkMsg convertTenantEventToDownlink(EdgeEvent edgeEvent, EdgeVersion edgeVersion) { TenantId tenantId = new TenantId(edgeEvent.getEntityId()); DownlinkMsg downlinkMsg = null; if (EdgeEventActionType.UPDATED.equals(edgeEvent.getAction())) { @@ -44,7 +45,7 @@ public class TenantEdgeProcessor extends BaseEdgeProcessor { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); TenantUpdateMsg tenantUpdateMsg = tenantMsgConstructor.constructTenantUpdateMsg(msgType, tenant); TenantProfile tenantProfile = tenantProfileService.findTenantProfileById(tenantId, tenant.getTenantProfileId()); - TenantProfileUpdateMsg tenantProfileUpdateMsg = tenantProfileMsgConstructor.constructTenantProfileUpdateMsg(msgType, tenantProfile); + TenantProfileUpdateMsg tenantProfileUpdateMsg = tenantProfileMsgConstructor.constructTenantProfileUpdateMsg(msgType, tenantProfile, edgeVersion); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addTenantUpdateMsg(tenantUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantProfileEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantProfileEdgeProcessor.java index c9071571c4..970ea3a1f6 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantProfileEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantProfileEdgeProcessor.java @@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.id.TenantProfileId; import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; @@ -33,7 +34,7 @@ import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; @TbCoreComponent public class TenantProfileEdgeProcessor extends BaseEdgeProcessor { - public DownlinkMsg convertTenantProfileEventToDownlink(EdgeEvent edgeEvent) { + public DownlinkMsg convertTenantProfileEventToDownlink(EdgeEvent edgeEvent, EdgeVersion edgeVersion) { TenantProfileId tenantProfileId = new TenantProfileId(edgeEvent.getEntityId()); DownlinkMsg downlinkMsg = null; if (EdgeEventActionType.UPDATED.equals(edgeEvent.getAction())) { @@ -41,7 +42,7 @@ public class TenantProfileEdgeProcessor extends BaseEdgeProcessor { if (tenantProfile != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); TenantProfileUpdateMsg tenantProfileUpdateMsg = - tenantProfileMsgConstructor.constructTenantProfileUpdateMsg(msgType, tenantProfile); + tenantProfileMsgConstructor.constructTenantProfileUpdateMsg(msgType, tenantProfile, edgeVersion); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addTenantProfileUpdateMsg(tenantProfileUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetTypeEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetTypeEdgeProcessor.java index 5171724439..de9617d5d7 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetTypeEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetTypeEdgeProcessor.java @@ -22,6 +22,7 @@ import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.id.WidgetTypeId; import org.thingsboard.server.common.data.widget.WidgetTypeDetails; import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.WidgetTypeUpdateMsg; import org.thingsboard.server.queue.util.TbCoreComponent; @@ -32,7 +33,7 @@ import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; @TbCoreComponent public class WidgetTypeEdgeProcessor extends BaseEdgeProcessor { - public DownlinkMsg convertWidgetTypeEventToDownlink(EdgeEvent edgeEvent) { + public DownlinkMsg convertWidgetTypeEventToDownlink(EdgeEvent edgeEvent, EdgeVersion edgeVersion) { WidgetTypeId widgetTypeId = new WidgetTypeId(edgeEvent.getEntityId()); DownlinkMsg downlinkMsg = null; switch (edgeEvent.getAction()) { @@ -42,7 +43,7 @@ public class WidgetTypeEdgeProcessor extends BaseEdgeProcessor { if (widgetTypeDetails != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); WidgetTypeUpdateMsg widgetTypeUpdateMsg = - widgetTypeMsgConstructor.constructWidgetTypeUpdateMsg(msgType, widgetTypeDetails); + widgetTypeMsgConstructor.constructWidgetTypeUpdateMsg(msgType, widgetTypeDetails, edgeVersion); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addWidgetTypeUpdateMsg(widgetTypeUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/utils/EdgeVersionUtils.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/utils/EdgeVersionUtils.java index 75cd430a26..c9e6987a23 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/utils/EdgeVersionUtils.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/utils/EdgeVersionUtils.java @@ -21,16 +21,7 @@ import org.thingsboard.server.gen.edge.v1.EdgeVersion; @Slf4j public final class EdgeVersionUtils { - public static boolean isEdgeProtoDeprecated(EdgeVersion edgeVersion) { - switch (edgeVersion) { - case V_3_3_0: - case V_3_3_3: - case V_3_4_0: - case V_3_6_0: - return true; - case V_3_6_1: - default: - return false; - } + public static boolean isEdgeVersionOlderThan(EdgeVersion currentVersion, EdgeVersion requiredVersion) { + return currentVersion.ordinal() < requiredVersion.ordinal(); } } diff --git a/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java index 8a3dbf6ecb..2771a71eac 100644 --- a/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java @@ -249,7 +249,7 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { validateMsgsCnt(RuleChainMetadataUpdateMsg.class, 1); validateRuleChainMetadataUpdates(ruleChainUUID); - // 4 messages ('general', 'mail', 'connectivity', 'jwt) + // 4 messages ('general', 'mail', 'connectivity', 'jwt') validateMsgsCnt(AdminSettingsUpdateMsg.class, 4); validateAdminSettings(4); From e5216e8354c62c686baf2f3441d467502142c304 Mon Sep 17 00:00:00 2001 From: Dmitriymush Date: Fri, 10 Nov 2023 16:49:45 +0200 Subject: [PATCH 098/252] UI: added additional z-index to widget no data container --- .../src/app/modules/home/components/widget/widget.component.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/ui-ngx/src/app/modules/home/components/widget/widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/widget.component.scss index ac942225c8..ef3d7007e0 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/widget.component.scss @@ -30,6 +30,7 @@ align-items: center; justify-content: center; background: rgba(255, 255, 255, .75); + z-index: 2; span { color: #000; From 37e8f29c074d98b0ae2874e3ab202dc862f519cc Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 10 Nov 2023 17:06:21 +0200 Subject: [PATCH 099/252] UI: Replacement of capital letters with lowercase letters in the second word in the names of menu items --- .../assets/locale/locale.constant-en_US.json | 66 +++++++++---------- 1 file changed, 33 insertions(+), 33 deletions(-) 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 32d79d5840..ef660731bf 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -97,7 +97,7 @@ "general-settings": "General Settings", "home-settings": "Home Settings", "home": "Home", - "outgoing-mail": "Mail Server", + "outgoing-mail": "Mail server", "outgoing-mail-settings": "Outgoing Mail Server Settings", "system-settings": "System Settings", "test-mail-sent": "Test mail was successfully sent!", @@ -621,7 +621,7 @@ "filter-type-device-search-query-description": "Devices with types {{deviceTypes}} that have {{relationType}} relation {{direction}} {{rootEntity}}", "filter-type-entity-view-search-query": "Entity view search query", "filter-type-entity-view-search-query-description": "Entity views with types {{entityViewTypes}} that have {{relationType}} relation {{direction}} {{rootEntity}}", - "filter-type-apiUsageState": "Api Usage State", + "filter-type-apiUsageState": "API Usage State", "filter-type-edge-search-query": "Edge search query", "filter-type-edge-search-query-description": "Edges with types {{edgeTypes}} that have {{relationType}} relation {{direction}} {{rootEntity}}", "entity-filter": "Entity filter", @@ -647,7 +647,7 @@ "assets": "Assets", "management": "Asset management", "view-assets": "View Assets", - "add": "Add Asset", + "add": "Add asset", "asset-type-max-length": "Asset type should be less than 256", "assign-to-customer": "Assign to customer", "assign-asset-to-customer": "Assign Asset(s) To Customer", @@ -772,7 +772,7 @@ }, "api-usage": { "api-features": "API features", - "api-usage": "Api Usage", + "api-usage": "API usage", "alarm": "Alarm", "alarms-created": "Alarms created", "alarms-created-daily-activity": "Alarms created daily activity", @@ -847,10 +847,10 @@ }, "audit-log": { "audit": "Audit", - "audit-logs": "Audit Logs", + "audit-logs": "Audit logs", "timestamp": "Timestamp", - "entity-type": "Entity Type", - "entity-name": "Entity Name", + "entity-type": "Entity type", + "entity-name": "Entity name", "user": "User", "type": "Type", "status": "Status", @@ -951,7 +951,7 @@ "public-assets": "Public Assets", "public-edges": "Public Edges", "public-entity-views": "Public Entity Views", - "add": "Add Customer", + "add": "Add customer", "delete": "Delete customer", "manage-customer-users": "Manage customer users", "manage-customer-devices": "Manage customer devices", @@ -1011,7 +1011,7 @@ "dashboards": "Dashboards", "management": "Dashboard management", "view-dashboards": "View Dashboards", - "add": "Add Dashboard", + "add": "Add dashboard", "assign-dashboard-to-customer": "Assign Dashboard(s) To Customer", "assign-dashboard-to-customer-text": "Please select the dashboards to assign to the customer", "assign-dashboard-to-edge-title": "Assign Dashboard(s) To Edge", @@ -1299,7 +1299,7 @@ "device-list-empty": "No devices selected.", "device-name-filter-required": "Device name filter is required.", "device-name-filter-no-device-matched": "No devices starting with '{{device}}' were found.", - "add": "Add Device", + "add": "Add device", "assign-to-customer": "Assign to customer", "assign-device-to-customer": "Assign Device(s) To Customer", "assign-device-to-customer-text": "Please select the devices to assign to the customer", @@ -1933,7 +1933,7 @@ "type-max-length": "Type should be less than 256", "management": "Edge management", "no-edges-matching": "No edges matching '{{entity}}' were found.", - "add": "Add Edge", + "add": "Add edge", "no-edges-text": "No edges found", "edge-details": "Edge details", "add-edge-text": "Add new edge", @@ -2192,7 +2192,7 @@ "no-entities-prompt": "No entities found", "no-data": "No data to display", "columns-to-display": "Columns to Display", - "type-api-usage-state": "Api Usage State", + "type-api-usage-state": "API Usage State", "type-edge": "Edge", "type-edges": "Edges", "list-of-edges": "{ count, plural, =1 {One edge} other {List of # edges} }", @@ -2231,9 +2231,9 @@ "label": "Label" }, "entity-view": { - "entity-view": "Entity View", + "entity-view": "Entity view", "entity-view-required": "Entity view is required.", - "entity-views": "Entity Views", + "entity-views": "Entity views", "management": "Entity View management", "view-entity-views": "View Entity Views", "entity-view-alias": "Entity View alias", @@ -2259,7 +2259,7 @@ "entity-view-list-empty": "No entity views selected.", "entity-view-name-filter-required": "Entity view name filter is required.", "entity-view-name-filter-no-entity-view-matched": "No entity views starting with '{{entityView}}' were found.", - "add": "Add Entity View", + "add": "Add entity view", "entity-view-public": "Entity view is public", "assign-to-customer": "Assign to customer", "assign-entity-view-to-customer": "Assign Entity View(s) To Customer", @@ -2287,14 +2287,14 @@ "unassign-entity-view": "Unassign entity view", "unassign-entity-views-title": "Are you sure you want to unassign { count, plural, =1 {1 entity view} other {# entity views} }?", "unassign-entity-views-text": "After the confirmation all selected entity views will be unassigned and won't be accessible by the customer.", - "entity-view-type": "Entity View type", - "entity-view-type-required": "Entity View type is required.", + "entity-view-type": "Entity view type", + "entity-view-type-required": "Entity view type is required.", "select-entity-view-type": "Select entity view type", "enter-entity-view-type": "Enter entity view type", "any-entity-view": "Any entity view", "no-entity-view-types-matching": "No entity view types matching '{{entitySubtype}}' were found.", "entity-view-type-list-empty": "No entity view types selected.", - "entity-view-types": "Entity View types", + "entity-view-types": "Entity view types", "created-time": "Created time", "name": "Name", "name-required": "Name is required.", @@ -3457,7 +3457,7 @@ "use-external-url": "Use external URL", "version": "Version", "version-required": "Version is required.", - "version-tag": "Version Tag", + "version-tag": "Version tag", "version-tag-hint": "Custom tag should match the package version reported by your device.", "version-max-length": "Version should be less than 256", "warning-after-save-no-edit": "Once the package is uploaded, you will not be able to modify title, version, device profile and package type." @@ -3608,7 +3608,7 @@ "no-relations-text": "No relations found" }, "resource": { - "add": "Add Resource", + "add": "Add resource", "all-types": "All", "copyId": "Copy resource Id", "delete": "Delete resource", @@ -3653,7 +3653,7 @@ "name-required": "Name is required.", "name-max-length": "Name should be less than 256", "description": "Description", - "add": "Add Rule Chain", + "add": "Add rule chain", "set-root": "Make rule chain root", "set-root-rulechain-title": "Are you sure you want to make the rule chain '{{ruleChainName}}' root?", "set-root-rulechain-text": "After the confirmation the rule chain will become root and will handle all incoming transport messages.", @@ -3685,8 +3685,8 @@ "selected-rulechains": "{ count, plural, =1 {1 rule chain} other {# rule chains} } selected", "open-rulechain": "Open rule chain", "assign-new-rulechain": "Assign new rulechain", - "edge-template-root": "Template Root", - "assign-to-edge": "Assign to Edge", + "edge-template-root": "Template root", + "assign-to-edge": "Assign to edge", "edge-rulechain": "Edge rule chain", "unassign-rulechain-from-edge-text": "After the confirmation the rulechain will be unassigned and won't be accessible by the edge.", "unassign-rulechains-from-edge-title": "Are you sure you want to unassign { count, plural, =1 {1 rulechain} other {# rulechains} }?", @@ -3896,7 +3896,7 @@ "tenant": "Tenant", "tenants": "Tenants", "management": "Tenant management", - "add": "Add Tenant", + "add": "Add tenant", "admins": "Admins", "manage-tenant-admins": "Manage tenant admins", "delete": "Delete tenant", @@ -4623,13 +4623,13 @@ "user": { "user": "User", "users": "Users", - "customer-users": "Customer Users", - "tenant-admins": "Tenant Admins", + "customer-users": "Customer users", + "tenant-admins": "Tenant admins", "sys-admin": "System administrator", "tenant-admin": "Tenant administrator", "customer": "Customer", "anonymous": "Anonymous", - "add": "Add User", + "add": "Add user", "delete": "Delete user", "add-user-text": "Add new user", "no-users-text": "No users found", @@ -4644,8 +4644,8 @@ "email": "Email", "email-required": "Email is required.", "invalid-email-format": "Invalid email format.", - "first-name": "First Name", - "last-name": "Last Name", + "first-name": "First name", + "last-name": "Last name", "description": "Description", "default-dashboard": "Default dashboard", "always-fullscreen": "Always fullscreen", @@ -4764,7 +4764,7 @@ "auto-commit-settings-read-only-hint": "Auto-commit feature doesn't work with enabled read-only option in Repository settings." }, "widget": { - "widget-library": "Widgets Library", + "widget-library": "Widgets library", "widget-bundle": "Widgets Bundle", "all-bundles": "All bundles", "select-widgets-bundle": "Select widgets bundle", @@ -4849,7 +4849,7 @@ "widget-template-load-failed-error": "Failed to load widget template!", "details": "Details", "widget-details": "Widget details", - "add": "Add Widget", + "add": "Add widget", "add-existing-widget": "Add existing widget", "add-new-widget": "Add new widget", "search-widgets": "Search widgets", @@ -4925,9 +4925,9 @@ }, "widgets-bundle": { "current": "Current bundle", - "widgets-bundles": "Widgets Bundles", + "widgets-bundles": "Widgets bundles", "widgets-bundle-widgets": "Widgets Bundle Widgets", - "add": "Add Widgets Bundle", + "add": "Add widgets bundle", "delete": "Delete widgets bundle", "title": "Title", "title-required": "Title is required.", From 3d182a736b44186f19cf44b183ed7fad44598a37 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Fri, 10 Nov 2023 17:32:29 +0200 Subject: [PATCH 100/252] Improve load system widgets method. --- .../install/ThingsboardInstallService.java | 4 +- .../DefaultSystemDataLoaderService.java | 51 ------- .../service/install/InstallScripts.java | 124 ++++++++++++------ .../install/SystemDataLoaderService.java | 4 - 4 files changed, 89 insertions(+), 94 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java index 9f51fcf288..0730bca8f8 100644 --- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java +++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java @@ -108,7 +108,7 @@ public class ThingsboardInstallService { log.info("Migrating ThingsBoard entities data from cassandra to SQL database ..."); entitiesMigrateService.migrate(); log.info("Updating system data..."); - systemDataLoaderService.updateSystemWidgets(); + systemDataLoaderService.loadSystemWidgets(); } else if ("3.0.1-cassandra".equals(upgradeFromVersion)) { log.info("Migrating ThingsBoard latest timeseries data from cassandra to SQL database ..."); latestMigrateService.migrate(); @@ -275,7 +275,7 @@ public class ThingsboardInstallService { entityDatabaseSchemaService.createOrUpdateDeviceInfoView(persistToTelemetry); log.info("Updating system data..."); dataUpdateService.upgradeRuleNodes(); - systemDataLoaderService.updateSystemWidgets(); + systemDataLoaderService.loadSystemWidgets(); installScripts.loadSystemLwm2mResources(); } log.info("Upgrade finished successfully!"); diff --git a/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java b/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java index 16704a0856..cf7b779dbe 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java @@ -63,7 +63,6 @@ import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.BooleanDataEntry; import org.thingsboard.server.common.data.kv.DoubleDataEntry; import org.thingsboard.server.common.data.kv.LongDataEntry; -import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageDataIterable; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.query.BooleanFilterPredicate; @@ -84,9 +83,6 @@ import org.thingsboard.server.common.data.security.UserCredentials; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; import org.thingsboard.server.common.data.tenant.profile.TenantProfileQueueConfiguration; -import org.thingsboard.server.common.data.widget.DeprecatedFilter; -import org.thingsboard.server.common.data.widget.WidgetTypeInfo; -import org.thingsboard.server.common.data.widget.WidgetsBundle; import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.device.DeviceCredentialsService; @@ -536,58 +532,11 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { installScripts.loadDashboards(demoTenant.getId(), null); } - @Override - public void deleteSystemWidgetBundle(String bundleAlias) throws Exception { - WidgetsBundle widgetsBundle = widgetsBundleService.findWidgetsBundleByTenantIdAndAlias(TenantId.SYS_TENANT_ID, bundleAlias); - if (widgetsBundle != null) { - PageData widgetTypes; - var pageLink = new PageLink(1024); - do { - widgetTypes = widgetTypeService.findWidgetTypesInfosByWidgetsBundleId(TenantId.SYS_TENANT_ID, widgetsBundle.getId(), false, DeprecatedFilter.ALL, null, pageLink); - for (var widgetType : widgetTypes.getData()) { - widgetTypeService.deleteWidgetType(TenantId.SYS_TENANT_ID, widgetType.getId()); - } - pageLink.nextPageLink(); - } while (widgetTypes.hasNext()); - widgetsBundleService.deleteWidgetsBundle(TenantId.SYS_TENANT_ID, widgetsBundle.getId()); - } - } - @Override public void loadSystemWidgets() throws Exception { installScripts.loadSystemWidgets(); } - @Override - public void updateSystemWidgets() throws Exception { - this.deleteSystemWidgetBundle("charts"); - this.deleteSystemWidgetBundle("cards"); - this.deleteSystemWidgetBundle("maps"); - this.deleteSystemWidgetBundle("analogue_gauges"); - this.deleteSystemWidgetBundle("digital_gauges"); - this.deleteSystemWidgetBundle("gpio_widgets"); - this.deleteSystemWidgetBundle("alarm_widgets"); - this.deleteSystemWidgetBundle("control_widgets"); - this.deleteSystemWidgetBundle("maps_v2"); - this.deleteSystemWidgetBundle("gateway_widgets"); - this.deleteSystemWidgetBundle("input_widgets"); - this.deleteSystemWidgetBundle("date"); - this.deleteSystemWidgetBundle("entity_admin_widgets"); - this.deleteSystemWidgetBundle("navigation_widgets"); - this.deleteSystemWidgetBundle("edge_widgets"); - this.deleteSystemWidgetBundle("home_page_widgets"); - this.deleteSystemWidgetBundle("entity_widgets"); - this.deleteSystemWidgetBundle("html_widgets"); - this.deleteSystemWidgetBundle("tables"); - this.deleteSystemWidgetBundle("count_widgets"); - this.deleteSystemWidgetBundle("status_indicators"); - this.deleteSystemWidgetBundle("outdoor_environment"); - this.deleteSystemWidgetBundle("indoor_environment"); - this.deleteSystemWidgetBundle("air_quality"); - this.deleteSystemWidgetBundle("liquid_level_tanks"); - installScripts.loadSystemWidgets(); - } - private User createUser(Authority authority, TenantId tenantId, CustomerId customerId, diff --git a/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java b/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java index 7f0b70cb27..3e23f9af37 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java +++ b/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java @@ -29,9 +29,13 @@ import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainMetaData; +import org.thingsboard.server.common.data.widget.DeprecatedFilter; import org.thingsboard.server.common.data.widget.WidgetTypeDetails; +import org.thingsboard.server.common.data.widget.WidgetTypeInfo; import org.thingsboard.server.common.data.widget.WidgetsBundle; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.exception.DataValidationException; @@ -48,7 +52,9 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Base64; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.function.Function; @@ -189,6 +195,38 @@ public class InstallScripts { } public void loadSystemWidgets() throws Exception { + Map widgetsBundlesMap = new HashMap<>(); + Path widgetBundlesDir = Paths.get(getDataDir(), JSON_DIR, SYSTEM_DIR, WIDGET_BUNDLES_DIR); + try (DirectoryStream dirStream = Files.newDirectoryStream(widgetBundlesDir, path -> path.toString().endsWith(JSON_EXT))) { + dirStream.forEach( + path -> { + JsonNode widgetsBundleDescriptorJson; + try { + widgetsBundleDescriptorJson = JacksonUtil.toJsonNode(path.toFile()); + } catch (Exception e) { + log.error("Unable to parse widgets bundle from json: [{}]", path); + throw new RuntimeException("Unable to parse widgets bundle from json", e); + } + if (widgetsBundleDescriptorJson == null || !widgetsBundleDescriptorJson.has("widgetsBundle")) { + log.error("Invalid widgets bundle json: [{}]", path); + throw new RuntimeException("Invalid widgets bundle json: [" + path + "]"); + } + widgetsBundlesMap.put(path, widgetsBundleDescriptorJson); + JsonNode bundleAliasNode = widgetsBundleDescriptorJson.get("widgetsBundle").get("alias"); + if (bundleAliasNode == null || !bundleAliasNode.isTextual()) { + log.error("Invalid widgets bundle json: [{}]", path); + throw new RuntimeException("Invalid widgets bundle json: [" + path + "]"); + } + String bundleAlias = bundleAliasNode.asText(); + try { + this.deleteSystemWidgetBundle(bundleAlias); + } catch (Exception e) { + log.error("Failed to delete system widgets bundle: [{}]", bundleAlias); + throw new RuntimeException("Failed to delete system widgets bundle: [" + bundleAlias + "]", e); + } + } + ); + } Path widgetTypesDir = Paths.get(getDataDir(), JSON_DIR, SYSTEM_DIR, WIDGET_TYPES_DIR); if (Files.exists(widgetTypesDir)) { try (DirectoryStream dirStream = Files.newDirectoryStream(widgetTypesDir, path -> path.toString().endsWith(JSON_EXT))) { @@ -206,44 +244,56 @@ public class InstallScripts { ); } } - Path widgetBundlesDir = Paths.get(getDataDir(), JSON_DIR, SYSTEM_DIR, WIDGET_BUNDLES_DIR); - try (DirectoryStream dirStream = Files.newDirectoryStream(widgetBundlesDir, path -> path.toString().endsWith(JSON_EXT))) { - dirStream.forEach( - path -> { - try { - JsonNode widgetsBundleDescriptorJson = JacksonUtil.toJsonNode(path.toFile()); - JsonNode widgetsBundleJson = widgetsBundleDescriptorJson.get("widgetsBundle"); - WidgetsBundle widgetsBundle = JacksonUtil.treeToValue(widgetsBundleJson, WidgetsBundle.class); - WidgetsBundle savedWidgetsBundle = widgetsBundleService.saveWidgetsBundle(widgetsBundle); - List widgetTypeFqns = new ArrayList<>(); - if (widgetsBundleDescriptorJson.has("widgetTypes")) { - JsonNode widgetTypesArrayJson = widgetsBundleDescriptorJson.get("widgetTypes"); - widgetTypesArrayJson.forEach( - widgetTypeJson -> { - try { - WidgetTypeDetails widgetTypeDetails = JacksonUtil.treeToValue(widgetTypeJson, WidgetTypeDetails.class); - var savedWidgetType = widgetTypeService.saveWidgetType(widgetTypeDetails); - widgetTypeFqns.add(savedWidgetType.getFqn()); - } catch (Exception e) { - log.error("Unable to load widget type from json: [{}]", path.toString()); - throw new RuntimeException("Unable to load widget type from json", e); - } - } - ); - } - if (widgetsBundleDescriptorJson.has("widgetTypeFqns")) { - JsonNode widgetFqnsArrayJson = widgetsBundleDescriptorJson.get("widgetTypeFqns"); - widgetFqnsArrayJson.forEach(fqnJson -> { - widgetTypeFqns.add(fqnJson.asText()); - }); + for (var widgetsBundleDescriptorEntry : widgetsBundlesMap.entrySet()) { + Path path = widgetsBundleDescriptorEntry.getKey(); + try { + JsonNode widgetsBundleDescriptorJson = widgetsBundleDescriptorEntry.getValue(); + JsonNode widgetsBundleJson = widgetsBundleDescriptorJson.get("widgetsBundle"); + WidgetsBundle widgetsBundle = JacksonUtil.treeToValue(widgetsBundleJson, WidgetsBundle.class); + WidgetsBundle savedWidgetsBundle = widgetsBundleService.saveWidgetsBundle(widgetsBundle); + List widgetTypeFqns = new ArrayList<>(); + if (widgetsBundleDescriptorJson.has("widgetTypes")) { + JsonNode widgetTypesArrayJson = widgetsBundleDescriptorJson.get("widgetTypes"); + widgetTypesArrayJson.forEach( + widgetTypeJson -> { + try { + WidgetTypeDetails widgetTypeDetails = JacksonUtil.treeToValue(widgetTypeJson, WidgetTypeDetails.class); + var savedWidgetType = widgetTypeService.saveWidgetType(widgetTypeDetails); + widgetTypeFqns.add(savedWidgetType.getFqn()); + } catch (Exception e) { + log.error("Unable to load widget type from json: [{}]", path.toString()); + throw new RuntimeException("Unable to load widget type from json", e); + } } - widgetTypeService.updateWidgetsBundleWidgetFqns(TenantId.SYS_TENANT_ID, savedWidgetsBundle.getId(), widgetTypeFqns); - } catch (Exception e) { - log.error("Unable to load widgets bundle from json: [{}]", path.toString()); - throw new RuntimeException("Unable to load widgets bundle from json", e); - } - } - ); + ); + } + if (widgetsBundleDescriptorJson.has("widgetTypeFqns")) { + JsonNode widgetFqnsArrayJson = widgetsBundleDescriptorJson.get("widgetTypeFqns"); + widgetFqnsArrayJson.forEach(fqnJson -> { + widgetTypeFqns.add(fqnJson.asText()); + }); + } + widgetTypeService.updateWidgetsBundleWidgetFqns(TenantId.SYS_TENANT_ID, savedWidgetsBundle.getId(), widgetTypeFqns); + } catch (Exception e) { + log.error("Unable to load widgets bundle from json: [{}]", path.toString()); + throw new RuntimeException("Unable to load widgets bundle from json", e); + } + } + } + + private void deleteSystemWidgetBundle(String bundleAlias) { + WidgetsBundle widgetsBundle = widgetsBundleService.findWidgetsBundleByTenantIdAndAlias(TenantId.SYS_TENANT_ID, bundleAlias); + if (widgetsBundle != null) { + PageData widgetTypes; + var pageLink = new PageLink(1024); + do { + widgetTypes = widgetTypeService.findWidgetTypesInfosByWidgetsBundleId(TenantId.SYS_TENANT_ID, widgetsBundle.getId(), false, DeprecatedFilter.ALL, null, pageLink); + for (var widgetType : widgetTypes.getData()) { + widgetTypeService.deleteWidgetType(TenantId.SYS_TENANT_ID, widgetType.getId()); + } + pageLink.nextPageLink(); + } while (widgetTypes.hasNext()); + widgetsBundleService.deleteWidgetsBundle(TenantId.SYS_TENANT_ID, widgetsBundle.getId()); } } diff --git a/application/src/main/java/org/thingsboard/server/service/install/SystemDataLoaderService.java b/application/src/main/java/org/thingsboard/server/service/install/SystemDataLoaderService.java index 08de12da78..344870cf1b 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/SystemDataLoaderService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/SystemDataLoaderService.java @@ -31,12 +31,8 @@ public interface SystemDataLoaderService { void loadSystemWidgets() throws Exception; - void updateSystemWidgets() throws Exception; - void loadDemoData() throws Exception; - void deleteSystemWidgetBundle(String bundleAlias) throws Exception; - void createQueues(); void createDefaultNotificationConfigs(); From ed7b86cf61e7d80d82a75831fdc3ff857650ffef Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Fri, 10 Nov 2023 17:41:16 +0200 Subject: [PATCH 101/252] Code review changes --- .../telemetry/TelemetryEdgeProcessor.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessor.java index 647994dcd9..667d661d2e 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessor.java @@ -21,6 +21,7 @@ import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.gen.edge.v1.DownlinkMsg; import org.thingsboard.server.gen.edge.v1.EntityDataProto; @@ -37,11 +38,14 @@ public class TelemetryEdgeProcessor extends BaseTelemetryProcessor { } public DownlinkMsg convertTelemetryEventToDownlink(EdgeEvent edgeEvent) throws JsonProcessingException { - if (edgeEvent.getBody() != null && edgeEvent.getBody().toString().length() > 1000) { - log.debug("[{}][{}][{}] Conversion to a DownlinkMsg telemetry event failed due to a size limit violation. " + - "Current size is {}, but the limit is 1000. {}", edgeEvent.getTenantId(), edgeEvent.getEdgeId(), - edgeEvent.getEntityId(), edgeEvent.getBody().toString().length(), edgeEvent.getBody().toString().substring(0, 100)); - return null; + if (edgeEvent.getBody() != null) { + String bodyStr = edgeEvent.getBody().toString(); + if (bodyStr.length() > 1000) { + log.debug("[{}][{}][{}] Conversion to a DownlinkMsg telemetry event failed due to a size limit violation. " + + "Current size is {}, but the limit is 1000. {}", edgeEvent.getTenantId(), edgeEvent.getEdgeId(), + edgeEvent.getEntityId(), bodyStr.length(), StringUtils.truncate(bodyStr, 100)); + return null; + } } EntityType entityType = EntityType.valueOf(edgeEvent.getType().name()); EntityDataProto entityDataProto = convertTelemetryEventToEntityDataProto( From 4041186f40f41f176c79b9a61adf23eb73aafff0 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Fri, 10 Nov 2023 17:59:58 +0200 Subject: [PATCH 102/252] EdgeRepository - fixed typo searchText <> textSearch --- .../thingsboard/server/dao/sql/edge/EdgeRepository.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/EdgeRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/EdgeRepository.java index f632ef92ef..69795edf11 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/EdgeRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/EdgeRepository.java @@ -91,10 +91,10 @@ public interface EdgeRepository extends JpaRepository { "LEFT JOIN CustomerEntity c on c.id = a.customerId " + "WHERE a.tenantId = :tenantId " + "AND a.customerId = :customerId " + - "AND (:searchText IS NULL OR ilike(a.name, CONCAT('%', :textSearch, '%')) = true)") + "AND (:textSearch IS NULL OR ilike(a.name, CONCAT('%', :textSearch, '%')) = true)") Page findEdgeInfosByTenantIdAndCustomerId(@Param("tenantId") UUID tenantId, @Param("customerId") UUID customerId, - @Param("searchText") String searchText, + @Param("textSearch") String textSearch, Pageable pageable); @Query("SELECT new org.thingsboard.server.dao.model.sql.EdgeInfoEntity(a, c.title, c.additionalInfo) " + @@ -113,11 +113,11 @@ public interface EdgeRepository extends JpaRepository { @Query("SELECT ee FROM EdgeEntity ee, RelationEntity re WHERE ee.tenantId = :tenantId " + "AND ee.id = re.fromId AND re.fromType = 'EDGE' AND re.relationTypeGroup = 'EDGE' " + "AND re.relationType = 'Contains' AND re.toId = :entityId AND re.toType = :entityType " + - "AND (:searchText IS NULL OR ilike(ee.name, CONCAT('%', :searchText, '%')) = true)") + "AND (:textSearch IS NULL OR ilike(ee.name, CONCAT('%', :textSearch, '%')) = true)") Page findByTenantIdAndEntityId(@Param("tenantId") UUID tenantId, @Param("entityId") UUID entityId, @Param("entityType") String entityType, - @Param("searchText") String searchText, + @Param("textSearch") String textSearch, Pageable pageable); @Query("SELECT ee FROM EdgeEntity ee, TenantEntity te WHERE ee.tenantId = te.id AND te.tenantProfileId = :tenantProfileId ") From 78a87185b0f7fc011ba08a57863b9e9c95f1831e Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Fri, 10 Nov 2023 18:26:02 +0200 Subject: [PATCH 103/252] Node upgrade improvement --- .../update/DefaultDataUpdateService.java | 4 +- .../rule/DefaultTbRuleChainService.java | 4 +- .../server/utils/TbNodeUpgradeUtils.java | 19 ++--- .../server/utils/TbNodeUpgradeUtilsTest.java | 72 +++++++++---------- 4 files changed, 46 insertions(+), 53 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java b/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java index 5375590084..7d08fdca00 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java @@ -294,9 +294,7 @@ public class DefaultDataUpdateService implements DataUpdateService { log.debug("Going to upgrade rule node with id: {} type: {} fromVersion: {} toVersion: {}", ruleNodeId, ruleNodeType, fromVersion, toVersion); try { - ruleNode.setConfiguration(TbNodeUpgradeUtils.upgradeRuleNodeConfiguration(ruleNode, ruleNodeClassInfo.getAnnotation(), - ruleNodeClassInfo.getClazz())); - ruleNode.setConfigurationVersion(toVersion); + TbNodeUpgradeUtils.upgradeConfigurationAndVersion(ruleNode, ruleNodeClassInfo); saveFutures.add(jpaExecutorService.submit(() -> { ruleChainService.saveRuleNode(TenantId.SYS_TENANT_ID, ruleNode); log.debug("Successfully upgrade rule node with id: {} type: {} fromVersion: {} toVersion: {}", diff --git a/application/src/main/java/org/thingsboard/server/service/rule/DefaultTbRuleChainService.java b/application/src/main/java/org/thingsboard/server/service/rule/DefaultTbRuleChainService.java index 50f370fbb9..70c150b5f2 100644 --- a/application/src/main/java/org/thingsboard/server/service/rule/DefaultTbRuleChainService.java +++ b/application/src/main/java/org/thingsboard/server/service/rule/DefaultTbRuleChainService.java @@ -406,9 +406,7 @@ public class DefaultTbRuleChainService extends AbstractTbEntityService implement log.debug("Going to upgrade rule node with id: {} type: {} fromVersion: {} toVersion: {}", ruleNodeId, ruleNodeType, fromVersion, toVersion); try { - node.setConfiguration(TbNodeUpgradeUtils.upgradeRuleNodeConfiguration(node, ruleNodeClass.getAnnotation(), - ruleNodeClass.getClazz())); - node.setConfigurationVersion(toVersion); + TbNodeUpgradeUtils.upgradeConfigurationAndVersion(node, ruleNodeClass); log.debug("Successfully upgrade rule node with id: {} type: {}, rule chain id: {} fromVersion: {} toVersion: {}", ruleNodeId, ruleNodeType, ruleChainId, fromVersion, toVersion); } catch (TbNodeException e) { diff --git a/application/src/main/java/org/thingsboard/server/utils/TbNodeUpgradeUtils.java b/application/src/main/java/org/thingsboard/server/utils/TbNodeUpgradeUtils.java index 98f5e713df..5aa4176b1f 100644 --- a/application/src/main/java/org/thingsboard/server/utils/TbNodeUpgradeUtils.java +++ b/application/src/main/java/org/thingsboard/server/utils/TbNodeUpgradeUtils.java @@ -20,20 +20,23 @@ import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.common.data.util.TbPair; +import org.thingsboard.server.service.component.RuleNodeClassInfo; public class TbNodeUpgradeUtils { - public static JsonNode upgradeRuleNodeConfiguration(RuleNode node, - org.thingsboard.rule.engine.api.RuleNode annotation, - Class nodeClass) throws Exception { + public static void upgradeConfigurationAndVersion(RuleNode node, RuleNodeClassInfo nodeInfo) throws Exception { JsonNode oldConfiguration = node.getConfiguration(); if (oldConfiguration == null || !oldConfiguration.isObject()) { - var configClass = annotation.configClazz(); - return JacksonUtil.valueToTree(configClass.getDeclaredConstructor().newInstance().defaultConfiguration()); + var configClass = nodeInfo.getAnnotation().configClazz(); + node.setConfiguration(JacksonUtil.valueToTree(configClass.getDeclaredConstructor().newInstance().defaultConfiguration())); + } else { + var tbVersionedNode = (TbNode) nodeInfo.getClazz().getDeclaredConstructor().newInstance(); + TbPair upgradeResult = tbVersionedNode.upgrade(node.getConfigurationVersion(), oldConfiguration); + if (upgradeResult.getFirst()) { + node.setConfiguration(upgradeResult.getSecond()); + } } - var tbVersionedNode = (TbNode) nodeClass.getDeclaredConstructor().newInstance(); - TbPair upgradeResult = tbVersionedNode.upgrade(node.getConfigurationVersion(), oldConfiguration); - return upgradeResult.getFirst() ? upgradeResult.getSecond() : oldConfiguration; + node.setConfigurationVersion(nodeInfo.getCurrentVersion()); } } diff --git a/application/src/test/java/org/thingsboard/server/utils/TbNodeUpgradeUtilsTest.java b/application/src/test/java/org/thingsboard/server/utils/TbNodeUpgradeUtilsTest.java index 1fb2ddcc25..483772301b 100644 --- a/application/src/test/java/org/thingsboard/server/utils/TbNodeUpgradeUtilsTest.java +++ b/application/src/test/java/org/thingsboard/server/utils/TbNodeUpgradeUtilsTest.java @@ -19,11 +19,10 @@ import com.fasterxml.jackson.databind.node.NullNode; import org.assertj.core.api.Assertions; import org.junit.Test; import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.metadata.TbGetAttributesNode; import org.thingsboard.rule.engine.metadata.TbGetAttributesNodeConfiguration; import org.thingsboard.server.common.data.rule.RuleNode; -import org.thingsboard.server.common.data.util.TbPair; +import org.thingsboard.server.service.component.RuleNodeClassInfo; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -33,53 +32,59 @@ public class TbNodeUpgradeUtilsTest { @Test public void testUpgradeRuleNodeConfigurationWithNullConfig() throws Exception { // GIVEN - var node = mock(RuleNode.class); - var nodeClass = TbGetAttributesNode.class; + var node = new RuleNode(); + var nodeInfo = mock(RuleNodeClassInfo.class); var nodeConfigClazz = TbGetAttributesNodeConfiguration.class; - var annotation = mock(org.thingsboard.rule.engine.api.RuleNode.class); - var defaultConfig = JacksonUtil.valueToTree(nodeConfigClazz.getDeclaredConstructor().newInstance().defaultConfiguration()); - when(node.getConfiguration()).thenReturn(null); - when(node.getConfigurationVersion()).thenReturn(0); + when(nodeInfo.getClazz()).thenReturn((Class)TbGetAttributesNode.class); + when(nodeInfo.getCurrentVersion()).thenReturn(1); + when(nodeInfo.getAnnotation()).thenReturn(annotation); when(annotation.configClazz()).thenReturn((Class) nodeConfigClazz); - // WHEN - var upgradedConfig = TbNodeUpgradeUtils.upgradeRuleNodeConfiguration(node, annotation, nodeClass); + // WHEN + TbNodeUpgradeUtils.upgradeConfigurationAndVersion(node, nodeInfo); // THEN - Assertions.assertThat(upgradedConfig).isEqualTo(defaultConfig); + Assertions.assertThat(node.getConfiguration()).isEqualTo(defaultConfig); + Assertions.assertThat(node.getConfigurationVersion()).isEqualTo(1); } @Test public void testUpgradeRuleNodeConfigurationWithNullNodeConfig() throws Exception { // GIVEN - var node = mock(RuleNode.class); - var nodeClass = TbGetAttributesNode.class; + var node = new RuleNode(); + node.setConfiguration(NullNode.instance); + var nodeInfo = mock(RuleNodeClassInfo.class); var nodeConfigClazz = TbGetAttributesNodeConfiguration.class; - var annotation = mock(org.thingsboard.rule.engine.api.RuleNode.class); - var defaultConfig = JacksonUtil.valueToTree(nodeConfigClazz.getDeclaredConstructor().newInstance().defaultConfiguration()); - when(node.getConfiguration()).thenReturn(NullNode.instance); - when(node.getConfigurationVersion()).thenReturn(0); + when(nodeInfo.getClazz()).thenReturn((Class)TbGetAttributesNode.class); + when(nodeInfo.getCurrentVersion()).thenReturn(1); + when(nodeInfo.getAnnotation()).thenReturn(annotation); when(annotation.configClazz()).thenReturn((Class) nodeConfigClazz); - // WHEN - var upgradedConfig = TbNodeUpgradeUtils.upgradeRuleNodeConfiguration(node, annotation, nodeClass); + // WHEN + TbNodeUpgradeUtils.upgradeConfigurationAndVersion(node, nodeInfo); // THEN - Assertions.assertThat(upgradedConfig).isEqualTo(defaultConfig); + Assertions.assertThat(node.getConfiguration()).isEqualTo(defaultConfig); + Assertions.assertThat(node.getConfigurationVersion()).isEqualTo(1); } @Test public void testUpgradeRuleNodeConfigurationWithNonNullConfig() throws Exception { // GIVEN - var node = mock(RuleNode.class); - var nodeClass = TbGetAttributesNode.class; + var node = new RuleNode(); + var nodeInfo = mock(RuleNodeClassInfo.class); var nodeConfigClazz = TbGetAttributesNodeConfiguration.class; - var annotation = mock(org.thingsboard.rule.engine.api.RuleNode.class); + var defaultConfig = JacksonUtil.valueToTree(nodeConfigClazz.getDeclaredConstructor().newInstance().defaultConfiguration()); + + when(nodeInfo.getClazz()).thenReturn((Class)TbGetAttributesNode.class); + when(nodeInfo.getCurrentVersion()).thenReturn(1); + when(nodeInfo.getAnnotation()).thenReturn(annotation); + when(annotation.configClazz()).thenReturn((Class) nodeConfigClazz); String versionZeroDefaultConfigStr = "{\"fetchToData\":false," + "\"clientAttributeNames\":[]," + @@ -88,24 +93,13 @@ public class TbNodeUpgradeUtilsTest { "\"latestTsKeyNames\":[]," + "\"tellFailureIfAbsent\":true," + "\"getLatestValueWithTs\":false}"; - - var existingConfig = JacksonUtil.toJsonNode(versionZeroDefaultConfigStr); - int fromVersion = 0; - var currentDefaultConfig = JacksonUtil.valueToTree(nodeConfigClazz.getDeclaredConstructor().newInstance().defaultConfiguration()); - - when(node.getConfiguration()).thenReturn(existingConfig); - when(node.getConfigurationVersion()).thenReturn(fromVersion); - when(annotation.configClazz()).thenReturn((Class) nodeConfigClazz); - - TbNode tbVersionedNodeMock = mock(nodeClass); - - when(tbVersionedNodeMock.upgrade(fromVersion, existingConfig)).thenReturn(new TbPair<>(true, currentDefaultConfig)); - + node.setConfiguration(JacksonUtil.toJsonNode(versionZeroDefaultConfigStr)); // WHEN - var upgradedConfig = TbNodeUpgradeUtils.upgradeRuleNodeConfiguration(node, annotation, nodeClass); - + TbNodeUpgradeUtils.upgradeConfigurationAndVersion(node, nodeInfo); // THEN - Assertions.assertThat(upgradedConfig).isEqualTo(currentDefaultConfig); + Assertions.assertThat(node.getConfiguration()).isEqualTo(defaultConfig); + Assertions.assertThat(node.getConfigurationVersion()).isEqualTo(1); + } } From afa54ef277c79ca3fa0e2d26c08cc5b77f4ae6f1 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Fri, 10 Nov 2023 19:36:39 +0200 Subject: [PATCH 104/252] Update TBEL version to 1.1.4 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d5b517c48e..818bef02f0 100755 --- a/pom.xml +++ b/pom.xml @@ -78,7 +78,7 @@ 3.8.1 3.21.9 1.42.1 - 1.1.3 + 1.1.4 1.18.18 1.2.4 1.2.5 From dd19d6086946ce00094f5996afede426792557d7 Mon Sep 17 00:00:00 2001 From: ShvaykaD Date: Fri, 10 Nov 2023 20:11:56 +0200 Subject: [PATCH 105/252] created entity view callback and removed it usage for save without latest method --- .../DefaultTelemetrySubscriptionService.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java index df3a15e765..e66029a8a1 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java @@ -175,20 +175,21 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer @Override public void saveAndNotifyInternal(TenantId tenantId, EntityId entityId, List ts, long ttl, FutureCallback callback) { ListenableFuture saveFuture = tsService.save(tenantId, entityId, ts, ttl); - addCallbacks(tenantId, entityId, ts, callback, saveFuture); + addMainCallback(saveFuture, callback); + addWsCallback(saveFuture, success -> onTimeSeriesUpdate(tenantId, entityId, ts)); + addEntityViewCallback(tenantId, entityId, ts); } private void saveWithoutLatestAndNotifyInternal(TenantId tenantId, EntityId entityId, List ts, long ttl, FutureCallback callback) { ListenableFuture saveFuture = tsService.saveWithoutLatest(tenantId, entityId, ts, ttl); - addCallbacks(tenantId, entityId, ts, callback, saveFuture); - } - - private void addCallbacks(TenantId tenantId, EntityId entityId, List ts, FutureCallback callback, ListenableFuture saveFuture) { addMainCallback(saveFuture, callback); addWsCallback(saveFuture, success -> onTimeSeriesUpdate(tenantId, entityId, ts)); + } + + private void addEntityViewCallback(TenantId tenantId, EntityId entityId, List ts) { if (EntityType.DEVICE.equals(entityId.getEntityType()) || EntityType.ASSET.equals(entityId.getEntityType())) { Futures.addCallback(this.tbEntityViewService.findEntityViewsByTenantIdAndEntityIdAsync(tenantId, entityId), - new FutureCallback>() { + new FutureCallback<>() { @Override public void onSuccess(@Nullable List result) { if (result != null && !result.isEmpty()) { @@ -212,7 +213,7 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer } } if (!entityViewLatest.isEmpty()) { - saveLatestAndNotify(tenantId, entityView.getId(), entityViewLatest, new FutureCallback() { + saveLatestAndNotify(tenantId, entityView.getId(), entityViewLatest, new FutureCallback<>() { @Override public void onSuccess(@Nullable Void tmp) { } From 316a09854cb23918afe3610a941e86b00bfed547 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Sun, 12 Nov 2023 22:15:57 +0100 Subject: [PATCH 106/252] implemented device actor delete msg and fixed assign to tenant --- .../server/actors/device/DeviceActor.java | 3 ++ .../server/actors/tenant/TenantActor.java | 14 ++++++++ .../DefaultTbNotificationEntityService.java | 3 +- .../device/DefaultTbDeviceService.java | 2 ++ .../queue/DefaultTbClusterService.java | 15 +++++--- .../server/service/queue/ProtoUtils.java | 22 ++++++++++++ .../server/cluster/TbClusterService.java | 4 ++- common/cluster-api/src/main/proto/queue.proto | 8 +++++ .../server/common/msg/MsgType.java | 2 ++ .../msg/rule/engine/DeviceDeleteMsg.java | 36 +++++++++++++++++++ 10 files changed, 103 insertions(+), 6 deletions(-) create mode 100644 common/message/src/main/java/org/thingsboard/server/common/msg/rule/engine/DeviceDeleteMsg.java diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java index 48425d58db..d69b9aec0b 100644 --- a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java +++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java @@ -64,6 +64,9 @@ public class DeviceActor extends ContextAwareActor { case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG: processor.processAttributesUpdate((DeviceAttributesEventNotificationMsg) msg); break; + case DEVICE_DELETE_TO_DEVICE_ACTOR_MSG: + ctx.stop(ctx.getSelf()); + break; case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG: processor.processCredentialsUpdate(msg); break; diff --git a/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java b/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java index c9f5448df3..f508d967c9 100644 --- a/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java +++ b/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java @@ -53,10 +53,13 @@ import org.thingsboard.server.common.msg.queue.PartitionChangeMsg; import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg; import org.thingsboard.server.common.msg.queue.RuleEngineException; import org.thingsboard.server.common.msg.queue.ServiceType; +import org.thingsboard.server.common.msg.rule.engine.DeviceDeleteMsg; import org.thingsboard.server.service.edge.rpc.EdgeRpcService; import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper; +import java.util.HashSet; import java.util.List; +import java.util.Set; @Slf4j public class TenantActor extends RuleChainManagerActor { @@ -65,8 +68,11 @@ public class TenantActor extends RuleChainManagerActor { private boolean isCore; private ApiUsageState apiUsageState; + private Set deletedDevices; + private TenantActor(ActorSystemContext systemContext, TenantId tenantId) { super(systemContext, tenantId); + this.deletedDevices = new HashSet<>(); } boolean cantFindTenant = false; @@ -221,6 +227,10 @@ public class TenantActor extends RuleChainManagerActor { if (!isCore) { log.warn("RECEIVED INVALID MESSAGE: {}", msg); } + if (deletedDevices.contains(msg.getDeviceId())) { + log.warn("RECEIVED MESSAGE FOR DELETED DEVICE: {}", msg); + return; + } TbActorRef deviceActor = getOrCreateDeviceActor(msg.getDeviceId()); if (priority) { deviceActor.tellWithHighPriority(msg); @@ -249,6 +259,10 @@ public class TenantActor extends RuleChainManagerActor { Edge edge = systemContext.getEdgeService().findEdgeById(tenantId, edgeId); edgeRpcService.updateEdge(tenantId, edge); } + } else if (msg.getEntityId().getEntityType() == EntityType.DEVICE && ComponentLifecycleEvent.DELETED == msg.getEvent()) { + DeviceId deviceId = (DeviceId) msg.getEntityId(); + onToDeviceActorMsg(new DeviceDeleteMsg(tenantId, deviceId), true); + deletedDevices.add(deviceId); } else if (isRuleEngine) { TbActorRef target = getEntityActorRef(msg.getEntityId()); if (target != null) { diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java index 72e8f44020..8682d18a62 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java @@ -112,7 +112,7 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS public void notifyDeleteDevice(TenantId tenantId, DeviceId deviceId, CustomerId customerId, Device device, User user, Object... additionalInfo) { gatewayNotificationsService.onDeviceDeleted(device); - tbClusterService.onDeviceDeleted(device, null); + tbClusterService.onDeviceDeleted(tenantId, device, null); logEntityAction(tenantId, deviceId, device, customerId, ActionType.DELETED, user, additionalInfo); } @@ -126,6 +126,7 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS @Override public void notifyAssignDeviceToTenant(TenantId tenantId, TenantId newTenantId, DeviceId deviceId, CustomerId customerId, Device device, Tenant tenant, User user, Object... additionalInfo) { + tbClusterService.onDeviceAssignedToTenant(tenantId, device); logEntityAction(tenantId, deviceId, device, customerId, ActionType.ASSIGNED_TO_TENANT, user, additionalInfo); pushAssignedFromNotification(tenant, newTenantId, device); } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java index ac2d656ba6..a3d04f6344 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java @@ -232,6 +232,8 @@ public class DefaultTbDeviceService extends AbstractTbEntityService implements T Tenant tenant = tenantService.findTenantById(tenantId); Device assignedDevice = deviceService.assignDeviceToTenant(newTenantId, device); + + notificationEntityService.notifyAssignDeviceToTenant(tenantId, newTenantId, deviceId, assignedDevice.getCustomerId(), assignedDevice, tenant, user, newTenantId.toString(), newTenant.getName()); diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java index 103d2b68e2..75a1baa5c6 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java @@ -308,10 +308,17 @@ public class DefaultTbClusterService implements TbClusterService { } @Override - public void onDeviceDeleted(Device device, TbQueueCallback callback) { - broadcastEntityDeleteToTransport(device.getTenantId(), device.getId(), device.getName(), callback); - sendDeviceStateServiceEvent(device.getTenantId(), device.getId(), false, false, true); - broadcastEntityStateChangeEvent(device.getTenantId(), device.getId(), ComponentLifecycleEvent.DELETED); + public void onDeviceDeleted(TenantId tenantId, Device device, TbQueueCallback callback) { + DeviceId deviceId = device.getId(); + broadcastEntityDeleteToTransport(tenantId, deviceId, device.getName(), callback); + sendDeviceStateServiceEvent(tenantId, deviceId, false, false, true); + broadcastEntityStateChangeEvent(tenantId, deviceId, ComponentLifecycleEvent.DELETED); + } + + @Override + public void onDeviceAssignedToTenant(TenantId oldTenantId, Device device) { + onDeviceDeleted(oldTenantId, device, null); + sendDeviceStateServiceEvent(device.getTenantId(), device.getId(), true, false, false); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/queue/ProtoUtils.java b/application/src/main/java/org/thingsboard/server/service/queue/ProtoUtils.java index 76a8e3e89a..8986ca8b51 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/ProtoUtils.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/ProtoUtils.java @@ -44,6 +44,7 @@ import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponseActorMsg; import org.thingsboard.server.common.msg.rpc.RemoveRpcActorMsg; import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequestActorMsg; +import org.thingsboard.server.common.msg.rule.engine.DeviceDeleteMsg; import org.thingsboard.server.common.msg.rule.engine.DeviceAttributesEventNotificationMsg; import org.thingsboard.server.common.msg.rule.engine.DeviceCredentialsUpdateNotificationMsg; import org.thingsboard.server.common.msg.rule.engine.DeviceEdgeUpdateMsg; @@ -377,6 +378,21 @@ public class ProtoUtils { ); } + private static TransportProtos.DeviceDeletedMsgProto toProto(DeviceDeleteMsg msg) { + return TransportProtos.DeviceDeletedMsgProto.newBuilder() + .setTenantIdMSB(msg.getTenantId().getId().getMostSignificantBits()) + .setTenantIdLSB(msg.getTenantId().getId().getLeastSignificantBits()) + .setDeviceIdMSB(msg.getDeviceId().getId().getMostSignificantBits()) + .setDeviceIdLSB(msg.getDeviceId().getId().getLeastSignificantBits()) + .build(); + } + + private static DeviceDeleteMsg fromProto(TransportProtos.DeviceDeletedMsgProto proto) { + return new DeviceDeleteMsg( + TenantId.fromUUID(new UUID(proto.getTenantIdMSB(), proto.getTenantIdLSB())), + new DeviceId(new UUID(proto.getDeviceIdMSB(), proto.getDeviceIdLSB()))); + } + public static TransportProtos.ToDeviceActorNotificationMsgProto toProto(ToDeviceActorNotificationMsg msg) { if (msg instanceof DeviceEdgeUpdateMsg) { DeviceEdgeUpdateMsg updateMsg = (DeviceEdgeUpdateMsg) msg; @@ -406,6 +422,10 @@ public class ProtoUtils { RemoveRpcActorMsg updateMsg = (RemoveRpcActorMsg) msg; TransportProtos.RemoveRpcActorMsgProto proto = toProto(updateMsg); return TransportProtos.ToDeviceActorNotificationMsgProto.newBuilder().setRemoveRpcActorMsg(proto).build(); + } else if (msg instanceof DeviceDeleteMsg) { + DeviceDeleteMsg updateMsg = (DeviceDeleteMsg) msg; + TransportProtos.DeviceDeletedMsgProto proto = toProto(updateMsg); + return TransportProtos.ToDeviceActorNotificationMsgProto.newBuilder().setDeviceAssignToTenantMsgProto(proto).build(); } return null; } @@ -425,6 +445,8 @@ public class ProtoUtils { return fromProto(proto.getFromDeviceRpcResponseMsg()); } else if (proto.hasRemoveRpcActorMsg()) { return fromProto(proto.getRemoveRpcActorMsg()); + } else if (proto.hasDeviceAssignToTenantMsgProto()) { + return fromProto(proto.getDeviceAssignToTenantMsgProto()); } return null; } diff --git a/common/cluster-api/src/main/java/org/thingsboard/server/cluster/TbClusterService.java b/common/cluster-api/src/main/java/org/thingsboard/server/cluster/TbClusterService.java index 5b1eb1ae4f..5c3b9076ff 100644 --- a/common/cluster-api/src/main/java/org/thingsboard/server/cluster/TbClusterService.java +++ b/common/cluster-api/src/main/java/org/thingsboard/server/cluster/TbClusterService.java @@ -83,7 +83,9 @@ public interface TbClusterService extends TbQueueClusterService { void onDeviceUpdated(Device device, Device old); - void onDeviceDeleted(Device device, TbQueueCallback callback); + void onDeviceDeleted(TenantId tenantId, Device device, TbQueueCallback callback); + + void onDeviceAssignedToTenant(TenantId oldTenantId, Device device); void onResourceChange(TbResource resource, TbQueueCallback callback); diff --git a/common/cluster-api/src/main/proto/queue.proto b/common/cluster-api/src/main/proto/queue.proto index 6116411b2e..5000e3025f 100644 --- a/common/cluster-api/src/main/proto/queue.proto +++ b/common/cluster-api/src/main/proto/queue.proto @@ -1004,6 +1004,13 @@ message RemoveRpcActorMsgProto { int64 deviceIdLSB = 6; } +message DeviceDeletedMsgProto { + int64 tenantIdMSB = 1; + int64 tenantIdLSB = 2; + int64 deviceIdMSB = 3; + int64 deviceIdLSB = 4; +} + message ToDeviceActorNotificationMsgProto { DeviceEdgeUpdateMsgProto deviceEdgeUpdateMsg = 1; DeviceNameOrTypeUpdateMsgProto deviceNameOrTypeMsg = 2; @@ -1012,6 +1019,7 @@ message ToDeviceActorNotificationMsgProto { ToDeviceRpcRequestActorMsgProto toDeviceRpcRequestMsg = 5; FromDeviceRpcResponseActorMsgProto fromDeviceRpcResponseMsg = 6; RemoveRpcActorMsgProto removeRpcActorMsg = 7; + DeviceDeletedMsgProto deviceAssignToTenantMsgProto = 8; } /** diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/MsgType.java b/common/message/src/main/java/org/thingsboard/server/common/msg/MsgType.java index 0a39d8d51b..c3d3623d8e 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/MsgType.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/MsgType.java @@ -93,6 +93,8 @@ public enum MsgType { DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG, + DEVICE_DELETE_TO_DEVICE_ACTOR_MSG, + DEVICE_EDGE_UPDATE_TO_DEVICE_ACTOR_MSG, DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG, diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/rule/engine/DeviceDeleteMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/rule/engine/DeviceDeleteMsg.java new file mode 100644 index 0000000000..1bdab7409b --- /dev/null +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/rule/engine/DeviceDeleteMsg.java @@ -0,0 +1,36 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.common.msg.rule.engine; + +import lombok.Data; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.msg.MsgType; +import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg; + +@Data +public class DeviceDeleteMsg implements ToDeviceActorNotificationMsg { + + private static final long serialVersionUID = 4679029228395462172L; + + private final TenantId tenantId; + private final DeviceId deviceId; + + @Override + public MsgType getMsgType() { + return MsgType.DEVICE_DELETE_TO_DEVICE_ACTOR_MSG; + } +} From deb19d24c0b4ebdcf8fdf07c5e94d6397fb413ab Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Mon, 13 Nov 2023 10:57:27 +0100 Subject: [PATCH 107/252] added verification for the corresponding test --- .../controller/DeviceControllerTest.java | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/DeviceControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/DeviceControllerTest.java index 778a11a552..b6afaa34e1 100644 --- a/application/src/test/java/org/thingsboard/server/controller/DeviceControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/DeviceControllerTest.java @@ -51,7 +51,6 @@ import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.alarm.AlarmSeverity; -import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.id.CustomerId; @@ -73,7 +72,9 @@ import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.DeviceCredentialsValidationException; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.service.DaoSqlTest; +import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.service.gateway_device.GatewayNotificationsService; +import org.thingsboard.server.service.state.DeviceStateService; import java.util.ArrayList; import java.util.List; @@ -82,6 +83,9 @@ import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.containsString; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -106,6 +110,9 @@ public class DeviceControllerTest extends AbstractControllerTest { @SpyBean private GatewayNotificationsService gatewayNotificationsService; + @SpyBean + private DeviceStateService deviceStateService; + @Autowired private DeviceDao deviceDao; @@ -1340,6 +1347,28 @@ public class DeviceControllerTest extends AbstractControllerTest { ActionType.ASSIGNED_TO_TENANT, savedDifferentTenant.getId().getId().toString(), savedDifferentTenant.getTitle()); testNotificationUpdateGatewayNever(); + Mockito.verify(deviceStateService, times(1)).onQueueMsg( + argThat(proto -> + proto.getTenantIdMSB() == savedTenant.getUuidId().getMostSignificantBits() && + proto.getTenantIdLSB() == savedTenant.getUuidId().getLeastSignificantBits() && + proto.getDeviceIdMSB() == savedDevice.getUuidId().getMostSignificantBits() && + proto.getDeviceIdLSB() == savedDevice.getUuidId().getLeastSignificantBits() && + proto.getDeleted() + ), + any() + ); + + Mockito.verify(deviceStateService, times(1)).onQueueMsg( + argThat(proto -> + proto.getTenantIdMSB() == savedDifferentTenant.getUuidId().getMostSignificantBits() && + proto.getTenantIdLSB() == savedDifferentTenant.getUuidId().getLeastSignificantBits() && + proto.getDeviceIdMSB() == savedDevice.getUuidId().getMostSignificantBits() && + proto.getDeviceIdLSB() == savedDevice.getUuidId().getLeastSignificantBits() && + proto.getAdded() + ), + any() + ); + login("tenant9@thingsboard.org", "testPassword1"); Device foundDevice1 = doGet("/api/device/" + assignedDevice.getId().getId(), Device.class); From b14a304473c1bc8f8f8a859ef8cae2afbe153c3b Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Tue, 26 Sep 2023 17:05:30 +0300 Subject: [PATCH 108/252] Refactoring for edge proto to send entity serialized in order not to update everytime entity changes its structure --- .../service/edge/rpc/EdgeGrpcSession.java | 44 +- .../AdminSettingsMsgConstructor.java | 11 +- .../rpc/constructor/AlarmMsgConstructor.java | 12 +- .../rpc/constructor/AssetMsgConstructor.java | 12 +- .../AssetProfileMsgConstructor.java | 13 +- .../constructor/CustomerMsgConstructor.java | 12 +- .../constructor/DashboardMsgConstructor.java | 12 +- .../rpc/constructor/DeviceMsgConstructor.java | 20 +- .../DeviceProfileMsgConstructor.java | 13 +- .../constructor/EntityViewMsgConstructor.java | 38 +- .../constructor/OtaPackageMsgConstructor.java | 12 +- .../rpc/constructor/QueueMsgConstructor.java | 13 +- .../constructor/RelationMsgConstructor.java | 10 +- .../constructor/RuleChainMsgConstructor.java | 16 +- .../rpc/constructor/TenantMsgConstructor.java | 10 +- .../TenantProfileMsgConstructor.java | 7 + .../rpc/constructor/UserMsgConstructor.java | 20 +- .../constructor/WidgetTypeMsgConstructor.java | 8 + .../WidgetsBundleMsgConstructor.java | 12 +- .../AbstractRuleChainMetadataConstructor.java | 13 +- .../rule/RuleChainMetadataConstructor.java | 4 +- .../processor/alarm/AlarmEdgeProcessor.java | 8 +- .../processor/alarm/BaseAlarmProcessor.java | 49 ++- .../processor/asset/AssetEdgeProcessor.java | 16 +- .../asset/AssetProfileEdgeProcessor.java | 27 +- .../processor/asset/BaseAssetProcessor.java | 47 ++- .../asset/BaseAssetProfileProcessor.java | 52 ++- .../customer/CustomerEdgeProcessor.java | 5 +- .../dashboard/BaseDashboardProcessor.java | 65 +-- .../dashboard/DashboardEdgeProcessor.java | 17 +- .../processor/device/BaseDeviceProcessor.java | 105 +++-- .../device/BaseDeviceProfileProcessor.java | 92 ++-- .../processor/device/DeviceEdgeProcessor.java | 20 +- .../device/DeviceProfileEdgeProcessor.java | 27 +- .../entityview/BaseEntityViewProcessor.java | 47 ++- .../entityview/EntityViewEdgeProcessor.java | 17 +- .../ota/OtaPackageEdgeProcessor.java | 5 +- .../processor/queue/QueueEdgeProcessor.java | 5 +- .../relation/BaseRelationProcessor.java | 46 +- .../relation/RelationEdgeProcessor.java | 5 +- .../rule/RuleChainEdgeProcessor.java | 4 +- .../settings/AdminSettingsEdgeProcessor.java | 6 +- .../processor/tenant/TenantEdgeProcessor.java | 2 +- .../rpc/processor/user/UserEdgeProcessor.java | 7 +- .../widget/WidgetBundleEdgeProcessor.java | 5 +- .../edge/rpc/utils/EdgeVersionUtils.java | 4 + .../server/controller/EdgeControllerTest.java | 109 +++-- .../server/edge/AbstractEdgeTest.java | 61 +-- .../server/edge/AlarmEdgeTest.java | 60 +-- .../server/edge/AssetEdgeTest.java | 67 +-- .../server/edge/AssetProfileEdgeTest.java | 57 +-- .../server/edge/CustomerEdgeTest.java | 11 +- .../server/edge/DashboardEdgeTest.java | 63 +-- .../server/edge/DeviceEdgeTest.java | 131 +++--- .../server/edge/DeviceProfileEdgeTest.java | 108 +++-- .../org/thingsboard/server/edge/EdgeTest.java | 6 +- .../server/edge/EntityViewEdgeTest.java | 78 ++-- .../server/edge/OtaPackageEdgeTest.java | 63 ++- .../server/edge/QueueEdgeTest.java | 37 +- .../server/edge/RelationEdgeTest.java | 63 ++- .../server/edge/RuleChainEdgeTest.java | 32 +- .../server/edge/TenantEdgeTest.java | 31 +- .../server/edge/TenantProfileEdgeTest.java | 11 +- .../thingsboard/server/edge/UserEdgeTest.java | 63 +-- .../server/edge/WidgetEdgeTest.java | 28 +- .../server/common/data/DashboardInfo.java | 2 + .../common/data/id/UserCredentialsId.java | 6 +- common/edge-api/src/main/proto/edge.proto | 399 ++++++++++-------- .../thingsboard/common/util/JacksonUtil.java | 12 + .../common/util/JacksonUtilTest.java | 23 + 70 files changed, 1508 insertions(+), 1008 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java index 7318b1eb24..277b8a3f34 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java @@ -623,37 +623,37 @@ public final class EdgeGrpcSession implements Closeable { case ASSET: return ctx.getAssetProcessor().convertAssetEventToDownlink(edgeEvent, this.edge.getId(), this.edgeVersion); case ENTITY_VIEW: - return ctx.getEntityViewProcessor().convertEntityViewEventToDownlink(edgeEvent); + return ctx.getEntityViewProcessor().convertEntityViewEventToDownlink(edgeEvent, this.edgeVersion); case DASHBOARD: - return ctx.getDashboardProcessor().convertDashboardEventToDownlink(edgeEvent); + return ctx.getDashboardProcessor().convertDashboardEventToDownlink(edgeEvent, this.edgeVersion); case CUSTOMER: - return ctx.getCustomerProcessor().convertCustomerEventToDownlink(edgeEvent); + return ctx.getCustomerProcessor().convertCustomerEventToDownlink(edgeEvent, this.edgeVersion); case RULE_CHAIN: - return ctx.getRuleChainProcessor().convertRuleChainEventToDownlink(edgeEvent); + return ctx.getRuleChainProcessor().convertRuleChainEventToDownlink(edgeEvent, this.edgeVersion); case RULE_CHAIN_METADATA: return ctx.getRuleChainProcessor().convertRuleChainMetadataEventToDownlink(edgeEvent, this.edgeVersion); case ALARM: - return ctx.getAlarmProcessor().convertAlarmEventToDownlink(edgeEvent); + return ctx.getAlarmProcessor().convertAlarmEventToDownlink(edgeEvent, this.edgeVersion); case USER: - return ctx.getUserProcessor().convertUserEventToDownlink(edgeEvent); + return ctx.getUserProcessor().convertUserEventToDownlink(edgeEvent, this.edgeVersion); case RELATION: - return ctx.getRelationProcessor().convertRelationEventToDownlink(edgeEvent); + return ctx.getRelationProcessor().convertRelationEventToDownlink(edgeEvent, this.edgeVersion); case WIDGETS_BUNDLE: - return ctx.getWidgetBundleProcessor().convertWidgetsBundleEventToDownlink(edgeEvent); + return ctx.getWidgetBundleProcessor().convertWidgetsBundleEventToDownlink(edgeEvent, this.edgeVersion); case WIDGET_TYPE: return ctx.getWidgetTypeProcessor().convertWidgetTypeEventToDownlink(edgeEvent, this.edgeVersion); case ADMIN_SETTINGS: - return ctx.getAdminSettingsProcessor().convertAdminSettingsEventToDownlink(edgeEvent); + return ctx.getAdminSettingsProcessor().convertAdminSettingsEventToDownlink(edgeEvent, this.edgeVersion); case OTA_PACKAGE: - return ctx.getOtaPackageEdgeProcessor().convertOtaPackageEventToDownlink(edgeEvent); + return ctx.getOtaPackageEdgeProcessor().convertOtaPackageEventToDownlink(edgeEvent, this.edgeVersion); case TB_RESOURCE: return ctx.getResourceEdgeProcessor().convertResourceEventToDownlink(edgeEvent); case QUEUE: - return ctx.getQueueEdgeProcessor().convertQueueEventToDownlink(edgeEvent); + return ctx.getQueueEdgeProcessor().convertQueueEventToDownlink(edgeEvent, this.edgeVersion); case TENANT: - return ctx.getTenantEdgeProcessor().convertTenantEventToDownlink(edgeEvent, this.getEdgeVersion()); + return ctx.getTenantEdgeProcessor().convertTenantEventToDownlink(edgeEvent, this.edgeVersion); case TENANT_PROFILE: - return ctx.getTenantProfileEdgeProcessor().convertTenantProfileEventToDownlink(edgeEvent, this.getEdgeVersion()); + return ctx.getTenantProfileEdgeProcessor().convertTenantProfileEventToDownlink(edgeEvent, this.edgeVersion); default: log.warn("[{}] Unsupported edge event type [{}]", this.tenantId, edgeEvent); return null; @@ -670,47 +670,47 @@ public final class EdgeGrpcSession implements Closeable { } if (uplinkMsg.getDeviceProfileUpdateMsgCount() > 0) { for (DeviceProfileUpdateMsg deviceProfileUpdateMsg : uplinkMsg.getDeviceProfileUpdateMsgList()) { - result.add(ctx.getDeviceProfileProcessor().processDeviceProfileMsgFromEdge(edge.getTenantId(), edge, deviceProfileUpdateMsg)); + result.add(ctx.getDeviceProfileProcessor().processDeviceProfileMsgFromEdge(edge.getTenantId(), edge, deviceProfileUpdateMsg, this.edgeVersion)); } } if (uplinkMsg.getDeviceUpdateMsgCount() > 0) { for (DeviceUpdateMsg deviceUpdateMsg : uplinkMsg.getDeviceUpdateMsgList()) { - result.add(ctx.getDeviceProcessor().processDeviceMsgFromEdge(edge.getTenantId(), edge, deviceUpdateMsg)); + result.add(ctx.getDeviceProcessor().processDeviceMsgFromEdge(edge.getTenantId(), edge, deviceUpdateMsg, this.edgeVersion)); } } if (uplinkMsg.getDeviceCredentialsUpdateMsgCount() > 0) { for (DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg : uplinkMsg.getDeviceCredentialsUpdateMsgList()) { - result.add(ctx.getDeviceProcessor().processDeviceCredentialsMsgFromEdge(edge.getTenantId(), edge.getId(), deviceCredentialsUpdateMsg)); + result.add(ctx.getDeviceProcessor().processDeviceCredentialsMsgFromEdge(edge.getTenantId(), edge.getId(), deviceCredentialsUpdateMsg, this.edgeVersion)); } } if (uplinkMsg.getAssetProfileUpdateMsgCount() > 0) { for (AssetProfileUpdateMsg assetProfileUpdateMsg : uplinkMsg.getAssetProfileUpdateMsgList()) { - result.add(ctx.getAssetProfileProcessor().processAssetProfileMsgFromEdge(edge.getTenantId(), edge, assetProfileUpdateMsg)); + result.add(ctx.getAssetProfileProcessor().processAssetProfileMsgFromEdge(edge.getTenantId(), edge, assetProfileUpdateMsg, this.edgeVersion)); } } if (uplinkMsg.getAssetUpdateMsgCount() > 0) { for (AssetUpdateMsg assetUpdateMsg : uplinkMsg.getAssetUpdateMsgList()) { - result.add(ctx.getAssetProcessor().processAssetMsgFromEdge(edge.getTenantId(), edge, assetUpdateMsg)); + result.add(ctx.getAssetProcessor().processAssetMsgFromEdge(edge.getTenantId(), edge, assetUpdateMsg, this.edgeVersion)); } } if (uplinkMsg.getAlarmUpdateMsgCount() > 0) { for (AlarmUpdateMsg alarmUpdateMsg : uplinkMsg.getAlarmUpdateMsgList()) { - result.add(ctx.getAlarmProcessor().processAlarmMsgFromEdge(edge.getTenantId(), edge.getId(), alarmUpdateMsg)); + result.add(ctx.getAlarmProcessor().processAlarmMsgFromEdge(edge.getTenantId(), edge.getId(), alarmUpdateMsg, this.edgeVersion)); } } if (uplinkMsg.getEntityViewUpdateMsgCount() > 0) { for (EntityViewUpdateMsg entityViewUpdateMsg : uplinkMsg.getEntityViewUpdateMsgList()) { - result.add(ctx.getEntityViewProcessor().processEntityViewMsgFromEdge(edge.getTenantId(), edge, entityViewUpdateMsg)); + result.add(ctx.getEntityViewProcessor().processEntityViewMsgFromEdge(edge.getTenantId(), edge, entityViewUpdateMsg, this.edgeVersion)); } } if (uplinkMsg.getRelationUpdateMsgCount() > 0) { for (RelationUpdateMsg relationUpdateMsg : uplinkMsg.getRelationUpdateMsgList()) { - result.add(ctx.getRelationProcessor().processRelationMsgFromEdge(edge.getTenantId(), edge, relationUpdateMsg)); + result.add(ctx.getRelationProcessor().processRelationMsgFromEdge(edge.getTenantId(), edge, relationUpdateMsg, this.edgeVersion)); } } if (uplinkMsg.getDashboardUpdateMsgCount() > 0) { for (DashboardUpdateMsg dashboardUpdateMsg : uplinkMsg.getDashboardUpdateMsgList()) { - result.add(ctx.getDashboardProcessor().processDashboardMsgFromEdge(edge.getTenantId(), edge, dashboardUpdateMsg)); + result.add(ctx.getDashboardProcessor().processDashboardMsgFromEdge(edge.getTenantId(), edge, dashboardUpdateMsg, this.edgeVersion)); } } if (uplinkMsg.getResourceUpdateMsgCount() > 0) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AdminSettingsMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AdminSettingsMsgConstructor.java index 79754b867c..888f203fb6 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AdminSettingsMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AdminSettingsMsgConstructor.java @@ -19,13 +19,21 @@ import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.gen.edge.v1.AdminSettingsUpdateMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; @Component @TbCoreComponent public class AdminSettingsMsgConstructor { - public AdminSettingsUpdateMsg constructAdminSettingsUpdateMsg(AdminSettings adminSettings) { + public AdminSettingsUpdateMsg constructAdminSettingsUpdateMsg(AdminSettings adminSettings, EdgeVersion edgeVersion) { + return EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion) + ? constructDeprecatedWidgetTypeUpdateMsg(adminSettings) + : AdminSettingsUpdateMsg.newBuilder().setEntity(JacksonUtil.toString(adminSettings)).build(); + } + + private AdminSettingsUpdateMsg constructDeprecatedWidgetTypeUpdateMsg(AdminSettings adminSettings) { AdminSettingsUpdateMsg.Builder builder = AdminSettingsUpdateMsg.newBuilder() .setKey(adminSettings.getKey()) .setJsonValue(JacksonUtil.toString(adminSettings.getJsonValue())); @@ -34,5 +42,4 @@ public class AdminSettingsMsgConstructor { } return builder.build(); } - } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AlarmMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AlarmMsgConstructor.java index 15d7c27ff6..2fbb4e6cd6 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AlarmMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AlarmMsgConstructor.java @@ -19,14 +19,24 @@ import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; @Component @TbCoreComponent public class AlarmMsgConstructor { - public AlarmUpdateMsg constructAlarmUpdatedMsg(UpdateMsgType msgType, Alarm alarm, String entityName) { + public AlarmUpdateMsg constructAlarmUpdatedMsg(UpdateMsgType msgType, Alarm alarm, String entityName, EdgeVersion edgeVersion) { + return EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion) + ? constructDeprecatedAlarmUpdatedMsg(msgType, alarm, entityName) + : AlarmUpdateMsg.newBuilder().setMsgType(msgType).setOriginatorName(entityName).setEntity(JacksonUtil.toString(alarm)) + .setIdMSB(alarm.getId().getId().getMostSignificantBits()) + .setIdLSB(alarm.getId().getId().getLeastSignificantBits()).build(); + } + + private AlarmUpdateMsg constructDeprecatedAlarmUpdatedMsg(UpdateMsgType msgType, Alarm alarm, String entityName) { AlarmUpdateMsg.Builder builder = AlarmUpdateMsg.newBuilder() .setMsgType(msgType) .setIdMSB(alarm.getId().getId().getMostSignificantBits()) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetMsgConstructor.java index 86ffc6dd5c..e3ffb71e47 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetMsgConstructor.java @@ -20,14 +20,24 @@ import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; @Component @TbCoreComponent public class AssetMsgConstructor { - public AssetUpdateMsg constructAssetUpdatedMsg(UpdateMsgType msgType, Asset asset) { + public AssetUpdateMsg constructAssetUpdatedMsg(UpdateMsgType msgType, Asset asset, EdgeVersion edgeVersion) { + return EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion) + ? constructDeprecatedAssetUpdateMsg(msgType, asset) + : AssetUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(asset)) + .setIdMSB(asset.getUuidId().getMostSignificantBits()) + .setIdLSB(asset.getUuidId().getLeastSignificantBits()).build(); + } + + private AssetUpdateMsg constructDeprecatedAssetUpdateMsg(UpdateMsgType msgType, Asset asset) { AssetUpdateMsg.Builder builder = AssetUpdateMsg.newBuilder() .setMsgType(msgType) .setIdMSB(asset.getUuidId().getMostSignificantBits()) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetProfileMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetProfileMsgConstructor.java index f5b921a32b..c344adc15c 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetProfileMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetProfileMsgConstructor.java @@ -17,11 +17,14 @@ package org.thingsboard.server.service.edge.rpc.constructor; import com.google.protobuf.ByteString; import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.id.AssetProfileId; import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; import java.nio.charset.StandardCharsets; @@ -29,7 +32,15 @@ import java.nio.charset.StandardCharsets; @TbCoreComponent public class AssetProfileMsgConstructor { - public AssetProfileUpdateMsg constructAssetProfileUpdatedMsg(UpdateMsgType msgType, AssetProfile assetProfile) { + public AssetProfileUpdateMsg constructAssetProfileUpdatedMsg(UpdateMsgType msgType, AssetProfile assetProfile, EdgeVersion edgeVersion) { + return EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion) + ? constructDeprecatedAssetProfileUpdatedMsg(msgType, assetProfile) + : AssetProfileUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(assetProfile)) + .setIdMSB(assetProfile.getId().getId().getMostSignificantBits()) + .setIdLSB(assetProfile.getId().getId().getLeastSignificantBits()).build(); + } + + private AssetProfileUpdateMsg constructDeprecatedAssetProfileUpdatedMsg(UpdateMsgType msgType, AssetProfile assetProfile) { AssetProfileUpdateMsg.Builder builder = AssetProfileUpdateMsg.newBuilder() .setMsgType(msgType) .setIdMSB(assetProfile.getId().getId().getMostSignificantBits()) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/CustomerMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/CustomerMsgConstructor.java index 0465dd5a94..714cb60672 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/CustomerMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/CustomerMsgConstructor.java @@ -20,14 +20,24 @@ import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.gen.edge.v1.CustomerUpdateMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; @Component @TbCoreComponent public class CustomerMsgConstructor { - public CustomerUpdateMsg constructCustomerUpdatedMsg(UpdateMsgType msgType, Customer customer) { + public CustomerUpdateMsg constructCustomerUpdatedMsg(UpdateMsgType msgType, Customer customer, EdgeVersion edgeVersion) { + return EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion) + ? constructDeprecatedCustomerUpdatedMsg(msgType, customer) + : CustomerUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(customer)) + .setIdMSB(customer.getId().getId().getMostSignificantBits()) + .setIdLSB(customer.getId().getId().getLeastSignificantBits()).build(); + } + + private CustomerUpdateMsg constructDeprecatedCustomerUpdatedMsg(UpdateMsgType msgType, Customer customer) { CustomerUpdateMsg.Builder builder = CustomerUpdateMsg.newBuilder() .setMsgType(msgType) .setIdMSB(customer.getId().getId().getMostSignificantBits()) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DashboardMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DashboardMsgConstructor.java index 50c2b46891..76a969e30a 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DashboardMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DashboardMsgConstructor.java @@ -20,14 +20,24 @@ import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; @Component @TbCoreComponent public class DashboardMsgConstructor { - public DashboardUpdateMsg constructDashboardUpdatedMsg(UpdateMsgType msgType, Dashboard dashboard) { + public DashboardUpdateMsg constructDashboardUpdatedMsg(UpdateMsgType msgType, Dashboard dashboard, EdgeVersion edgeVersion) { + return EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion) + ? constructDeprecatedDashboardUpdatedMsg(msgType, dashboard) + : DashboardUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(dashboard)) + .setIdMSB(dashboard.getId().getId().getMostSignificantBits()) + .setIdLSB(dashboard.getId().getId().getLeastSignificantBits()).build(); + } + + private DashboardUpdateMsg constructDeprecatedDashboardUpdatedMsg(UpdateMsgType msgType, Dashboard dashboard) { DashboardUpdateMsg.Builder builder = DashboardUpdateMsg.newBuilder() .setMsgType(msgType) .setIdMSB(dashboard.getId().getId().getMostSignificantBits()) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceMsgConstructor.java index 78b785a5d8..9325fbc424 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceMsgConstructor.java @@ -26,11 +26,13 @@ import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.gen.edge.v1.DeviceCredentialsUpdateMsg; import org.thingsboard.server.gen.edge.v1.DeviceRpcCallMsg; import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.RpcRequestMsg; import org.thingsboard.server.gen.edge.v1.RpcResponseMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.DataDecodingEncodingService; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; import java.util.UUID; @@ -41,7 +43,15 @@ public class DeviceMsgConstructor { @Autowired private DataDecodingEncodingService dataDecodingEncodingService; - public DeviceUpdateMsg constructDeviceUpdatedMsg(UpdateMsgType msgType, Device device) { + public DeviceUpdateMsg constructDeviceUpdatedMsg(UpdateMsgType msgType, Device device, EdgeVersion edgeVersion) { + return EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion) + ? constructDeprecatedDeviceUpdateMsg(msgType, device) + : DeviceUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(device)) + .setIdMSB(device.getId().getId().getMostSignificantBits()) + .setIdLSB(device.getId().getId().getLeastSignificantBits()).build(); + } + + private DeviceUpdateMsg constructDeprecatedDeviceUpdateMsg(UpdateMsgType msgType, Device device) { DeviceUpdateMsg.Builder builder = DeviceUpdateMsg.newBuilder() .setMsgType(msgType) .setIdMSB(device.getId().getId().getMostSignificantBits()) @@ -76,7 +86,13 @@ public class DeviceMsgConstructor { return builder.build(); } - public DeviceCredentialsUpdateMsg constructDeviceCredentialsUpdatedMsg(DeviceCredentials deviceCredentials) { + public DeviceCredentialsUpdateMsg constructDeviceCredentialsUpdatedMsg(DeviceCredentials deviceCredentials, EdgeVersion edgeVersion) { + return EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion) + ? constructDeprecatedDeviceCredentialsUpdatedMsg(deviceCredentials) + : DeviceCredentialsUpdateMsg.newBuilder().setEntity(JacksonUtil.toString(deviceCredentials)).build(); + } + + private DeviceCredentialsUpdateMsg constructDeprecatedDeviceCredentialsUpdatedMsg(DeviceCredentials deviceCredentials) { DeviceCredentialsUpdateMsg.Builder builder = DeviceCredentialsUpdateMsg.newBuilder() .setDeviceIdMSB(deviceCredentials.getDeviceId().getId().getMostSignificantBits()) .setDeviceIdLSB(deviceCredentials.getDeviceId().getId().getLeastSignificantBits()); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceProfileMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceProfileMsgConstructor.java index d8e19ee854..802cadbe87 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceProfileMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceProfileMsgConstructor.java @@ -18,12 +18,15 @@ package org.thingsboard.server.service.edge.rpc.constructor; import com.google.protobuf.ByteString; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.DataDecodingEncodingService; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; import java.nio.charset.StandardCharsets; @@ -34,7 +37,15 @@ public class DeviceProfileMsgConstructor { @Autowired private DataDecodingEncodingService dataDecodingEncodingService; - public DeviceProfileUpdateMsg constructDeviceProfileUpdatedMsg(UpdateMsgType msgType, DeviceProfile deviceProfile) { + public DeviceProfileUpdateMsg constructDeviceProfileUpdatedMsg(UpdateMsgType msgType, DeviceProfile deviceProfile, EdgeVersion edgeVersion) { + return EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion) + ? constructDeprecatedDeviceProfileUpdatedMsg(msgType, deviceProfile) + : DeviceProfileUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(deviceProfile)) + .setIdMSB(deviceProfile.getId().getId().getMostSignificantBits()) + .setIdLSB(deviceProfile.getId().getId().getLeastSignificantBits()).build(); + } + + private DeviceProfileUpdateMsg constructDeprecatedDeviceProfileUpdatedMsg(UpdateMsgType msgType, DeviceProfile deviceProfile) { DeviceProfileUpdateMsg.Builder builder = DeviceProfileUpdateMsg.newBuilder() .setMsgType(msgType) .setIdMSB(deviceProfile.getId().getId().getMostSignificantBits()) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityViewMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityViewMsgConstructor.java index 235199d75e..2f71103ba5 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityViewMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityViewMsgConstructor.java @@ -17,29 +17,30 @@ package org.thingsboard.server.service.edge.rpc.constructor; import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.gen.edge.v1.EdgeEntityType; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.EntityViewUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; @Component @TbCoreComponent public class EntityViewMsgConstructor { - public EntityViewUpdateMsg constructEntityViewUpdatedMsg(UpdateMsgType msgType, EntityView entityView) { - EdgeEntityType entityType; - switch (entityView.getEntityId().getEntityType()) { - case DEVICE: - entityType = EdgeEntityType.DEVICE; - break; - case ASSET: - entityType = EdgeEntityType.ASSET; - break; - default: - throw new RuntimeException("Unsupported entity type [" + entityView.getEntityId().getEntityType() + "]"); - } + public EntityViewUpdateMsg constructEntityViewUpdatedMsg(UpdateMsgType msgType, EntityView entityView, EdgeVersion edgeVersion) { + return EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion) + ? constructEntityViewUpdatedMsgDeprecated(msgType, entityView) + : EntityViewUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(entityView)) + .setIdMSB(entityView.getId().getId().getMostSignificantBits()) + .setIdLSB(entityView.getId().getId().getLeastSignificantBits()).build(); + } + + private EntityViewUpdateMsg constructEntityViewUpdatedMsgDeprecated(UpdateMsgType msgType, EntityView entityView) { + EdgeEntityType edgeEntityType = checkEntityType(entityView.getEntityId().getEntityType()); EntityViewUpdateMsg.Builder builder = EntityViewUpdateMsg.newBuilder() .setMsgType(msgType) .setIdMSB(entityView.getId().getId().getMostSignificantBits()) @@ -48,7 +49,7 @@ public class EntityViewMsgConstructor { .setType(entityView.getType()) .setEntityIdMSB(entityView.getEntityId().getId().getMostSignificantBits()) .setEntityIdLSB(entityView.getEntityId().getId().getLeastSignificantBits()) - .setEntityType(entityType); + .setEntityType(edgeEntityType); if (entityView.getCustomerId() != null) { builder.setCustomerIdMSB(entityView.getCustomerId().getId().getMostSignificantBits()); builder.setCustomerIdLSB(entityView.getCustomerId().getId().getLeastSignificantBits()); @@ -59,6 +60,17 @@ public class EntityViewMsgConstructor { return builder.build(); } + private EdgeEntityType checkEntityType(EntityType entityType) { + switch (entityType) { + case DEVICE: + return EdgeEntityType.DEVICE; + case ASSET: + return EdgeEntityType.ASSET; + default: + throw new RuntimeException("Unsupported entity type [" + entityType + "]"); + } + } + public EntityViewUpdateMsg constructEntityViewDeleteMsg(EntityViewId entityViewId) { return EntityViewUpdateMsg.newBuilder() .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/OtaPackageMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/OtaPackageMsgConstructor.java index 253305a5ab..a946767088 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/OtaPackageMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/OtaPackageMsgConstructor.java @@ -20,15 +20,25 @@ import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.OtaPackage; import org.thingsboard.server.common.data.id.OtaPackageId; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.OtaPackageUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; @Component @TbCoreComponent public class OtaPackageMsgConstructor { - public OtaPackageUpdateMsg constructOtaPackageUpdatedMsg(UpdateMsgType msgType, OtaPackage otaPackage) { + public OtaPackageUpdateMsg constructOtaPackageUpdatedMsg(UpdateMsgType msgType, OtaPackage otaPackage, EdgeVersion edgeVersion) { + return EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion) + ? constructDeprecatedOtaPackageUpdatedMsg(msgType, otaPackage) + : OtaPackageUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(otaPackage)) + .setIdMSB(otaPackage.getId().getId().getMostSignificantBits()) + .setIdLSB(otaPackage.getId().getId().getLeastSignificantBits()).build(); + } + + private OtaPackageUpdateMsg constructDeprecatedOtaPackageUpdatedMsg(UpdateMsgType msgType, OtaPackage otaPackage) { OtaPackageUpdateMsg.Builder builder = OtaPackageUpdateMsg.newBuilder() .setMsgType(msgType) .setIdMSB(otaPackage.getId().getId().getMostSignificantBits()) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/QueueMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/QueueMsgConstructor.java index 2644a5409b..3dbb8d4d05 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/QueueMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/QueueMsgConstructor.java @@ -16,21 +16,32 @@ package org.thingsboard.server.service.edge.rpc.constructor; import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.id.QueueId; import org.thingsboard.server.common.data.queue.ProcessingStrategy; import org.thingsboard.server.common.data.queue.Queue; import org.thingsboard.server.common.data.queue.SubmitStrategy; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.ProcessingStrategyProto; import org.thingsboard.server.gen.edge.v1.QueueUpdateMsg; import org.thingsboard.server.gen.edge.v1.SubmitStrategyProto; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; @Component @TbCoreComponent public class QueueMsgConstructor { - public QueueUpdateMsg constructQueueUpdatedMsg(UpdateMsgType msgType, Queue queue) { + public QueueUpdateMsg constructQueueUpdatedMsg(UpdateMsgType msgType, Queue queue, EdgeVersion edgeVersion) { + return EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion) + ? constructDeprecatedQueueUpdatedMsg(msgType, queue) + : QueueUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(queue)) + .setIdMSB(queue.getId().getId().getMostSignificantBits()) + .setIdLSB(queue.getId().getId().getLeastSignificantBits()).build(); + } + + private QueueUpdateMsg constructDeprecatedQueueUpdatedMsg(UpdateMsgType msgType, Queue queue) { QueueUpdateMsg.Builder builder = QueueUpdateMsg.newBuilder() .setMsgType(msgType) .setIdMSB(queue.getId().getId().getMostSignificantBits()) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RelationMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RelationMsgConstructor.java index 24786a4f7c..31b4ade4b1 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RelationMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RelationMsgConstructor.java @@ -18,15 +18,23 @@ package org.thingsboard.server.service.edge.rpc.constructor; import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; @Component @TbCoreComponent public class RelationMsgConstructor { - public RelationUpdateMsg constructRelationUpdatedMsg(UpdateMsgType msgType, EntityRelation entityRelation) { + public RelationUpdateMsg constructRelationUpdatedMsg(UpdateMsgType msgType, EntityRelation entityRelation, EdgeVersion edgeVersion) { + return EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion) + ? constructDeprecatedRelationUpdatedMsg(msgType, entityRelation) + : RelationUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(entityRelation)).build(); + } + + private RelationUpdateMsg constructDeprecatedRelationUpdatedMsg(UpdateMsgType msgType, EntityRelation entityRelation) { RelationUpdateMsg.Builder builder = RelationUpdateMsg.newBuilder() .setMsgType(msgType) .setFromIdMSB(entityRelation.getFrom().getId().getMostSignificantBits()) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java index 8f91f5d19e..f51ee17c13 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java @@ -29,19 +29,29 @@ import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.edge.rpc.constructor.rule.RuleChainMetadataConstructor; import org.thingsboard.server.service.edge.rpc.constructor.rule.RuleChainMetadataConstructorFactory; +import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; @Component @Slf4j @TbCoreComponent public class RuleChainMsgConstructor { - public RuleChainUpdateMsg constructRuleChainUpdatedMsg(UpdateMsgType msgType, RuleChain ruleChain, boolean isRoot) { + public RuleChainUpdateMsg constructRuleChainUpdatedMsg(UpdateMsgType msgType, RuleChain ruleChain, boolean isRoot, EdgeVersion edgeVersion) { + ruleChain.setRoot(isRoot); + return EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion) + ? constructDeprecatedRuleChainUpdatedMsg(msgType, ruleChain) + : RuleChainUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(ruleChain)) + .setIdMSB(ruleChain.getId().getId().getMostSignificantBits()) + .setIdLSB(ruleChain.getId().getId().getLeastSignificantBits()).build(); + } + + private RuleChainUpdateMsg constructDeprecatedRuleChainUpdatedMsg(UpdateMsgType msgType, RuleChain ruleChain) { RuleChainUpdateMsg.Builder builder = RuleChainUpdateMsg.newBuilder() .setMsgType(msgType) .setIdMSB(ruleChain.getId().getId().getMostSignificantBits()) .setIdLSB(ruleChain.getId().getId().getLeastSignificantBits()) .setName(ruleChain.getName()) - .setRoot(isRoot) + .setRoot(ruleChain.isRoot()) .setDebugMode(ruleChain.isDebugMode()) .setConfiguration(JacksonUtil.toString(ruleChain.getConfiguration())); if (ruleChain.getFirstRuleNodeId() != null) { @@ -57,7 +67,7 @@ public class RuleChainMsgConstructor { EdgeVersion edgeVersion) { RuleChainMetadataConstructor ruleChainMetadataConstructor = RuleChainMetadataConstructorFactory.getByEdgeVersion(edgeVersion); - return ruleChainMetadataConstructor.constructRuleChainMetadataUpdatedMsg(tenantId, msgType, ruleChainMetaData); + return ruleChainMetadataConstructor.constructRuleChainMetadataUpdatedMsg(tenantId, msgType, ruleChainMetaData, edgeVersion); } public RuleChainUpdateMsg constructRuleChainDeleteMsg(RuleChainId ruleChainId) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/TenantMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/TenantMsgConstructor.java index 321a57970a..6f9aee29f5 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/TenantMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/TenantMsgConstructor.java @@ -18,15 +18,23 @@ package org.thingsboard.server.service.edge.rpc.constructor; import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.TenantUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; @Component @TbCoreComponent public class TenantMsgConstructor { - public TenantUpdateMsg constructTenantUpdateMsg(UpdateMsgType msgType, Tenant tenant) { + public TenantUpdateMsg constructTenantUpdateMsg(UpdateMsgType msgType, Tenant tenant, EdgeVersion edgeVersion) { + return EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion) + ? constructDeprecatedTenantUpdateMsg(msgType, tenant) + : TenantUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(tenant)).build(); + } + + private TenantUpdateMsg constructDeprecatedTenantUpdateMsg(UpdateMsgType msgType, Tenant tenant) { TenantUpdateMsg.Builder builder = TenantUpdateMsg.newBuilder() .setMsgType(msgType) .setIdMSB(tenant.getId().getId().getMostSignificantBits()) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/TenantProfileMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/TenantProfileMsgConstructor.java index 9fdb1bf069..f56a781437 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/TenantProfileMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/TenantProfileMsgConstructor.java @@ -18,6 +18,7 @@ package org.thingsboard.server.service.edge.rpc.constructor; import com.google.protobuf.ByteString; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg; @@ -36,6 +37,12 @@ public class TenantProfileMsgConstructor { public TenantProfileUpdateMsg constructTenantProfileUpdateMsg(UpdateMsgType msgType, TenantProfile tenantProfile, EdgeVersion edgeVersion) { ByteString profileData = EdgeVersionUtils.isEdgeVersionOlderThan(edgeVersion, EdgeVersion.V_3_6_1) ? ByteString.empty() : ByteString.copyFrom(dataDecodingEncodingService.encode(tenantProfile.getProfileData())); + return EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion) + ? constructDeprecatedTenantProfileUpdateMsg(msgType, tenantProfile, profileData) + : TenantProfileUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(tenantProfile)).build(); + } + + private TenantProfileUpdateMsg constructDeprecatedTenantProfileUpdateMsg(UpdateMsgType msgType, TenantProfile tenantProfile, ByteString profileData) { TenantProfileUpdateMsg.Builder builder = TenantProfileUpdateMsg.newBuilder() .setMsgType(msgType) .setIdMSB(tenantProfile.getId().getId().getMostSignificantBits()) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/UserMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/UserMsgConstructor.java index fbd3b31688..e0829ead4f 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/UserMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/UserMsgConstructor.java @@ -20,16 +20,26 @@ import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.security.UserCredentials; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.UserCredentialsUpdateMsg; import org.thingsboard.server.gen.edge.v1.UserUpdateMsg; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; @Component @TbCoreComponent public class UserMsgConstructor { - public UserUpdateMsg constructUserUpdatedMsg(UpdateMsgType msgType, User user) { + public UserUpdateMsg constructUserUpdatedMsg(UpdateMsgType msgType, User user, EdgeVersion edgeVersion) { + return EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion) + ? constructDeprecatedUserUpdatedMsg(msgType, user) + : UserUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(user)) + .setIdMSB(user.getId().getId().getMostSignificantBits()) + .setIdLSB(user.getId().getId().getLeastSignificantBits()).build(); + } + + private UserUpdateMsg constructDeprecatedUserUpdatedMsg(UpdateMsgType msgType, User user) { UserUpdateMsg.Builder builder = UserUpdateMsg.newBuilder() .setMsgType(msgType) .setIdMSB(user.getId().getId().getMostSignificantBits()) @@ -59,7 +69,13 @@ public class UserMsgConstructor { .setIdLSB(userId.getId().getLeastSignificantBits()).build(); } - public UserCredentialsUpdateMsg constructUserCredentialsUpdatedMsg(UserCredentials userCredentials) { + public UserCredentialsUpdateMsg constructUserCredentialsUpdatedMsg(UserCredentials userCredentials, EdgeVersion edgeVersion) { + return EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion) + ? constructDeprecatedUserCredentialsUpdatedMsg(userCredentials) + : UserCredentialsUpdateMsg.newBuilder().setEntity(JacksonUtil.toString(userCredentials)).build(); + } + + private UserCredentialsUpdateMsg constructDeprecatedUserCredentialsUpdatedMsg(UserCredentials userCredentials) { UserCredentialsUpdateMsg.Builder builder = UserCredentialsUpdateMsg.newBuilder() .setUserIdMSB(userCredentials.getUserId().getId().getMostSignificantBits()) .setUserIdLSB(userCredentials.getUserId().getId().getLeastSignificantBits()) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetTypeMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetTypeMsgConstructor.java index 17a66a47d4..8d928e7125 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetTypeMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetTypeMsgConstructor.java @@ -33,6 +33,14 @@ import java.util.Arrays; public class WidgetTypeMsgConstructor { public WidgetTypeUpdateMsg constructWidgetTypeUpdateMsg(UpdateMsgType msgType, WidgetTypeDetails widgetTypeDetails, EdgeVersion edgeVersion) { + return EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion) + ? constructDeprecatedWidgetTypeUpdateMsg(msgType, widgetTypeDetails, edgeVersion) + : WidgetTypeUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(widgetTypeDetails)) + .setIdMSB(widgetTypeDetails.getId().getId().getMostSignificantBits()) + .setIdLSB(widgetTypeDetails.getId().getId().getLeastSignificantBits()).build(); + } + + private WidgetTypeUpdateMsg constructDeprecatedWidgetTypeUpdateMsg(UpdateMsgType msgType, WidgetTypeDetails widgetTypeDetails, EdgeVersion edgeVersion) { WidgetTypeUpdateMsg.Builder builder = WidgetTypeUpdateMsg.newBuilder() .setMsgType(msgType) .setIdMSB(widgetTypeDetails.getId().getId().getMostSignificantBits()) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetsBundleMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetsBundleMsgConstructor.java index 5b615ca9f8..e52e44aa1e 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetsBundleMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetsBundleMsgConstructor.java @@ -21,9 +21,11 @@ import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.WidgetsBundleId; import org.thingsboard.server.common.data.widget.WidgetsBundle; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.WidgetsBundleUpdateMsg; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; import java.nio.charset.StandardCharsets; import java.util.List; @@ -32,7 +34,15 @@ import java.util.List; @TbCoreComponent public class WidgetsBundleMsgConstructor { - public WidgetsBundleUpdateMsg constructWidgetsBundleUpdateMsg(UpdateMsgType msgType, WidgetsBundle widgetsBundle, List widgets) { + public WidgetsBundleUpdateMsg constructWidgetsBundleUpdateMsg(UpdateMsgType msgType, WidgetsBundle widgetsBundle, List widgets, EdgeVersion edgeVersion) { + return EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion) + ? constructDeprecatedWidgetsBundleUpdateMsg(msgType, widgetsBundle, widgets) + : WidgetsBundleUpdateMsg.newBuilder().setWidgets(JacksonUtil.toString(widgets)).setEntity(JacksonUtil.toString(widgetsBundle)) + .setMsgType(msgType).setIdMSB(widgetsBundle.getId().getId().getMostSignificantBits()) + .setIdLSB(widgetsBundle.getId().getId().getLeastSignificantBits()).build(); + } + + private WidgetsBundleUpdateMsg constructDeprecatedWidgetsBundleUpdateMsg(UpdateMsgType msgType, WidgetsBundle widgetsBundle, List widgets) { WidgetsBundleUpdateMsg.Builder builder = WidgetsBundleUpdateMsg.newBuilder() .setMsgType(msgType) .setIdMSB(widgetsBundle.getId().getId().getMostSignificantBits()) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/AbstractRuleChainMetadataConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/AbstractRuleChainMetadataConstructor.java index a720d0aab6..997613a42a 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/AbstractRuleChainMetadataConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/AbstractRuleChainMetadataConstructor.java @@ -24,11 +24,13 @@ import org.thingsboard.server.common.data.rule.NodeConnectionInfo; import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo; import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.NodeConnectionInfoProto; import org.thingsboard.server.gen.edge.v1.RuleChainConnectionInfoProto; import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; import org.thingsboard.server.gen.edge.v1.RuleNodeProto; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; import java.util.ArrayList; import java.util.List; @@ -41,7 +43,16 @@ public abstract class AbstractRuleChainMetadataConstructor implements RuleChainM @Override public RuleChainMetadataUpdateMsg constructRuleChainMetadataUpdatedMsg(TenantId tenantId, UpdateMsgType msgType, - RuleChainMetaData ruleChainMetaData) { + RuleChainMetaData ruleChainMetaData, + EdgeVersion edgeVersion) { + return EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion) + ? constructDeprecatedRuleChainMetadataUpdatedMsg(tenantId, msgType, ruleChainMetaData) + : RuleChainMetadataUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(ruleChainMetaData)).build(); + } + + private RuleChainMetadataUpdateMsg constructDeprecatedRuleChainMetadataUpdatedMsg(TenantId tenantId, + UpdateMsgType msgType, + RuleChainMetaData ruleChainMetaData) { try { RuleChainMetadataUpdateMsg.Builder builder = RuleChainMetadataUpdateMsg.newBuilder(); builder.setRuleChainIdMSB(ruleChainMetaData.getRuleChainId().getId().getMostSignificantBits()) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructor.java index 51b7380e48..f80b30b3c4 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructor.java @@ -17,6 +17,7 @@ package org.thingsboard.server.service.edge.rpc.constructor.rule; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.rule.RuleChainMetaData; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; @@ -24,5 +25,6 @@ public interface RuleChainMetadataConstructor { RuleChainMetadataUpdateMsg constructRuleChainMetadataUpdatedMsg(TenantId tenantId, UpdateMsgType msgType, - RuleChainMetaData ruleChainMetaData); + RuleChainMetaData ruleChainMetaData, + EdgeVersion edgeVersion); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/AlarmEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/AlarmEdgeProcessor.java index 882fd90c46..c15b79356e 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/AlarmEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/AlarmEdgeProcessor.java @@ -34,6 +34,7 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg; import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.util.TbCoreComponent; @@ -56,9 +57,9 @@ public class AlarmEdgeProcessor extends BaseAlarmProcessor { } } - public DownlinkMsg convertAlarmEventToDownlink(EdgeEvent edgeEvent) { - AlarmUpdateMsg alarmUpdateMsg = - convertAlarmEventToAlarmMsg(edgeEvent.getTenantId(), edgeEvent.getEntityId(), edgeEvent.getAction(), edgeEvent.getBody()); + public DownlinkMsg convertAlarmEventToDownlink(EdgeEvent edgeEvent, EdgeVersion edgeVersion) { + AlarmUpdateMsg alarmUpdateMsg = convertAlarmEventToAlarmMsg(edgeEvent.getTenantId(), edgeEvent.getEntityId(), + edgeEvent.getAction(), edgeEvent.getBody(), edgeVersion); if (alarmUpdateMsg != null) { return DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) @@ -122,5 +123,4 @@ public class AlarmEdgeProcessor extends BaseAlarmProcessor { } while (pageData != null && pageData.hasNext()); return futures; } - } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java index 3fb086cd52..284b20d71e 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java @@ -37,18 +37,28 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; +import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; import java.util.UUID; @Slf4j public abstract class BaseAlarmProcessor extends BaseEdgeProcessor { - protected ListenableFuture processAlarmMsg(TenantId tenantId, AlarmUpdateMsg alarmUpdateMsg) { - EntityId originatorId = getAlarmOriginator(tenantId, alarmUpdateMsg.getOriginatorName(), - EntityType.valueOf(alarmUpdateMsg.getOriginatorType())); + public ListenableFuture processAlarmMsg(TenantId tenantId, AlarmUpdateMsg alarmUpdateMsg, EdgeVersion edgeVersion) { + log.trace("[{}] processAlarmMsg [{}]", tenantId, alarmUpdateMsg); AlarmId alarmId = new AlarmId(new UUID(alarmUpdateMsg.getIdMSB(), alarmUpdateMsg.getIdLSB())); + boolean isEdgeProtoDeprecated = EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion); + Alarm alarm = isEdgeProtoDeprecated ? createDeprecatedAlarm(tenantId, alarmUpdateMsg) + : JacksonUtil.fromEdgeString(alarmUpdateMsg.getEntity(), Alarm.class); + if (alarm == null) { + throw new RuntimeException("[{" + tenantId + "}] alarmUpdateMsg {" + alarmUpdateMsg + "} cannot be converted to alarm"); + } + EntityType entityType = isEdgeProtoDeprecated ? EntityType.valueOf(alarmUpdateMsg.getOriginatorType()) + : alarm.getOriginator().getEntityType(); + EntityId originatorId = getAlarmOriginator(tenantId, alarmUpdateMsg.getOriginatorName(), entityType); if (originatorId == null) { log.warn("[{}] Originator not found for the alarm msg {}", tenantId, alarmUpdateMsg); return Futures.immediateFuture(null); @@ -58,10 +68,7 @@ public abstract class BaseAlarmProcessor extends BaseEdgeProcessor { switch (alarmUpdateMsg.getMsgType()) { case ENTITY_CREATED_RPC_MESSAGE: case ENTITY_UPDATED_RPC_MESSAGE: - Alarm alarm = new Alarm(); alarm.setId(alarmId); - alarm.setTenantId(tenantId); - alarm.setType(alarmUpdateMsg.getName()); alarm.setOriginator(originatorId); alarm.setSeverity(AlarmSeverity.valueOf(alarmUpdateMsg.getSeverity())); alarm.setStartTs(alarmUpdateMsg.getStartTs()); @@ -82,14 +89,13 @@ public abstract class BaseAlarmProcessor extends BaseEdgeProcessor { case ALARM_ACK_RPC_MESSAGE: Alarm alarmToAck = alarmService.findAlarmById(tenantId, alarmId); if (alarmToAck != null) { - alarmService.acknowledgeAlarm(tenantId, alarmId, alarmUpdateMsg.getAckTs()); + alarmService.acknowledgeAlarm(tenantId, alarmId, alarm.getAckTs()); } break; case ALARM_CLEAR_RPC_MESSAGE: Alarm alarmToClear = alarmService.findAlarmById(tenantId, alarmId); if (alarmToClear != null) { - alarmService.clearAlarm(tenantId, alarmId, alarmUpdateMsg.getClearTs(), - JacksonUtil.toJsonNode(alarmUpdateMsg.getDetails())); + alarmService.clearAlarm(tenantId, alarmId, alarm.getClearTs(), alarm.getDetails()); } break; case ENTITY_DELETED_RPC_MESSAGE: @@ -109,7 +115,24 @@ public abstract class BaseAlarmProcessor extends BaseEdgeProcessor { return Futures.immediateFuture(null); } - protected AlarmUpdateMsg convertAlarmEventToAlarmMsg(TenantId tenantId, UUID entityId, EdgeEventActionType actionType, JsonNode body) { + private Alarm createDeprecatedAlarm(TenantId tenantId, AlarmUpdateMsg alarmUpdateMsg) { + Alarm alarm = new Alarm(); + alarm.setTenantId(tenantId); + alarm.setType(alarmUpdateMsg.getName()); + alarm.setSeverity(AlarmSeverity.valueOf(alarmUpdateMsg.getSeverity())); + alarm.setStartTs(alarmUpdateMsg.getStartTs()); + AlarmStatus alarmStatus = AlarmStatus.valueOf(alarmUpdateMsg.getStatus()); + alarm.setClearTs(alarmUpdateMsg.getClearTs()); + alarm.setPropagate(alarmUpdateMsg.getPropagate()); + alarm.setCleared(alarmStatus.isCleared()); + alarm.setAcknowledged(alarmStatus.isAck()); + alarm.setAckTs(alarmUpdateMsg.getAckTs()); + alarm.setEndTs(alarmUpdateMsg.getEndTs()); + alarm.setDetails(JacksonUtil.toJsonNode(alarmUpdateMsg.getDetails())); + return alarm; + } + + public AlarmUpdateMsg convertAlarmEventToAlarmMsg(TenantId tenantId, UUID entityId, EdgeEventActionType actionType, JsonNode body, EdgeVersion edgeVersion) { AlarmId alarmId = new AlarmId(entityId); UpdateMsgType msgType = getUpdateMsgType(actionType); switch (actionType) { @@ -119,12 +142,14 @@ public abstract class BaseAlarmProcessor extends BaseEdgeProcessor { case ALARM_CLEAR: Alarm alarm = alarmService.findAlarmById(tenantId, alarmId); if (alarm != null) { - return alarmMsgConstructor.constructAlarmUpdatedMsg(msgType, alarm, findOriginatorEntityName(tenantId, alarm)); + return alarmMsgConstructor.constructAlarmUpdatedMsg(msgType, alarm, findOriginatorEntityName(tenantId, alarm), edgeVersion); } break; case DELETED: Alarm deletedAlarm = JacksonUtil.convertValue(body, Alarm.class); - return alarmMsgConstructor.constructAlarmUpdatedMsg(msgType, deletedAlarm, findOriginatorEntityName(tenantId, deletedAlarm)); + if (deletedAlarm != null) { + return alarmMsgConstructor.constructAlarmUpdatedMsg(msgType, deletedAlarm, findOriginatorEntityName(tenantId, deletedAlarm), edgeVersion); + } } return null; } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessor.java index caa03af8a5..8b04d838e8 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessor.java @@ -29,7 +29,6 @@ import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.edge.EdgeEventType; import org.thingsboard.server.common.data.id.AssetId; -import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.msg.TbMsgType; @@ -41,6 +40,7 @@ import org.thingsboard.server.gen.edge.v1.DownlinkMsg; import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; import java.util.UUID; @@ -49,7 +49,7 @@ import java.util.UUID; @TbCoreComponent public class AssetEdgeProcessor extends BaseAssetProcessor { - public ListenableFuture processAssetMsgFromEdge(TenantId tenantId, Edge edge, AssetUpdateMsg assetUpdateMsg) { + public ListenableFuture processAssetMsgFromEdge(TenantId tenantId, Edge edge, AssetUpdateMsg assetUpdateMsg, EdgeVersion edgeVersion) { log.trace("[{}] executing processAssetMsgFromEdge [{}] from edge [{}]", tenantId, assetUpdateMsg, edge.getId()); AssetId assetId = new AssetId(new UUID(assetUpdateMsg.getIdMSB(), assetUpdateMsg.getIdLSB())); try { @@ -58,7 +58,7 @@ public class AssetEdgeProcessor extends BaseAssetProcessor { switch (assetUpdateMsg.getMsgType()) { case ENTITY_CREATED_RPC_MESSAGE: case ENTITY_UPDATED_RPC_MESSAGE: - saveOrUpdateAsset(tenantId, assetId, assetUpdateMsg, edge); + saveOrUpdateAsset(tenantId, assetId, assetUpdateMsg, edge, edgeVersion); return Futures.immediateFuture(null); case ENTITY_DELETED_RPC_MESSAGE: Asset assetToDelete = assetService.findAssetById(tenantId, assetId); @@ -82,9 +82,9 @@ public class AssetEdgeProcessor extends BaseAssetProcessor { } } - private void saveOrUpdateAsset(TenantId tenantId, AssetId assetId, AssetUpdateMsg assetUpdateMsg, Edge edge) { - CustomerId customerId = safeGetCustomerId(assetUpdateMsg.getCustomerIdMSB(), assetUpdateMsg.getCustomerIdLSB()); - Pair resultPair = super.saveOrUpdateAsset(tenantId, assetId, assetUpdateMsg, customerId); + private void saveOrUpdateAsset(TenantId tenantId, AssetId assetId, AssetUpdateMsg assetUpdateMsg, Edge edge, EdgeVersion edgeVersion) { + Pair resultPair = super.saveOrUpdateAsset(tenantId, assetId, assetUpdateMsg, + EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)); Boolean created = resultPair.getFirst(); if (created) { createRelationFromEdge(tenantId, edge.getId(), assetId); @@ -121,14 +121,14 @@ public class AssetEdgeProcessor extends BaseAssetProcessor { if (asset != null && !BaseAssetService.TB_SERVICE_QUEUE.equals(asset.getType())) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); AssetUpdateMsg assetUpdateMsg = - assetMsgConstructor.constructAssetUpdatedMsg(msgType, asset); + assetMsgConstructor.constructAssetUpdatedMsg(msgType, asset, edgeVersion); DownlinkMsg.Builder builder = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addAssetUpdateMsg(assetUpdateMsg); if (UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE.equals(msgType)) { AssetProfile assetProfile = assetProfileService.findAssetProfileById(edgeEvent.getTenantId(), asset.getAssetProfileId()); assetProfile = checkIfAssetProfileDefaultFieldsAssignedToEdge(edgeEvent.getTenantId(), edgeId, assetProfile, edgeVersion); - builder.addAssetProfileUpdateMsg(assetProfileMsgConstructor.constructAssetProfileUpdatedMsg(msgType, assetProfile)); + builder.addAssetProfileUpdateMsg(assetProfileMsgConstructor.constructAssetProfileUpdatedMsg(msgType, assetProfile, edgeVersion)); } downlinkMsg = builder.build(); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetProfileEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetProfileEdgeProcessor.java index fb55edd8c5..c9b9329531 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetProfileEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetProfileEdgeProcessor.java @@ -40,6 +40,7 @@ import org.thingsboard.server.gen.edge.v1.DownlinkMsg; import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; import java.util.UUID; @@ -48,7 +49,7 @@ import java.util.UUID; @TbCoreComponent public class AssetProfileEdgeProcessor extends BaseAssetProfileProcessor { - public ListenableFuture processAssetProfileMsgFromEdge(TenantId tenantId, Edge edge, AssetProfileUpdateMsg assetProfileUpdateMsg) { + public ListenableFuture processAssetProfileMsgFromEdge(TenantId tenantId, Edge edge, AssetProfileUpdateMsg assetProfileUpdateMsg, EdgeVersion edgeVersion) { log.trace("[{}] executing processAssetProfileMsgFromEdge [{}] from edge [{}]", tenantId, assetProfileUpdateMsg, edge.getId()); AssetProfileId assetProfileId = new AssetProfileId(new UUID(assetProfileUpdateMsg.getIdMSB(), assetProfileUpdateMsg.getIdLSB())); try { @@ -57,7 +58,7 @@ public class AssetProfileEdgeProcessor extends BaseAssetProfileProcessor { switch (assetProfileUpdateMsg.getMsgType()) { case ENTITY_CREATED_RPC_MESSAGE: case ENTITY_UPDATED_RPC_MESSAGE: - saveOrUpdateAssetProfile(tenantId, assetProfileId, assetProfileUpdateMsg, edge); + saveOrUpdateAssetProfile(tenantId, assetProfileId, assetProfileUpdateMsg, edge, edgeVersion); return Futures.immediateFuture(null); case ENTITY_DELETED_RPC_MESSAGE: case UNRECOGNIZED: @@ -72,8 +73,9 @@ public class AssetProfileEdgeProcessor extends BaseAssetProfileProcessor { } } - private void saveOrUpdateAssetProfile(TenantId tenantId, AssetProfileId assetProfileId, AssetProfileUpdateMsg assetProfileUpdateMsg, Edge edge) { - Pair resultPair = super.saveOrUpdateAssetProfile(tenantId, assetProfileId, assetProfileUpdateMsg); + private void saveOrUpdateAssetProfile(TenantId tenantId, AssetProfileId assetProfileId, AssetProfileUpdateMsg assetProfileUpdateMsg, Edge edge, EdgeVersion edgeVersion) { + Pair resultPair = super.saveOrUpdateAssetProfile(tenantId, assetProfileId, assetProfileUpdateMsg, + EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)); Boolean created = resultPair.getFirst(); if (created) { createRelationFromEdge(tenantId, edge.getId(), assetProfileId); @@ -107,7 +109,7 @@ public class AssetProfileEdgeProcessor extends BaseAssetProfileProcessor { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); assetProfile = checkIfAssetProfileDefaultFieldsAssignedToEdge(edgeEvent.getTenantId(), edgeId, assetProfile, edgeVersion); AssetProfileUpdateMsg assetProfileUpdateMsg = - assetProfileMsgConstructor.constructAssetProfileUpdatedMsg(msgType, assetProfile); + assetProfileMsgConstructor.constructAssetProfileUpdatedMsg(msgType, assetProfile, edgeVersion); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addAssetProfileUpdateMsg(assetProfileUpdateMsg) @@ -127,19 +129,24 @@ public class AssetProfileEdgeProcessor extends BaseAssetProfileProcessor { } @Override - protected void setDefaultRuleChainId(TenantId tenantId, AssetProfile assetProfile, AssetProfileUpdateMsg assetProfileUpdateMsg) { + protected void setDefaultRuleChainId(TenantId tenantId, AssetProfile assetProfile) { // do nothing on cloud } @Override - protected void setDefaultEdgeRuleChainId(TenantId tenantId,AssetProfile assetProfile, AssetProfileUpdateMsg assetProfileUpdateMsg) { - UUID defaultEdgeRuleChainUUID = safeGetUUID(assetProfileUpdateMsg.getDefaultRuleChainIdMSB(), assetProfileUpdateMsg.getDefaultRuleChainIdLSB()); + protected void setDefaultEdgeRuleChainId(AssetProfile assetProfile, RuleChainId ruleChainId, AssetProfileUpdateMsg assetProfileUpdateMsg, boolean isEdgeVersionDeprecated) { + UUID defaultEdgeRuleChainUUID = isEdgeVersionDeprecated + ? safeGetUUID(assetProfileUpdateMsg.getDefaultRuleChainIdMSB(), assetProfileUpdateMsg.getDefaultRuleChainIdLSB()) + : assetProfile.getDefaultRuleChainId() != null ? assetProfile.getDefaultRuleChainId().getId() : null; assetProfile.setDefaultEdgeRuleChainId(defaultEdgeRuleChainUUID != null ? new RuleChainId(defaultEdgeRuleChainUUID) : null); + assetProfile.setDefaultRuleChainId(ruleChainId); } @Override - protected void setDefaultDashboardId(TenantId tenantId, AssetProfile assetProfile, AssetProfileUpdateMsg assetProfileUpdateMsg) { - UUID defaultDashboardUUID = safeGetUUID(assetProfileUpdateMsg.getDefaultDashboardIdMSB(), assetProfileUpdateMsg.getDefaultDashboardIdLSB()); + protected void setDefaultDashboardId(TenantId tenantId, AssetProfile assetProfile, AssetProfileUpdateMsg assetProfileUpdateMsg, boolean isEdgeVersionDeprecated) { + UUID defaultDashboardUUID = isEdgeVersionDeprecated + ? safeGetUUID(assetProfileUpdateMsg.getDefaultDashboardIdMSB(), assetProfileUpdateMsg.getDefaultDashboardIdLSB()) + : assetProfile.getDefaultDashboardId() != null ? assetProfile.getDefaultDashboardId().getId() : null; assetProfile.setDefaultDashboardId(defaultDashboardUUID != null ? new DashboardId(defaultDashboardUUID) : null); } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProcessor.java index f8dcdbd8f6..343fd2f80e 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProcessor.java @@ -33,36 +33,33 @@ import java.util.UUID; @Slf4j public abstract class BaseAssetProcessor extends BaseEdgeProcessor { - protected Pair saveOrUpdateAsset(TenantId tenantId, AssetId assetId, AssetUpdateMsg assetUpdateMsg, CustomerId customerId) { + protected Pair saveOrUpdateAsset(TenantId tenantId, AssetId assetId, AssetUpdateMsg assetUpdateMsg, boolean isEdgeProtoDeprecated) { boolean created = false; boolean assetNameUpdated = false; assetCreationLock.lock(); try { - Asset asset = assetService.findAssetById(tenantId, assetId); - String assetName = assetUpdateMsg.getName(); + Asset asset = isEdgeProtoDeprecated + ? createAsset(tenantId, assetId, assetUpdateMsg) + : JacksonUtil.fromStringIgnoreUnknownProperties(assetUpdateMsg.getEntity(), Asset.class); if (asset == null) { + throw new RuntimeException("[{" + tenantId + "}] assetUpdateMsg {" + assetUpdateMsg + " } cannot be converted to asset"); + } + Asset assetById = assetService.findAssetById(tenantId, assetId); + if (assetById == null) { created = true; - asset = new Asset(); - asset.setTenantId(tenantId); - asset.setCreatedTime(Uuids.unixTimestamp(assetId.getId())); + asset.setId(null); + } else { + asset.setId(assetId); } + String assetName = asset.getName(); Asset assetByName = assetService.findAssetByTenantIdAndName(tenantId, assetName); if (assetByName != null && !assetByName.getId().equals(assetId)) { assetName = assetName + "_" + StringUtils.randomAlphanumeric(15); log.warn("[{}] Asset with name {} already exists. Renaming asset name to {}", - tenantId, assetUpdateMsg.getName(), assetName); + tenantId, asset.getName(), assetName); assetNameUpdated = true; } asset.setName(assetName); - asset.setType(assetUpdateMsg.getType()); - asset.setLabel(assetUpdateMsg.hasLabel() ? assetUpdateMsg.getLabel() : null); - asset.setAdditionalInfo(assetUpdateMsg.hasAdditionalInfo() - ? JacksonUtil.toJsonNode(assetUpdateMsg.getAdditionalInfo()) : null); - - UUID assetProfileUUID = safeGetUUID(assetUpdateMsg.getAssetProfileIdMSB(), assetUpdateMsg.getAssetProfileIdLSB()); - asset.setAssetProfileId(assetProfileUUID != null ? new AssetProfileId(assetProfileUUID) : null); - - asset.setCustomerId(customerId); assetValidator.validate(asset, Asset::getTenantId); if (created) { @@ -77,4 +74,22 @@ public abstract class BaseAssetProcessor extends BaseEdgeProcessor { } return Pair.of(created, assetNameUpdated); } + + private Asset createAsset(TenantId tenantId, AssetId assetId, AssetUpdateMsg assetUpdateMsg) { + Asset asset = new Asset(); + asset.setTenantId(tenantId); + asset.setName(assetUpdateMsg.getName()); + asset.setCreatedTime(Uuids.unixTimestamp(assetId.getId())); + asset.setType(assetUpdateMsg.getType()); + asset.setLabel(assetUpdateMsg.hasLabel() ? assetUpdateMsg.getLabel() : null); + asset.setAdditionalInfo(assetUpdateMsg.hasAdditionalInfo() + ? JacksonUtil.toJsonNode(assetUpdateMsg.getAdditionalInfo()) : null); + + UUID assetProfileUUID = safeGetUUID(assetUpdateMsg.getAssetProfileIdMSB(), assetUpdateMsg.getAssetProfileIdLSB()); + asset.setAssetProfileId(assetProfileUUID != null ? new AssetProfileId(assetProfileUUID) : null); + + CustomerId customerId = safeGetCustomerId(assetUpdateMsg.getCustomerIdMSB(), assetUpdateMsg.getCustomerIdLSB()); + asset.setCustomerId(customerId); + return asset; + } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProfileProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProfileProcessor.java index 67c8f8de44..378d229e06 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProfileProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProfileProcessor.java @@ -18,9 +18,11 @@ package org.thingsboard.server.service.edge.rpc.processor.asset; import com.datastax.oss.driver.api.core.uuid.Uuids; import lombok.extern.slf4j.Slf4j; import org.springframework.data.util.Pair; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.id.AssetProfileId; +import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; @@ -30,36 +32,37 @@ import java.nio.charset.StandardCharsets; @Slf4j public abstract class BaseAssetProfileProcessor extends BaseEdgeProcessor { - protected Pair saveOrUpdateAssetProfile(TenantId tenantId, AssetProfileId assetProfileId, AssetProfileUpdateMsg assetProfileUpdateMsg) { + protected Pair saveOrUpdateAssetProfile(TenantId tenantId, AssetProfileId assetProfileId, AssetProfileUpdateMsg assetProfileUpdateMsg, boolean isEdgeProtoDeprecated) { boolean created = false; boolean assetProfileNameUpdated = false; assetCreationLock.lock(); try { - AssetProfile assetProfile = assetProfileService.findAssetProfileById(tenantId, assetProfileId); - String assetProfileName = assetProfileUpdateMsg.getName(); + AssetProfile assetProfile = isEdgeProtoDeprecated + ? createAssetProfile(tenantId, assetProfileId, assetProfileUpdateMsg) + : JacksonUtil.fromEdgeString(assetProfileUpdateMsg.getEntity(), AssetProfile.class); if (assetProfile == null) { + throw new RuntimeException("[{" + tenantId + "}] assetProfileUpdateMsg {" + assetProfileUpdateMsg + "} cannot be converted to asset profile"); + } + AssetProfile assetProfileById = assetProfileService.findAssetProfileById(tenantId, assetProfileId); + if (assetProfileById == null) { created = true; - assetProfile = new AssetProfile(); - assetProfile.setTenantId(tenantId); - assetProfile.setCreatedTime(Uuids.unixTimestamp(assetProfileId.getId())); + assetProfile.setId(null); + } else { + assetProfile.setId(assetProfileId); } + String assetProfileName = assetProfile.getName(); AssetProfile assetProfileByName = assetProfileService.findAssetProfileByName(tenantId, assetProfileName); if (assetProfileByName != null && !assetProfileByName.getId().equals(assetProfileId)) { assetProfileName = assetProfileName + "_" + StringUtils.randomAlphabetic(15); log.warn("[{}] Asset profile with name {} already exists. Renaming asset profile name to {}", - tenantId, assetProfileUpdateMsg.getName(), assetProfileName); + tenantId, assetProfile.getName(), assetProfileName); assetProfileNameUpdated = true; } assetProfile.setName(assetProfileName); - assetProfile.setDefault(assetProfileUpdateMsg.getDefault()); - assetProfile.setDefaultQueueName(assetProfileUpdateMsg.hasDefaultQueueName() ? assetProfileUpdateMsg.getDefaultQueueName() : null); - assetProfile.setDescription(assetProfileUpdateMsg.hasDescription() ? assetProfileUpdateMsg.getDescription() : null); - assetProfile.setImage(assetProfileUpdateMsg.hasImage() - ? new String(assetProfileUpdateMsg.getImage().toByteArray(), StandardCharsets.UTF_8) : null); - setDefaultRuleChainId(tenantId, assetProfile, assetProfileUpdateMsg); - setDefaultEdgeRuleChainId(tenantId, assetProfile, assetProfileUpdateMsg); - setDefaultDashboardId(tenantId, assetProfile, assetProfileUpdateMsg); + setDefaultRuleChainId(tenantId, assetProfile); + setDefaultEdgeRuleChainId(assetProfile, created ? null : assetProfileById.getDefaultRuleChainId(), assetProfileUpdateMsg, isEdgeProtoDeprecated); + setDefaultDashboardId(tenantId, assetProfile, assetProfileUpdateMsg, isEdgeProtoDeprecated); assetProfileValidator.validate(assetProfile, AssetProfile::getTenantId); if (created) { @@ -75,9 +78,22 @@ public abstract class BaseAssetProfileProcessor extends BaseEdgeProcessor { return Pair.of(created, assetProfileNameUpdated); } - protected abstract void setDefaultRuleChainId(TenantId tenantId, AssetProfile assetProfile, AssetProfileUpdateMsg assetProfileUpdateMsg); + private AssetProfile createAssetProfile(TenantId tenantId, AssetProfileId assetProfileId, AssetProfileUpdateMsg assetProfileUpdateMsg) { + AssetProfile assetProfile = new AssetProfile(); + assetProfile.setTenantId(tenantId); + assetProfile.setName(assetProfile.getName()); + assetProfile.setCreatedTime(Uuids.unixTimestamp(assetProfileId.getId())); + assetProfile.setDefault(assetProfileUpdateMsg.getDefault()); + assetProfile.setDefaultQueueName(assetProfileUpdateMsg.hasDefaultQueueName() ? assetProfileUpdateMsg.getDefaultQueueName() : null); + assetProfile.setDescription(assetProfileUpdateMsg.hasDescription() ? assetProfileUpdateMsg.getDescription() : null); + assetProfile.setImage(assetProfileUpdateMsg.hasImage() + ? new String(assetProfileUpdateMsg.getImage().toByteArray(), StandardCharsets.UTF_8) : null); + return assetProfile; + } + + protected abstract void setDefaultRuleChainId(TenantId tenantId, AssetProfile assetProfile); - protected abstract void setDefaultEdgeRuleChainId(TenantId tenantId, AssetProfile assetProfile, AssetProfileUpdateMsg assetProfileUpdateMsg); + protected abstract void setDefaultEdgeRuleChainId(AssetProfile assetProfile, RuleChainId ruleChainId, AssetProfileUpdateMsg assetProfileUpdateMsg, boolean isEdgeVersionDeprecated); - protected abstract void setDefaultDashboardId(TenantId tenantId, AssetProfile assetProfile, AssetProfileUpdateMsg assetProfileUpdateMsg); + protected abstract void setDefaultDashboardId(TenantId tenantId, AssetProfile assetProfile, AssetProfileUpdateMsg assetProfileUpdateMsg, boolean isEdgeVersionDeprecated); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/customer/CustomerEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/customer/CustomerEdgeProcessor.java index f553d6d5a0..04d786022f 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/customer/CustomerEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/customer/CustomerEdgeProcessor.java @@ -33,6 +33,7 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.gen.edge.v1.CustomerUpdateMsg; import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.util.TbCoreComponent; @@ -47,7 +48,7 @@ import java.util.UUID; @TbCoreComponent public class CustomerEdgeProcessor extends BaseEdgeProcessor { - public DownlinkMsg convertCustomerEventToDownlink(EdgeEvent edgeEvent) { + public DownlinkMsg convertCustomerEventToDownlink(EdgeEvent edgeEvent, EdgeVersion edgeVersion) { CustomerId customerId = new CustomerId(edgeEvent.getEntityId()); DownlinkMsg downlinkMsg = null; switch (edgeEvent.getAction()) { @@ -57,7 +58,7 @@ public class CustomerEdgeProcessor extends BaseEdgeProcessor { if (customer != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); CustomerUpdateMsg customerUpdateMsg = - customerMsgConstructor.constructCustomerUpdatedMsg(msgType, customer); + customerMsgConstructor.constructCustomerUpdatedMsg(msgType, customer, edgeVersion); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addCustomerUpdateMsg(customerUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/BaseDashboardProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/BaseDashboardProcessor.java index 687e14a07d..4474b29b4d 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/BaseDashboardProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/BaseDashboardProcessor.java @@ -21,7 +21,6 @@ import lombok.extern.slf4j.Slf4j; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.ShortCustomerInfo; -import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg; @@ -32,20 +31,46 @@ import java.util.Set; @Slf4j public abstract class BaseDashboardProcessor extends BaseEdgeProcessor { - protected boolean saveOrUpdateDashboard(TenantId tenantId, DashboardId dashboardId, DashboardUpdateMsg dashboardUpdateMsg, CustomerId customerId) { + protected boolean saveOrUpdateDashboard(TenantId tenantId, DashboardId deprecatedDashboardId, DashboardUpdateMsg dashboardUpdateMsg, boolean isEdgeProtoDeprecated) { boolean created = false; - Dashboard dashboard = dashboardService.findDashboardById(tenantId, dashboardId); + Dashboard dashboard = isEdgeProtoDeprecated + ? createDashboard(tenantId, deprecatedDashboardId, dashboardUpdateMsg) + : JacksonUtil.fromEdgeString(dashboardUpdateMsg.getEntity(), Dashboard.class); if (dashboard == null) { + throw new RuntimeException("[{" + tenantId + "}] dashboardUpdateMsg {" + dashboardUpdateMsg + "} cannot be converted to dashboard"); + } + DashboardId dashboardId = dashboard.getId(); + Dashboard dashboardById = dashboardService.findDashboardById(tenantId, dashboardId); + if (dashboardById == null) { created = true; - dashboard = new Dashboard(); - dashboard.setTenantId(tenantId); - dashboard.setCreatedTime(Uuids.unixTimestamp(dashboardId.getId())); + dashboard.setId(null); + } else { + dashboard.setId(dashboardId); + } + + dashboardValidator.validate(dashboard, Dashboard::getTenantId); + if (created) { + dashboard.setId(dashboardId); } + Dashboard savedDashboard = dashboardService.saveDashboard(dashboard, false); + if (savedDashboard.getAssignedCustomers() != null && savedDashboard.getAssignedCustomers().isEmpty()) { + for (ShortCustomerInfo assignedCustomer : savedDashboard.getAssignedCustomers()) { + dashboardService.unassignDashboardFromCustomer(tenantId, savedDashboard.getId(), assignedCustomer.getCustomerId()); + } + } + return created; + } + + private Dashboard createDashboard(TenantId tenantId, DashboardId dashboardId, DashboardUpdateMsg dashboardUpdateMsg) { + Dashboard dashboard = new Dashboard(); + dashboard.setTenantId(tenantId); + dashboard.setId(dashboardId); + dashboard.setCreatedTime(Uuids.unixTimestamp(dashboardId.getId())); dashboard.setTitle(dashboardUpdateMsg.getTitle()); dashboard.setImage(dashboardUpdateMsg.hasImage() ? dashboardUpdateMsg.getImage() : null); dashboard.setConfiguration(JacksonUtil.toJsonNode(dashboardUpdateMsg.getConfiguration())); - Set assignedCustomers = null; + Set assignedCustomers; if (dashboardUpdateMsg.hasAssignedCustomers()) { assignedCustomers = JacksonUtil.fromString(dashboardUpdateMsg.getAssignedCustomers(), new TypeReference<>() {}); assignedCustomers = filterNonExistingCustomers(tenantId, assignedCustomers); @@ -54,31 +79,9 @@ public abstract class BaseDashboardProcessor extends BaseEdgeProcessor { dashboard.setMobileOrder(dashboardUpdateMsg.hasMobileOrder() ? dashboardUpdateMsg.getMobileOrder() : null); dashboard.setMobileHide(dashboardUpdateMsg.getMobileHide()); - - dashboardValidator.validate(dashboard, Dashboard::getTenantId); - if (created) { - dashboard.setId(dashboardId); - } - Dashboard savedDashboard = dashboardService.saveDashboard(dashboard, false); - if (assignedCustomers != null && !assignedCustomers.isEmpty()) { - for (ShortCustomerInfo assignedCustomer : assignedCustomers) { - if (assignedCustomer.getCustomerId().equals(customerId)) { - dashboardService.assignDashboardToCustomer(tenantId, dashboardId, assignedCustomer.getCustomerId()); - } - } - } else { - unassignCustomersFromDashboard(tenantId, savedDashboard); - } - return created; + return dashboard; } - protected abstract Set filterNonExistingCustomers(TenantId tenantId, Set assignedCustomers); - private void unassignCustomersFromDashboard(TenantId tenantId, Dashboard dashboard) { - if (dashboard.getAssignedCustomers() != null && !dashboard.getAssignedCustomers().isEmpty()) { - for (ShortCustomerInfo assignedCustomer : dashboard.getAssignedCustomers()) { - dashboardService.unassignDashboardFromCustomer(tenantId, dashboard.getId(), assignedCustomer.getCustomerId()); - } - } - } + protected abstract Set filterNonExistingCustomers(TenantId tenantId, Set assignedCustomers); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardEdgeProcessor.java index 291491a2fe..630416f775 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardEdgeProcessor.java @@ -25,7 +25,6 @@ import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.ShortCustomerInfo; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeEvent; -import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.msg.TbMsgType; @@ -33,8 +32,10 @@ import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg; import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; import java.util.Set; import java.util.UUID; @@ -44,7 +45,7 @@ import java.util.UUID; @TbCoreComponent public class DashboardEdgeProcessor extends BaseDashboardProcessor { - public ListenableFuture processDashboardMsgFromEdge(TenantId tenantId, Edge edge, DashboardUpdateMsg dashboardUpdateMsg) { + public ListenableFuture processDashboardMsgFromEdge(TenantId tenantId, Edge edge, DashboardUpdateMsg dashboardUpdateMsg, EdgeVersion edgeVersion) { log.trace("[{}] executing processDashboardMsgFromEdge [{}] from edge [{}]", tenantId, dashboardUpdateMsg, edge.getId()); DashboardId dashboardId = new DashboardId(new UUID(dashboardUpdateMsg.getIdMSB(), dashboardUpdateMsg.getIdLSB())); try { @@ -53,7 +54,7 @@ public class DashboardEdgeProcessor extends BaseDashboardProcessor { switch (dashboardUpdateMsg.getMsgType()) { case ENTITY_CREATED_RPC_MESSAGE: case ENTITY_UPDATED_RPC_MESSAGE: - saveOrUpdateDashboard(tenantId, dashboardId, dashboardUpdateMsg, edge); + saveOrUpdateDashboard(tenantId, dashboardId, dashboardUpdateMsg, edge, edgeVersion); return Futures.immediateFuture(null); case ENTITY_DELETED_RPC_MESSAGE: Dashboard dashboardToDelete = dashboardService.findDashboardById(tenantId, dashboardId); @@ -77,9 +78,9 @@ public class DashboardEdgeProcessor extends BaseDashboardProcessor { } } - private void saveOrUpdateDashboard(TenantId tenantId, DashboardId dashboardId, DashboardUpdateMsg dashboardUpdateMsg, Edge edge) { - CustomerId customerId = safeGetCustomerId(dashboardUpdateMsg.getCustomerIdMSB(), dashboardUpdateMsg.getCustomerIdLSB()); - boolean created = super.saveOrUpdateDashboard(tenantId, dashboardId, dashboardUpdateMsg, customerId); + private void saveOrUpdateDashboard(TenantId tenantId, DashboardId dashboardId, DashboardUpdateMsg dashboardUpdateMsg, Edge edge, EdgeVersion edgeVersion) { + boolean created = super.saveOrUpdateDashboard(tenantId, dashboardId, dashboardUpdateMsg, + EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)); if (created) { createRelationFromEdge(tenantId, edge.getId(), dashboardId); pushDashboardCreatedEventToRuleEngine(tenantId, edge, dashboardId); @@ -98,7 +99,7 @@ public class DashboardEdgeProcessor extends BaseDashboardProcessor { } } - public DownlinkMsg convertDashboardEventToDownlink(EdgeEvent edgeEvent) { + public DownlinkMsg convertDashboardEventToDownlink(EdgeEvent edgeEvent, EdgeVersion edgeVersion) { DashboardId dashboardId = new DashboardId(edgeEvent.getEntityId()); DownlinkMsg downlinkMsg = null; switch (edgeEvent.getAction()) { @@ -111,7 +112,7 @@ public class DashboardEdgeProcessor extends BaseDashboardProcessor { if (dashboard != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); DashboardUpdateMsg dashboardUpdateMsg = - dashboardMsgConstructor.constructDashboardUpdatedMsg(msgType, dashboard); + dashboardMsgConstructor.constructDashboardUpdatedMsg(msgType, dashboard, edgeVersion); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addDashboardUpdateMsg(dashboardUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProcessor.java index 8958db0c44..b43843580b 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProcessor.java @@ -32,8 +32,10 @@ import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentialsType; import org.thingsboard.server.gen.edge.v1.DeviceCredentialsUpdateMsg; import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.queue.util.DataDecodingEncodingService; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; +import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; import java.util.Optional; import java.util.UUID; @@ -44,46 +46,35 @@ public abstract class BaseDeviceProcessor extends BaseEdgeProcessor { @Autowired protected DataDecodingEncodingService dataDecodingEncodingService; - protected Pair saveOrUpdateDevice(TenantId tenantId, DeviceId deviceId, DeviceUpdateMsg deviceUpdateMsg, CustomerId customerId) { + protected Pair saveOrUpdateDevice(TenantId tenantId, DeviceId deviceId, DeviceUpdateMsg deviceUpdateMsg, boolean isEdgeProtoDeprecated) { boolean created = false; boolean deviceNameUpdated = false; deviceCreationLock.lock(); try { - Device device = deviceService.findDeviceById(tenantId, deviceId); - String deviceName = deviceUpdateMsg.getName(); + Device device = isEdgeProtoDeprecated + ? createDevice(tenantId, deviceId, deviceUpdateMsg) + : JacksonUtil.fromStringIgnoreUnknownProperties(deviceUpdateMsg.getEntity(), Device.class); if (device == null) { + throw new RuntimeException("[{" + tenantId + "}] deviceUpdateMsg {" + deviceUpdateMsg + "} cannot be converted to device"); + } + Device deviceById = deviceService.findDeviceById(tenantId, deviceId); + if (deviceById == null) { created = true; - device = new Device(); - device.setTenantId(tenantId); - device.setCreatedTime(Uuids.unixTimestamp(deviceId.getId())); + device.setId(null); + } else { + device.setId(deviceId); } + String deviceName = device.getName(); Device deviceByName = deviceService.findDeviceByTenantIdAndName(tenantId, deviceName); if (deviceByName != null && !deviceByName.getId().equals(deviceId)) { deviceName = deviceName + "_" + StringUtils.randomAlphabetic(15); log.warn("[{}] Device with name {} already exists. Renaming device name to {}", - tenantId, deviceUpdateMsg.getName(), deviceName); + tenantId, device.getName(), deviceName); deviceNameUpdated = true; } device.setName(deviceName); - device.setType(deviceUpdateMsg.getType()); - device.setLabel(deviceUpdateMsg.hasLabel() ? deviceUpdateMsg.getLabel() : null); - device.setAdditionalInfo(deviceUpdateMsg.hasAdditionalInfo() - ? JacksonUtil.toJsonNode(deviceUpdateMsg.getAdditionalInfo()) : null); - - UUID deviceProfileUUID = safeGetUUID(deviceUpdateMsg.getDeviceProfileIdMSB(), deviceUpdateMsg.getDeviceProfileIdLSB()); - device.setDeviceProfileId(deviceProfileUUID != null ? new DeviceProfileId(deviceProfileUUID) : null); - - device.setCustomerId(customerId); + setCustomerId(tenantId, created ? null : deviceById.getCustomerId(), device, deviceUpdateMsg, isEdgeProtoDeprecated); - Optional deviceDataOpt = - dataDecodingEncodingService.decode(deviceUpdateMsg.getDeviceDataBytes().toByteArray()); - device.setDeviceData(deviceDataOpt.orElse(null)); - - UUID firmwareUUID = safeGetUUID(deviceUpdateMsg.getFirmwareIdMSB(), deviceUpdateMsg.getFirmwareIdLSB()); - device.setFirmwareId(firmwareUUID != null ? new OtaPackageId(firmwareUUID) : null); - - UUID softwareUUID = safeGetUUID(deviceUpdateMsg.getSoftwareIdMSB(), deviceUpdateMsg.getSoftwareIdLSB()); - device.setSoftwareId(softwareUUID != null ? new OtaPackageId(softwareUUID) : null); deviceValidator.validate(device, Device::getTenantId); if (created) { device.setId(deviceId); @@ -100,25 +91,53 @@ public abstract class BaseDeviceProcessor extends BaseEdgeProcessor { } catch (Exception e) { log.error("[{}] Failed to process device update msg [{}]", tenantId, deviceUpdateMsg, e); throw e; - } finally { + } finally { deviceCreationLock.unlock(); } return Pair.of(created, deviceNameUpdated); } - protected void updateDeviceCredentials(TenantId tenantId, DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg) { - DeviceId deviceId = new DeviceId(new UUID(deviceCredentialsUpdateMsg.getDeviceIdMSB(), deviceCredentialsUpdateMsg.getDeviceIdLSB())); - Device device = deviceService.findDeviceById(tenantId, deviceId); + private Device createDevice(TenantId tenantId, DeviceId deviceId, DeviceUpdateMsg deviceUpdateMsg) { + Device device = new Device(); + device.setTenantId(tenantId); + device.setCreatedTime(Uuids.unixTimestamp(deviceId.getId())); + device.setName(deviceUpdateMsg.getName()); + device.setType(deviceUpdateMsg.getType()); + device.setLabel(deviceUpdateMsg.hasLabel() ? deviceUpdateMsg.getLabel() : null); + device.setAdditionalInfo(deviceUpdateMsg.hasAdditionalInfo() + ? JacksonUtil.toJsonNode(deviceUpdateMsg.getAdditionalInfo()) : null); + + UUID deviceProfileUUID = safeGetUUID(deviceUpdateMsg.getDeviceProfileIdMSB(), deviceUpdateMsg.getDeviceProfileIdLSB()); + device.setDeviceProfileId(deviceProfileUUID != null ? new DeviceProfileId(deviceProfileUUID) : null); + + Optional deviceDataOpt = dataDecodingEncodingService.decode(deviceUpdateMsg.getDeviceDataBytes().toByteArray()); + device.setDeviceData(deviceDataOpt.orElse(null)); + + UUID firmwareUUID = safeGetUUID(deviceUpdateMsg.getFirmwareIdMSB(), deviceUpdateMsg.getFirmwareIdLSB()); + device.setFirmwareId(firmwareUUID != null ? new OtaPackageId(firmwareUUID) : null); + UUID softwareUUID = safeGetUUID(deviceUpdateMsg.getSoftwareIdMSB(), deviceUpdateMsg.getSoftwareIdLSB()); + device.setSoftwareId(softwareUUID != null ? new OtaPackageId(softwareUUID) : null); + + return device; + } + + protected void updateDeviceCredentials(TenantId tenantId, DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg, EdgeVersion edgeVersion) { + DeviceCredentials deviceCredentials = EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion) + ? createDeviceCredentials(deviceCredentialsUpdateMsg) + : JacksonUtil.fromStringIgnoreUnknownProperties(deviceCredentialsUpdateMsg.getEntity(), DeviceCredentials.class); + if (deviceCredentials == null) { + throw new RuntimeException("[{" + tenantId + "}] deviceCredentialsUpdateMsg {" + deviceCredentialsUpdateMsg + "} cannot be converted to device credentials"); + } + Device device = deviceService.findDeviceById(tenantId, deviceCredentials.getDeviceId()); if (device != null) { log.debug("[{}] Updating device credentials for device [{}]. New device credentials Id [{}], value [{}]", - tenantId, device.getName(), deviceCredentialsUpdateMsg.getCredentialsId(), deviceCredentialsUpdateMsg.getCredentialsValue()); + tenantId, device.getName(), deviceCredentials.getCredentialsId(), deviceCredentials.getCredentialsValue()); try { - DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(tenantId, device.getId()); - deviceCredentials.setCredentialsType(DeviceCredentialsType.valueOf(deviceCredentialsUpdateMsg.getCredentialsType())); - deviceCredentials.setCredentialsId(deviceCredentialsUpdateMsg.getCredentialsId()); - deviceCredentials.setCredentialsValue(deviceCredentialsUpdateMsg.hasCredentialsValue() - ? deviceCredentialsUpdateMsg.getCredentialsValue() : null); - deviceCredentialsService.updateDeviceCredentials(tenantId, deviceCredentials); + DeviceCredentials deviceCredentialsByDeviceId = deviceCredentialsService.findDeviceCredentialsByDeviceId(tenantId, device.getId()); + deviceCredentialsByDeviceId.setCredentialsType(deviceCredentials.getCredentialsType()); + deviceCredentialsByDeviceId.setCredentialsId(deviceCredentials.getCredentialsId()); + deviceCredentialsByDeviceId.setCredentialsValue(deviceCredentials.getCredentialsValue()); + deviceCredentialsService.updateDeviceCredentials(tenantId, deviceCredentialsByDeviceId); } catch (Exception e) { log.error("[{}] Can't update device credentials for device [{}], deviceCredentialsUpdateMsg [{}]", @@ -126,7 +145,19 @@ public abstract class BaseDeviceProcessor extends BaseEdgeProcessor { throw new RuntimeException(e); } } else { - log.warn("[{}] Can't find device by id [{}], deviceCredentialsUpdateMsg [{}]", tenantId, deviceId, deviceCredentialsUpdateMsg); + log.warn("[{}] Can't find device by id [{}], deviceCredentialsUpdateMsg [{}]", tenantId, deviceCredentials.getDeviceId(), deviceCredentialsUpdateMsg); } } + + private DeviceCredentials createDeviceCredentials(DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg) { + DeviceCredentials deviceCredentials = new DeviceCredentials(); + deviceCredentials.setDeviceId(new DeviceId(new UUID(deviceCredentialsUpdateMsg.getDeviceIdMSB(), deviceCredentialsUpdateMsg.getDeviceIdLSB()))); + deviceCredentials.setCredentialsType(DeviceCredentialsType.valueOf(deviceCredentialsUpdateMsg.getCredentialsType())); + deviceCredentials.setCredentialsId(deviceCredentialsUpdateMsg.getCredentialsId()); + deviceCredentials.setCredentialsValue(deviceCredentialsUpdateMsg.hasCredentialsValue() + ? deviceCredentialsUpdateMsg.getCredentialsValue() : null); + return deviceCredentials; + } + + protected abstract void setCustomerId(TenantId tenantId, CustomerId customerId, Device device, DeviceUpdateMsg deviceUpdateMsg, boolean isEdgeVersionDeprecated); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProfileProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProfileProcessor.java index bd35a42e59..a48a578e48 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProfileProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProfileProcessor.java @@ -19,6 +19,7 @@ import com.datastax.oss.driver.api.core.uuid.Uuids; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.util.Pair; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfileProvisionType; import org.thingsboard.server.common.data.DeviceProfileType; @@ -27,6 +28,7 @@ import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.device.profile.DeviceProfileData; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.OtaPackageId; +import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg; import org.thingsboard.server.queue.util.DataDecodingEncodingService; @@ -42,56 +44,37 @@ public abstract class BaseDeviceProfileProcessor extends BaseEdgeProcessor { @Autowired private DataDecodingEncodingService dataDecodingEncodingService; - protected Pair saveOrUpdateDeviceProfile(TenantId tenantId, DeviceProfileId deviceProfileId, DeviceProfileUpdateMsg deviceProfileUpdateMsg) { + protected Pair saveOrUpdateDeviceProfile(TenantId tenantId, DeviceProfileId deviceProfileId, DeviceProfileUpdateMsg deviceProfileUpdateMsg, boolean isEdgeVersionProtoDeprecated) { boolean created = false; boolean deviceProfileNameUpdated = false; deviceCreationLock.lock(); try { - DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileById(tenantId, deviceProfileId); - String deviceProfileName = deviceProfileUpdateMsg.getName(); + DeviceProfile deviceProfile = isEdgeVersionProtoDeprecated + ? createDeviceProfile(tenantId, deviceProfileId, deviceProfileUpdateMsg) + : JacksonUtil.fromEdgeString(deviceProfileUpdateMsg.getEntity(), DeviceProfile.class); if (deviceProfile == null) { + throw new RuntimeException("[{" + tenantId + "}] deviceProfileUpdateMsg {" + deviceProfileUpdateMsg + "} cannot be converted to device profile"); + } + DeviceProfile deviceProfileById = deviceProfileService.findDeviceProfileById(tenantId, deviceProfileId); + if (deviceProfileById == null) { created = true; - deviceProfile = new DeviceProfile(); - deviceProfile.setTenantId(tenantId); - deviceProfile.setCreatedTime(Uuids.unixTimestamp(deviceProfileId.getId())); + deviceProfile.setId(null); + } else { + deviceProfile.setId(deviceProfileId); } + String deviceProfileName = deviceProfile.getName(); DeviceProfile deviceProfileByName = deviceProfileService.findDeviceProfileByName(tenantId, deviceProfileName); if (deviceProfileByName != null && !deviceProfileByName.getId().equals(deviceProfileId)) { deviceProfileName = deviceProfileName + "_" + StringUtils.randomAlphabetic(15); log.warn("[{}] Device profile with name {} already exists. Renaming device profile name to {}", - tenantId, deviceProfileUpdateMsg.getName(), deviceProfileName); + tenantId, deviceProfile.getName(), deviceProfileName); deviceProfileNameUpdated = true; } deviceProfile.setName(deviceProfileName); - deviceProfile.setDescription(deviceProfileUpdateMsg.hasDescription() ? deviceProfileUpdateMsg.getDescription() : null); - deviceProfile.setType(DeviceProfileType.valueOf(deviceProfileUpdateMsg.getType())); - deviceProfile.setTransportType(deviceProfileUpdateMsg.hasTransportType() - ? DeviceTransportType.valueOf(deviceProfileUpdateMsg.getTransportType()) : DeviceTransportType.DEFAULT); - deviceProfile.setImage(deviceProfileUpdateMsg.hasImage() - ? new String(deviceProfileUpdateMsg.getImage().toByteArray(), StandardCharsets.UTF_8) : null); - deviceProfile.setProvisionType(deviceProfileUpdateMsg.hasProvisionType() - ? DeviceProfileProvisionType.valueOf(deviceProfileUpdateMsg.getProvisionType()) : DeviceProfileProvisionType.DISABLED); - deviceProfile.setProvisionDeviceKey(deviceProfileUpdateMsg.hasProvisionDeviceKey() - ? deviceProfileUpdateMsg.getProvisionDeviceKey() : null); - deviceProfile.setDefaultQueueName(deviceProfileUpdateMsg.getDefaultQueueName()); - - Optional profileDataOpt = - dataDecodingEncodingService.decode(deviceProfileUpdateMsg.getProfileDataBytes().toByteArray()); - deviceProfile.setProfileData(profileDataOpt.orElse(null)); - - setDefaultRuleChainId(tenantId, deviceProfile, deviceProfileUpdateMsg); - setDefaultEdgeRuleChainId(tenantId, deviceProfile, deviceProfileUpdateMsg); - setDefaultDashboardId(tenantId, deviceProfile, deviceProfileUpdateMsg); - - String defaultQueueName = StringUtils.isNotBlank(deviceProfileUpdateMsg.getDefaultQueueName()) - ? deviceProfileUpdateMsg.getDefaultQueueName() : null; - deviceProfile.setDefaultQueueName(defaultQueueName); - - UUID firmwareUUID = safeGetUUID(deviceProfileUpdateMsg.getFirmwareIdMSB(), deviceProfileUpdateMsg.getFirmwareIdLSB()); - deviceProfile.setFirmwareId(firmwareUUID != null ? new OtaPackageId(firmwareUUID) : null); - UUID softwareUUID = safeGetUUID(deviceProfileUpdateMsg.getSoftwareIdMSB(), deviceProfileUpdateMsg.getSoftwareIdLSB()); - deviceProfile.setSoftwareId(softwareUUID != null ? new OtaPackageId(softwareUUID) : null); + setDefaultRuleChainId(tenantId, deviceProfile); + setDefaultEdgeRuleChainId(deviceProfile, created ? null : deviceProfileById.getDefaultRuleChainId(), deviceProfileUpdateMsg, isEdgeVersionProtoDeprecated); + setDefaultDashboardId(tenantId, deviceProfile, deviceProfileUpdateMsg, isEdgeVersionProtoDeprecated); deviceProfileValidator.validate(deviceProfile, DeviceProfile::getTenantId); if (created) { @@ -107,9 +90,42 @@ public abstract class BaseDeviceProfileProcessor extends BaseEdgeProcessor { return Pair.of(created, deviceProfileNameUpdated); } - protected abstract void setDefaultRuleChainId(TenantId tenantId, DeviceProfile deviceProfile, DeviceProfileUpdateMsg deviceProfileUpdateMsg); + private DeviceProfile createDeviceProfile(TenantId tenantId, DeviceProfileId deviceProfileId, DeviceProfileUpdateMsg deviceProfileUpdateMsg) { + DeviceProfile deviceProfile = new DeviceProfile(); + deviceProfile.setTenantId(tenantId); + deviceProfile.setCreatedTime(Uuids.unixTimestamp(deviceProfileId.getId())); + deviceProfile.setName(deviceProfileUpdateMsg.getName()); + deviceProfile.setDescription(deviceProfileUpdateMsg.hasDescription() ? deviceProfileUpdateMsg.getDescription() : null); + deviceProfile.setType(DeviceProfileType.valueOf(deviceProfileUpdateMsg.getType())); + deviceProfile.setTransportType(deviceProfileUpdateMsg.hasTransportType() + ? DeviceTransportType.valueOf(deviceProfileUpdateMsg.getTransportType()) : DeviceTransportType.DEFAULT); + deviceProfile.setImage(deviceProfileUpdateMsg.hasImage() + ? new String(deviceProfileUpdateMsg.getImage().toByteArray(), StandardCharsets.UTF_8) : null); + deviceProfile.setProvisionType(deviceProfileUpdateMsg.hasProvisionType() + ? DeviceProfileProvisionType.valueOf(deviceProfileUpdateMsg.getProvisionType()) : DeviceProfileProvisionType.DISABLED); + deviceProfile.setProvisionDeviceKey(deviceProfileUpdateMsg.hasProvisionDeviceKey() + ? deviceProfileUpdateMsg.getProvisionDeviceKey() : null); + deviceProfile.setDefaultQueueName(deviceProfileUpdateMsg.getDefaultQueueName()); + + Optional profileDataOpt = + dataDecodingEncodingService.decode(deviceProfileUpdateMsg.getProfileDataBytes().toByteArray()); + deviceProfile.setProfileData(profileDataOpt.orElse(null)); + + String defaultQueueName = StringUtils.isNotBlank(deviceProfileUpdateMsg.getDefaultQueueName()) + ? deviceProfileUpdateMsg.getDefaultQueueName() : null; + deviceProfile.setDefaultQueueName(defaultQueueName); + + UUID firmwareUUID = safeGetUUID(deviceProfileUpdateMsg.getFirmwareIdMSB(), deviceProfileUpdateMsg.getFirmwareIdLSB()); + deviceProfile.setFirmwareId(firmwareUUID != null ? new OtaPackageId(firmwareUUID) : null); + + UUID softwareUUID = safeGetUUID(deviceProfileUpdateMsg.getSoftwareIdMSB(), deviceProfileUpdateMsg.getSoftwareIdLSB()); + deviceProfile.setSoftwareId(softwareUUID != null ? new OtaPackageId(softwareUUID) : null); + return deviceProfile; + } + + protected abstract void setDefaultRuleChainId(TenantId tenantId, DeviceProfile deviceProfile); - protected abstract void setDefaultEdgeRuleChainId(TenantId tenantId, DeviceProfile deviceProfile, DeviceProfileUpdateMsg deviceProfileUpdateMsg); + protected abstract void setDefaultEdgeRuleChainId(DeviceProfile deviceProfile, RuleChainId ruleChainId, DeviceProfileUpdateMsg deviceProfileUpdateMsg, boolean isEdgeVersionDeprecated); - protected abstract void setDefaultDashboardId(TenantId tenantId, DeviceProfile deviceProfile, DeviceProfileUpdateMsg deviceProfileUpdateMsg); + protected abstract void setDefaultDashboardId(TenantId tenantId, DeviceProfile deviceProfile, DeviceProfileUpdateMsg deviceProfileUpdateMsg, boolean isEdgeVersionDeprecated); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java index 910be10240..17eec5eb0c 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java @@ -32,7 +32,6 @@ import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.edge.EdgeEventType; -import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; @@ -43,6 +42,7 @@ import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgDataType; import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse; +import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponseActorMsg; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.gen.edge.v1.DeviceCredentialsRequestMsg; import org.thingsboard.server.gen.edge.v1.DeviceCredentialsUpdateMsg; @@ -54,7 +54,7 @@ import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.TbQueueCallback; import org.thingsboard.server.queue.TbQueueMsgMetadata; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponseActorMsg; +import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; import java.util.UUID; @@ -63,7 +63,7 @@ import java.util.UUID; @TbCoreComponent public class DeviceEdgeProcessor extends BaseDeviceProcessor { - public ListenableFuture processDeviceMsgFromEdge(TenantId tenantId, Edge edge, DeviceUpdateMsg deviceUpdateMsg) { + public ListenableFuture processDeviceMsgFromEdge(TenantId tenantId, Edge edge, DeviceUpdateMsg deviceUpdateMsg, EdgeVersion edgeVersion) { log.trace("[{}] executing processDeviceMsgFromEdge [{}] from edge [{}]", tenantId, deviceUpdateMsg, edge.getId()); DeviceId deviceId = new DeviceId(new UUID(deviceUpdateMsg.getIdMSB(), deviceUpdateMsg.getIdLSB())); try { @@ -72,7 +72,7 @@ public class DeviceEdgeProcessor extends BaseDeviceProcessor { switch (deviceUpdateMsg.getMsgType()) { case ENTITY_CREATED_RPC_MESSAGE: case ENTITY_UPDATED_RPC_MESSAGE: - saveOrUpdateDevice(tenantId, deviceId, deviceUpdateMsg, edge); + saveOrUpdateDevice(tenantId, deviceId, deviceUpdateMsg, edge, edgeVersion); return saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.CREDENTIALS_REQUEST, deviceId, null); case ENTITY_DELETED_RPC_MESSAGE: Device deviceToDelete = deviceService.findDeviceById(tenantId, deviceId); @@ -108,9 +108,9 @@ public class DeviceEdgeProcessor extends BaseDeviceProcessor { return Futures.immediateFuture(null); } - private void saveOrUpdateDevice(TenantId tenantId, DeviceId deviceId, DeviceUpdateMsg deviceUpdateMsg, Edge edge) { - CustomerId customerId = safeGetCustomerId(deviceUpdateMsg.getCustomerIdMSB(), deviceUpdateMsg.getCustomerIdLSB()); - Pair resultPair = super.saveOrUpdateDevice(tenantId, deviceId, deviceUpdateMsg, customerId); + private void saveOrUpdateDevice(TenantId tenantId, DeviceId deviceId, DeviceUpdateMsg deviceUpdateMsg, Edge edge, EdgeVersion edgeVersion) { + Pair resultPair = super.saveOrUpdateDevice(tenantId, deviceId, deviceUpdateMsg, + EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)); Boolean created = resultPair.getFirst(); if (created) { createRelationFromEdge(tenantId, edge.getId(), deviceId); @@ -228,14 +228,14 @@ public class DeviceEdgeProcessor extends BaseDeviceProcessor { if (device != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); DeviceUpdateMsg deviceUpdateMsg = - deviceMsgConstructor.constructDeviceUpdatedMsg(msgType, device); + deviceMsgConstructor.constructDeviceUpdatedMsg(msgType, device, edgeVersion); DownlinkMsg.Builder builder = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addDeviceUpdateMsg(deviceUpdateMsg); if (UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE.equals(msgType)) { DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileById(edgeEvent.getTenantId(), device.getDeviceProfileId()); deviceProfile = checkIfDeviceProfileDefaultFieldsAssignedToEdge(edgeEvent.getTenantId(), edgeId, deviceProfile, edgeVersion); - builder.addDeviceProfileUpdateMsg(deviceProfileMsgConstructor.constructDeviceProfileUpdatedMsg(msgType, deviceProfile)); + builder.addDeviceProfileUpdateMsg(deviceProfileMsgConstructor.constructDeviceProfileUpdatedMsg(msgType, deviceProfile, edgeVersion)); } downlinkMsg = builder.build(); } @@ -253,7 +253,7 @@ public class DeviceEdgeProcessor extends BaseDeviceProcessor { DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(edgeEvent.getTenantId(), deviceId); if (deviceCredentials != null) { DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg = - deviceMsgConstructor.constructDeviceCredentialsUpdatedMsg(deviceCredentials); + deviceMsgConstructor.constructDeviceCredentialsUpdatedMsg(deviceCredentials, edgeVersion); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addDeviceCredentialsUpdateMsg(deviceCredentialsUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceProfileEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceProfileEdgeProcessor.java index 3ed75bdb41..523422bec7 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceProfileEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceProfileEdgeProcessor.java @@ -40,6 +40,7 @@ import org.thingsboard.server.gen.edge.v1.DownlinkMsg; import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; import java.util.UUID; @@ -48,7 +49,7 @@ import java.util.UUID; @TbCoreComponent public class DeviceProfileEdgeProcessor extends BaseDeviceProfileProcessor { - public ListenableFuture processDeviceProfileMsgFromEdge(TenantId tenantId, Edge edge, DeviceProfileUpdateMsg deviceProfileUpdateMsg) { + public ListenableFuture processDeviceProfileMsgFromEdge(TenantId tenantId, Edge edge, DeviceProfileUpdateMsg deviceProfileUpdateMsg, EdgeVersion edgeVersion) { log.trace("[{}] executing processDeviceProfileMsgFromEdge [{}] from edge [{}]", tenantId, deviceProfileUpdateMsg, edge.getId()); DeviceProfileId deviceProfileId = new DeviceProfileId(new UUID(deviceProfileUpdateMsg.getIdMSB(), deviceProfileUpdateMsg.getIdLSB())); try { @@ -57,7 +58,7 @@ public class DeviceProfileEdgeProcessor extends BaseDeviceProfileProcessor { switch (deviceProfileUpdateMsg.getMsgType()) { case ENTITY_CREATED_RPC_MESSAGE: case ENTITY_UPDATED_RPC_MESSAGE: - saveOrUpdateDeviceProfile(tenantId, deviceProfileId, deviceProfileUpdateMsg, edge); + saveOrUpdateDeviceProfile(tenantId, deviceProfileId, deviceProfileUpdateMsg, edge, edgeVersion); return Futures.immediateFuture(null); case ENTITY_DELETED_RPC_MESSAGE: case UNRECOGNIZED: @@ -72,8 +73,9 @@ public class DeviceProfileEdgeProcessor extends BaseDeviceProfileProcessor { } } - private void saveOrUpdateDeviceProfile(TenantId tenantId, DeviceProfileId deviceProfileId, DeviceProfileUpdateMsg deviceProfileUpdateMsg, Edge edge) { - Pair resultPair = super.saveOrUpdateDeviceProfile(tenantId, deviceProfileId, deviceProfileUpdateMsg); + private void saveOrUpdateDeviceProfile(TenantId tenantId, DeviceProfileId deviceProfileId, DeviceProfileUpdateMsg deviceProfileUpdateMsg, Edge edge, EdgeVersion edgeVersion) { + Pair resultPair = super.saveOrUpdateDeviceProfile(tenantId, deviceProfileId, deviceProfileUpdateMsg, + EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)); Boolean created = resultPair.getFirst(); if (created) { createRelationFromEdge(tenantId, edge.getId(), deviceProfileId); @@ -107,7 +109,7 @@ public class DeviceProfileEdgeProcessor extends BaseDeviceProfileProcessor { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); deviceProfile = checkIfDeviceProfileDefaultFieldsAssignedToEdge(edgeEvent.getTenantId(), edgeId, deviceProfile, edgeVersion); DeviceProfileUpdateMsg deviceProfileUpdateMsg = - deviceProfileMsgConstructor.constructDeviceProfileUpdatedMsg(msgType, deviceProfile); + deviceProfileMsgConstructor.constructDeviceProfileUpdatedMsg(msgType, deviceProfile, edgeVersion); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addDeviceProfileUpdateMsg(deviceProfileUpdateMsg) @@ -127,19 +129,24 @@ public class DeviceProfileEdgeProcessor extends BaseDeviceProfileProcessor { } @Override - protected void setDefaultRuleChainId(TenantId tenantId, DeviceProfile deviceProfile, DeviceProfileUpdateMsg deviceProfileUpdateMsg) { + protected void setDefaultRuleChainId(TenantId tenantId, DeviceProfile deviceProfile) { // do nothing on cloud } @Override - protected void setDefaultEdgeRuleChainId(TenantId tenantId, DeviceProfile deviceProfile, DeviceProfileUpdateMsg deviceProfileUpdateMsg) { - UUID defaultEdgeRuleChainUUID = safeGetUUID(deviceProfileUpdateMsg.getDefaultRuleChainIdMSB(), deviceProfileUpdateMsg.getDefaultRuleChainIdLSB()); + protected void setDefaultEdgeRuleChainId(DeviceProfile deviceProfile, RuleChainId ruleChainId, DeviceProfileUpdateMsg deviceProfileUpdateMsg, boolean isEdgeVersionDeprecated) { + UUID defaultEdgeRuleChainUUID = isEdgeVersionDeprecated + ? safeGetUUID(deviceProfileUpdateMsg.getDefaultRuleChainIdMSB(), deviceProfileUpdateMsg.getDefaultRuleChainIdLSB()) + : deviceProfile.getDefaultRuleChainId() != null ? deviceProfile.getDefaultRuleChainId().getId() : null; deviceProfile.setDefaultEdgeRuleChainId(defaultEdgeRuleChainUUID != null ? new RuleChainId(defaultEdgeRuleChainUUID) : null); + deviceProfile.setDefaultRuleChainId(ruleChainId); } @Override - protected void setDefaultDashboardId(TenantId tenantId, DeviceProfile deviceProfile, DeviceProfileUpdateMsg deviceProfileUpdateMsg) { - UUID defaultDashboardUUID = safeGetUUID(deviceProfileUpdateMsg.getDefaultDashboardIdMSB(), deviceProfileUpdateMsg.getDefaultDashboardIdLSB()); + protected void setDefaultDashboardId(TenantId tenantId, DeviceProfile deviceProfile, DeviceProfileUpdateMsg deviceProfileUpdateMsg, boolean isEdgeVersionDeprecated) { + UUID defaultDashboardUUID = isEdgeVersionDeprecated + ? safeGetUUID(deviceProfileUpdateMsg.getDefaultDashboardIdMSB(), deviceProfileUpdateMsg.getDefaultDashboardIdLSB()) + : deviceProfile.getDefaultDashboardId() != null ? deviceProfile.getDefaultDashboardId().getId() : null; deviceProfile.setDefaultDashboardId(defaultDashboardUUID != null ? new DashboardId(defaultDashboardUUID) : null); } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/BaseEntityViewProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/BaseEntityViewProcessor.java index cca0057707..3d13b7a044 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/BaseEntityViewProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/BaseEntityViewProcessor.java @@ -35,42 +35,59 @@ import java.util.UUID; @Slf4j public abstract class BaseEntityViewProcessor extends BaseEdgeProcessor { - protected Pair saveOrUpdateEntityView(TenantId tenantId, EntityViewId entityViewId, EntityViewUpdateMsg entityViewUpdateMsg, CustomerId customerId) { + protected Pair saveOrUpdateEntityView(TenantId tenantId, EntityViewId entityViewId, EntityViewUpdateMsg entityViewUpdateMsg, boolean isEdgeProtoDeprecated) { boolean created = false; boolean entityViewNameUpdated = false; - EntityView entityView = entityViewService.findEntityViewById(tenantId, entityViewId); - String entityViewName = entityViewUpdateMsg.getName(); + EntityView entityView = isEdgeProtoDeprecated + ? createEntityView(tenantId, entityViewId, entityViewUpdateMsg) + : JacksonUtil.fromEdgeString(entityViewUpdateMsg.getEntity(), EntityView.class); if (entityView == null) { + throw new RuntimeException("[{" + tenantId + "}] entityViewUpdateMsg {" + entityViewUpdateMsg + "} cannot be converted to entity view"); + } + EntityView entityViewById = entityViewService.findEntityViewById(tenantId, entityViewId); + if (entityViewById == null) { created = true; - entityView = new EntityView(); - entityView.setTenantId(tenantId); - entityView.setCreatedTime(Uuids.unixTimestamp(entityViewId.getId())); + entityView.setId(null); + } else { + entityView.setId(entityViewId); } + String entityViewName = entityView.getName(); EntityView entityViewByName = entityViewService.findEntityViewByTenantIdAndName(tenantId, entityViewName); if (entityViewByName != null && !entityViewByName.getId().equals(entityViewId)) { entityViewName = entityViewName + "_" + StringUtils.randomAlphanumeric(15); log.warn("[{}] Entity view with name {} already exists. Renaming entity view name to {}", - tenantId, entityViewUpdateMsg.getName(), entityViewName); + tenantId, entityView.getName(), entityViewName); entityViewNameUpdated = true; } entityView.setName(entityViewName); + + entityViewValidator.validate(entityView, EntityView::getTenantId); + if (created) { + entityView.setId(entityViewId); + } + entityViewService.saveEntityView(entityView, false); + return Pair.of(created, entityViewNameUpdated); + } + + private EntityView createEntityView(TenantId tenantId, EntityViewId entityViewId, EntityViewUpdateMsg entityViewUpdateMsg) { + EntityView entityView = new EntityView(); + entityView.setTenantId(tenantId); + entityView.setCreatedTime(Uuids.unixTimestamp(entityViewId.getId())); + entityView.setName(entityViewUpdateMsg.getName()); entityView.setType(entityViewUpdateMsg.getType()); - entityView.setCustomerId(customerId); + entityView.setAdditionalInfo(entityViewUpdateMsg.hasAdditionalInfo() ? JacksonUtil.toJsonNode(entityViewUpdateMsg.getAdditionalInfo()) : null); + CustomerId customerId = safeGetCustomerId(entityViewUpdateMsg.getCustomerIdMSB(), entityViewUpdateMsg.getCustomerIdLSB()); + entityView.setCustomerId(customerId); + UUID entityIdUUID = safeGetUUID(entityViewUpdateMsg.getEntityIdMSB(), entityViewUpdateMsg.getEntityIdLSB()); if (EdgeEntityType.DEVICE.equals(entityViewUpdateMsg.getEntityType())) { entityView.setEntityId(entityIdUUID != null ? new DeviceId(entityIdUUID) : null); } else if (EdgeEntityType.ASSET.equals(entityViewUpdateMsg.getEntityType())) { entityView.setEntityId(entityIdUUID != null ? new AssetId(entityIdUUID) : null); } - - entityViewValidator.validate(entityView, EntityView::getTenantId); - if (created) { - entityView.setId(entityViewId); - } - entityViewService.saveEntityView(entityView, false); - return Pair.of(created, entityViewNameUpdated); + return entityView; } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewEdgeProcessor.java index cd3a0b35ad..41996d8afb 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewEdgeProcessor.java @@ -27,16 +27,17 @@ import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.edge.EdgeEventType; -import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.EntityViewUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; import java.util.UUID; @@ -45,7 +46,7 @@ import java.util.UUID; @TbCoreComponent public class EntityViewEdgeProcessor extends BaseEntityViewProcessor { - public ListenableFuture processEntityViewMsgFromEdge(TenantId tenantId, Edge edge, EntityViewUpdateMsg entityViewUpdateMsg) { + public ListenableFuture processEntityViewMsgFromEdge(TenantId tenantId, Edge edge, EntityViewUpdateMsg entityViewUpdateMsg, EdgeVersion edgeVersion) { log.trace("[{}] executing processEntityViewMsgFromEdge [{}] from edge [{}]", tenantId, entityViewUpdateMsg, edge.getId()); EntityViewId entityViewId = new EntityViewId(new UUID(entityViewUpdateMsg.getIdMSB(), entityViewUpdateMsg.getIdLSB())); try { @@ -54,7 +55,7 @@ public class EntityViewEdgeProcessor extends BaseEntityViewProcessor { switch (entityViewUpdateMsg.getMsgType()) { case ENTITY_CREATED_RPC_MESSAGE: case ENTITY_UPDATED_RPC_MESSAGE: - saveOrUpdateEntityView(tenantId, entityViewId, entityViewUpdateMsg, edge); + saveOrUpdateEntityView(tenantId, entityViewId, entityViewUpdateMsg, edge, edgeVersion); return Futures.immediateFuture(null); case ENTITY_DELETED_RPC_MESSAGE: EntityView entityViewToDelete = entityViewService.findEntityViewById(tenantId, entityViewId); @@ -78,9 +79,9 @@ public class EntityViewEdgeProcessor extends BaseEntityViewProcessor { } } - private void saveOrUpdateEntityView(TenantId tenantId, EntityViewId entityViewId, EntityViewUpdateMsg entityViewUpdateMsg, Edge edge) { - CustomerId customerId = safeGetCustomerId(entityViewUpdateMsg.getCustomerIdMSB(), entityViewUpdateMsg.getCustomerIdLSB()); - Pair resultPair = super.saveOrUpdateEntityView(tenantId, entityViewId, entityViewUpdateMsg, customerId); + private void saveOrUpdateEntityView(TenantId tenantId, EntityViewId entityViewId, EntityViewUpdateMsg entityViewUpdateMsg, Edge edge, EdgeVersion edgeVersion) { + Pair resultPair = super.saveOrUpdateEntityView(tenantId, entityViewId, entityViewUpdateMsg, + EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)); Boolean created = resultPair.getFirst(); if (created) { createRelationFromEdge(tenantId, edge.getId(), entityViewId); @@ -104,7 +105,7 @@ public class EntityViewEdgeProcessor extends BaseEntityViewProcessor { } } - public DownlinkMsg convertEntityViewEventToDownlink(EdgeEvent edgeEvent) { + public DownlinkMsg convertEntityViewEventToDownlink(EdgeEvent edgeEvent, EdgeVersion edgeVersion) { EntityViewId entityViewId = new EntityViewId(edgeEvent.getEntityId()); DownlinkMsg downlinkMsg = null; switch (edgeEvent.getAction()) { @@ -117,7 +118,7 @@ public class EntityViewEdgeProcessor extends BaseEntityViewProcessor { if (entityView != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); EntityViewUpdateMsg entityViewUpdateMsg = - entityViewMsgConstructor.constructEntityViewUpdatedMsg(msgType, entityView); + entityViewMsgConstructor.constructEntityViewUpdatedMsg(msgType, entityView, edgeVersion); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addEntityViewUpdateMsg(entityViewUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/ota/OtaPackageEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/ota/OtaPackageEdgeProcessor.java index fae6399e3a..3c710fd3ee 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/ota/OtaPackageEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/ota/OtaPackageEdgeProcessor.java @@ -22,6 +22,7 @@ import org.thingsboard.server.common.data.OtaPackage; import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.OtaPackageUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; @@ -32,7 +33,7 @@ import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; @TbCoreComponent public class OtaPackageEdgeProcessor extends BaseEdgeProcessor { - public DownlinkMsg convertOtaPackageEventToDownlink(EdgeEvent edgeEvent) { + public DownlinkMsg convertOtaPackageEventToDownlink(EdgeEvent edgeEvent, EdgeVersion edgeVersion) { OtaPackageId otaPackageId = new OtaPackageId(edgeEvent.getEntityId()); DownlinkMsg downlinkMsg = null; switch (edgeEvent.getAction()) { @@ -42,7 +43,7 @@ public class OtaPackageEdgeProcessor extends BaseEdgeProcessor { if (otaPackage != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); OtaPackageUpdateMsg otaPackageUpdateMsg = - otaPackageMsgConstructor.constructOtaPackageUpdatedMsg(msgType, otaPackage); + otaPackageMsgConstructor.constructOtaPackageUpdatedMsg(msgType, otaPackage, edgeVersion); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addOtaPackageUpdateMsg(otaPackageUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/queue/QueueEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/queue/QueueEdgeProcessor.java index 8562582940..0df98dbcac 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/queue/QueueEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/queue/QueueEdgeProcessor.java @@ -22,6 +22,7 @@ import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.id.QueueId; import org.thingsboard.server.common.data.queue.Queue; import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.QueueUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; @@ -32,7 +33,7 @@ import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; @TbCoreComponent public class QueueEdgeProcessor extends BaseEdgeProcessor { - public DownlinkMsg convertQueueEventToDownlink(EdgeEvent edgeEvent) { + public DownlinkMsg convertQueueEventToDownlink(EdgeEvent edgeEvent, EdgeVersion edgeVersion) { QueueId queueId = new QueueId(edgeEvent.getEntityId()); DownlinkMsg downlinkMsg = null; switch (edgeEvent.getAction()) { @@ -42,7 +43,7 @@ public class QueueEdgeProcessor extends BaseEdgeProcessor { if (queue != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); QueueUpdateMsg queueUpdateMsg = - queueMsgConstructor.constructQueueUpdatedMsg(msgType, queue); + queueMsgConstructor.constructQueueUpdatedMsg(msgType, queue, edgeVersion); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addQueueUpdateMsg(queueUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/BaseRelationProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/BaseRelationProcessor.java index 952affdc3d..d673b1a613 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/BaseRelationProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/BaseRelationProcessor.java @@ -25,41 +25,35 @@ import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.RelationTypeGroup; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; +import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; import java.util.UUID; @Slf4j public abstract class BaseRelationProcessor extends BaseEdgeProcessor { - protected ListenableFuture processRelationMsg(TenantId tenantId, RelationUpdateMsg relationUpdateMsg) { + protected ListenableFuture processRelationMsg(TenantId tenantId, RelationUpdateMsg relationUpdateMsg, EdgeVersion edgeVersion) { + log.trace("[{}] processRelationMsg [{}]", tenantId, relationUpdateMsg); try { - EntityRelation entityRelation = new EntityRelation(); - - UUID fromUUID = new UUID(relationUpdateMsg.getFromIdMSB(), relationUpdateMsg.getFromIdLSB()); - EntityId fromId = EntityIdFactory.getByTypeAndUuid(EntityType.valueOf(relationUpdateMsg.getFromEntityType()), fromUUID); - entityRelation.setFrom(fromId); - - UUID toUUID = new UUID(relationUpdateMsg.getToIdMSB(), relationUpdateMsg.getToIdLSB()); - EntityId toId = EntityIdFactory.getByTypeAndUuid(EntityType.valueOf(relationUpdateMsg.getToEntityType()), toUUID); - entityRelation.setTo(toId); - - entityRelation.setType(relationUpdateMsg.getType()); - entityRelation.setTypeGroup(relationUpdateMsg.hasTypeGroup() - ? RelationTypeGroup.valueOf(relationUpdateMsg.getTypeGroup()) : RelationTypeGroup.COMMON); - entityRelation.setAdditionalInfo(JacksonUtil.toJsonNode(relationUpdateMsg.getAdditionalInfo())); + EntityRelation entityRelation = EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion) + ? createEntityRelation(relationUpdateMsg) + : JacksonUtil.fromStringIgnoreUnknownProperties(relationUpdateMsg.getEntity(), EntityRelation.class); + if (entityRelation == null) { + throw new RuntimeException("[{" + tenantId + "}] relationUpdateMsg {" + relationUpdateMsg + "} cannot be converted to entity relation"); + } switch (relationUpdateMsg.getMsgType()) { case ENTITY_CREATED_RPC_MESSAGE: case ENTITY_UPDATED_RPC_MESSAGE: if (isEntityExists(tenantId, entityRelation.getTo()) && isEntityExists(tenantId, entityRelation.getFrom())) { relationService.saveRelation(tenantId, entityRelation); - break; } else { log.warn("[{}] Skipping relating update msg because from/to entity doesn't exists on edge, {}", tenantId, relationUpdateMsg); - break; } + break; case ENTITY_DELETED_RPC_MESSAGE: relationService.deleteRelation(tenantId, entityRelation); break; @@ -73,4 +67,22 @@ public abstract class BaseRelationProcessor extends BaseEdgeProcessor { } return Futures.immediateFuture(null); } + + private EntityRelation createEntityRelation(RelationUpdateMsg relationUpdateMsg) { + EntityRelation entityRelation = new EntityRelation(); + + UUID fromUUID = new UUID(relationUpdateMsg.getFromIdMSB(), relationUpdateMsg.getFromIdLSB()); + EntityId fromId = EntityIdFactory.getByTypeAndUuid(EntityType.valueOf(relationUpdateMsg.getFromEntityType()), fromUUID); + entityRelation.setFrom(fromId); + + UUID toUUID = new UUID(relationUpdateMsg.getToIdMSB(), relationUpdateMsg.getToIdLSB()); + EntityId toId = EntityIdFactory.getByTypeAndUuid(EntityType.valueOf(relationUpdateMsg.getToEntityType()), toUUID); + entityRelation.setTo(toId); + + entityRelation.setType(relationUpdateMsg.getType()); + entityRelation.setTypeGroup(relationUpdateMsg.hasTypeGroup() + ? RelationTypeGroup.valueOf(relationUpdateMsg.getTypeGroup()) : RelationTypeGroup.COMMON); + entityRelation.setAdditionalInfo(JacksonUtil.toJsonNode(relationUpdateMsg.getAdditionalInfo())); + return entityRelation; + } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessor.java index b3676e8d82..84065d1c8a 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessor.java @@ -30,6 +30,7 @@ import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.transport.TransportProtos; @@ -56,10 +57,10 @@ public class RelationEdgeProcessor extends BaseRelationProcessor { } } - public DownlinkMsg convertRelationEventToDownlink(EdgeEvent edgeEvent) { + public DownlinkMsg convertRelationEventToDownlink(EdgeEvent edgeEvent, EdgeVersion edgeVersion) { EntityRelation entityRelation = JacksonUtil.convertValue(edgeEvent.getBody(), EntityRelation.class); UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); - RelationUpdateMsg relationUpdateMsg = relationMsgConstructor.constructRelationUpdatedMsg(msgType, entityRelation); + RelationUpdateMsg relationUpdateMsg = relationMsgConstructor.constructRelationUpdatedMsg(msgType, entityRelation, edgeVersion); return DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addRelationUpdateMsg(relationUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/rule/RuleChainEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/rule/RuleChainEdgeProcessor.java index ba6fc1ef97..d480f9e4cd 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/rule/RuleChainEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/rule/RuleChainEdgeProcessor.java @@ -37,7 +37,7 @@ import static org.thingsboard.server.service.edge.DefaultEdgeNotificationService @TbCoreComponent public class RuleChainEdgeProcessor extends BaseEdgeProcessor { - public DownlinkMsg convertRuleChainEventToDownlink(EdgeEvent edgeEvent) { + public DownlinkMsg convertRuleChainEventToDownlink(EdgeEvent edgeEvent, EdgeVersion edgeVersion) { RuleChainId ruleChainId = new RuleChainId(edgeEvent.getEntityId()); DownlinkMsg downlinkMsg = null; switch (edgeEvent.getAction()) { @@ -54,7 +54,7 @@ public class RuleChainEdgeProcessor extends BaseEdgeProcessor { } UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); RuleChainUpdateMsg ruleChainUpdateMsg = - ruleChainMsgConstructor.constructRuleChainUpdatedMsg(msgType, ruleChain, isRoot); + ruleChainMsgConstructor.constructRuleChainUpdatedMsg(msgType, ruleChain, isRoot, edgeVersion); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addRuleChainUpdateMsg(ruleChainUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/settings/AdminSettingsEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/settings/AdminSettingsEdgeProcessor.java index bbd9edd172..735c4de399 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/settings/AdminSettingsEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/settings/AdminSettingsEdgeProcessor.java @@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.gen.edge.v1.AdminSettingsUpdateMsg; import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; @@ -31,16 +32,15 @@ import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; @TbCoreComponent public class AdminSettingsEdgeProcessor extends BaseEdgeProcessor { - public DownlinkMsg convertAdminSettingsEventToDownlink(EdgeEvent edgeEvent) { + public DownlinkMsg convertAdminSettingsEventToDownlink(EdgeEvent edgeEvent, EdgeVersion edgeVersion) { AdminSettings adminSettings = JacksonUtil.convertValue(edgeEvent.getBody(), AdminSettings.class); if (adminSettings == null) { return null; } - AdminSettingsUpdateMsg adminSettingsUpdateMsg = adminSettingsMsgConstructor.constructAdminSettingsUpdateMsg(adminSettings); + AdminSettingsUpdateMsg adminSettingsUpdateMsg = adminSettingsMsgConstructor.constructAdminSettingsUpdateMsg(adminSettings, edgeVersion); return DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addAdminSettingsUpdateMsg(adminSettingsUpdateMsg) .build(); } - } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantEdgeProcessor.java index 9fd08b4447..f4b0c5a355 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantEdgeProcessor.java @@ -43,7 +43,7 @@ public class TenantEdgeProcessor extends BaseEdgeProcessor { Tenant tenant = tenantService.findTenantById(tenantId); if (tenant != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); - TenantUpdateMsg tenantUpdateMsg = tenantMsgConstructor.constructTenantUpdateMsg(msgType, tenant); + TenantUpdateMsg tenantUpdateMsg = tenantMsgConstructor.constructTenantUpdateMsg(msgType, tenant, edgeVersion); TenantProfile tenantProfile = tenantProfileService.findTenantProfileById(tenantId, tenant.getTenantProfileId()); TenantProfileUpdateMsg tenantProfileUpdateMsg = tenantProfileMsgConstructor.constructTenantProfileUpdateMsg(msgType, tenantProfile, edgeVersion); downlinkMsg = DownlinkMsg.newBuilder() diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/user/UserEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/user/UserEdgeProcessor.java index de40fdb1a3..3a3b00f4c8 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/user/UserEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/user/UserEdgeProcessor.java @@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.security.UserCredentials; import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.UserCredentialsUpdateMsg; import org.thingsboard.server.queue.util.TbCoreComponent; @@ -33,7 +34,7 @@ import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; @TbCoreComponent public class UserEdgeProcessor extends BaseEdgeProcessor { - public DownlinkMsg convertUserEventToDownlink(EdgeEvent edgeEvent) { + public DownlinkMsg convertUserEventToDownlink(EdgeEvent edgeEvent, EdgeVersion edgeVersion) { UserId userId = new UserId(edgeEvent.getEntityId()); DownlinkMsg downlinkMsg = null; switch (edgeEvent.getAction()) { @@ -44,7 +45,7 @@ public class UserEdgeProcessor extends BaseEdgeProcessor { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) - .addUserUpdateMsg(userMsgConstructor.constructUserUpdatedMsg(msgType, user)) + .addUserUpdateMsg(userMsgConstructor.constructUserUpdatedMsg(msgType, user, edgeVersion)) .build(); } break; @@ -58,7 +59,7 @@ public class UserEdgeProcessor extends BaseEdgeProcessor { UserCredentials userCredentialsByUserId = userService.findUserCredentialsByUserId(edgeEvent.getTenantId(), userId); if (userCredentialsByUserId != null && userCredentialsByUserId.isEnabled()) { UserCredentialsUpdateMsg userCredentialsUpdateMsg = - userMsgConstructor.constructUserCredentialsUpdatedMsg(userCredentialsByUserId); + userMsgConstructor.constructUserCredentialsUpdatedMsg(userCredentialsByUserId, edgeVersion); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addUserCredentialsUpdateMsg(userCredentialsUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetBundleEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetBundleEdgeProcessor.java index 877ece1b15..abef3a1ab5 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetBundleEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetBundleEdgeProcessor.java @@ -22,6 +22,7 @@ import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.id.WidgetsBundleId; import org.thingsboard.server.common.data.widget.WidgetsBundle; import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.WidgetsBundleUpdateMsg; import org.thingsboard.server.queue.util.TbCoreComponent; @@ -34,7 +35,7 @@ import java.util.List; @TbCoreComponent public class WidgetBundleEdgeProcessor extends BaseEdgeProcessor { - public DownlinkMsg convertWidgetsBundleEventToDownlink(EdgeEvent edgeEvent) { + public DownlinkMsg convertWidgetsBundleEventToDownlink(EdgeEvent edgeEvent, EdgeVersion edgeVersion) { WidgetsBundleId widgetsBundleId = new WidgetsBundleId(edgeEvent.getEntityId()); DownlinkMsg downlinkMsg = null; switch (edgeEvent.getAction()) { @@ -45,7 +46,7 @@ public class WidgetBundleEdgeProcessor extends BaseEdgeProcessor { List widgets = widgetTypeService.findWidgetFqnsByWidgetsBundleId(edgeEvent.getTenantId(), widgetsBundleId); UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); WidgetsBundleUpdateMsg widgetsBundleUpdateMsg = - widgetsBundleMsgConstructor.constructWidgetsBundleUpdateMsg(msgType, widgetsBundle, widgets); + widgetsBundleMsgConstructor.constructWidgetsBundleUpdateMsg(msgType, widgetsBundle, widgets, edgeVersion); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addWidgetsBundleUpdateMsg(widgetsBundleUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/utils/EdgeVersionUtils.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/utils/EdgeVersionUtils.java index c9e6987a23..dc66e50748 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/utils/EdgeVersionUtils.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/utils/EdgeVersionUtils.java @@ -24,4 +24,8 @@ public final class EdgeVersionUtils { public static boolean isEdgeVersionOlderThan(EdgeVersion currentVersion, EdgeVersion requiredVersion) { return currentVersion.ordinal() < requiredVersion.ordinal(); } + + public static boolean isEdgeVersionOlderThan_3_6_2(EdgeVersion currentVersion) { + return isEdgeVersionOlderThan(currentVersion, EdgeVersion.V_3_6_2); + } } diff --git a/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java index f0c47bb53a..1f7715ddc2 100644 --- a/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java @@ -37,14 +37,18 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestPropertySource; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardExecutors; +import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.id.CustomerId; @@ -53,6 +57,8 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantProfileId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.queue.Queue; +import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.model.JwtSettings; import org.thingsboard.server.dao.edge.EdgeDao; @@ -78,7 +84,6 @@ import org.thingsboard.server.gen.edge.v1.UserUpdateMsg; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.UUID; import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; @@ -121,7 +126,7 @@ public class EdgeControllerTest extends AbstractControllerTest { } @After - public void teardownEdgeTest() throws Exception { + public void teardownEdgeTest() { executor.shutdownNow(); } @@ -204,15 +209,13 @@ public class EdgeControllerTest extends AbstractControllerTest { @Test public void testFindEdgeTypesByTenantId() throws Exception { - List edges = new ArrayList<>(); - int cntEntity = 3; Mockito.reset(tbClusterService, auditLogService); for (int i = 0; i < cntEntity; i++) { Edge edge = constructEdge("My edge B" + i, "typeB"); - edges.add(doPost("/api/edge", edge, Edge.class)); + doPost("/api/edge", edge, Edge.class); } testNotifyManyEntityManyTimeMsgToEdgeServiceNeverAdditionalInfoAny(new Edge(), new Edge(), @@ -221,11 +224,11 @@ public class EdgeControllerTest extends AbstractControllerTest { for (int i = 0; i < 7; i++) { Edge edge = constructEdge("My edge C" + i, "typeC"); - edges.add(doPost("/api/edge", edge, Edge.class)); + doPost("/api/edge", edge, Edge.class); } for (int i = 0; i < 9; i++) { Edge edge = constructEdge("My edge A" + i, "typeA"); - edges.add(doPost("/api/edge", edge, Edge.class)); + doPost("/api/edge", edge, Edge.class); } List edgeTypes = doGetTyped("/api/edge/types", new TypeReference<>() { @@ -395,7 +398,7 @@ public class EdgeControllerTest extends AbstractControllerTest { List edges = new ArrayList<>(Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS)); List loadedEdges = new ArrayList<>(); PageLink pageLink = new PageLink(23); - PageData pageData = null; + PageData pageData; do { pageData = doGetTypedWithPageLink("/api/tenant/edges?", new TypeReference<>() { @@ -442,10 +445,10 @@ public class EdgeControllerTest extends AbstractControllerTest { List loadedEdgesTitle1 = new ArrayList<>(); PageLink pageLink = new PageLink(15, 0, title1); - PageData pageData = null; + PageData pageData; do { pageData = doGetTypedWithPageLink("/api/tenant/edges?", - new TypeReference>() { + new TypeReference<>() { }, pageLink); loadedEdgesTitle1.addAll(pageData.getData()); if (pageData.hasNext()) { @@ -462,7 +465,7 @@ public class EdgeControllerTest extends AbstractControllerTest { pageLink = new PageLink(4, 0, title2); do { pageData = doGetTypedWithPageLink("/api/tenant/edges?", - new TypeReference>() { + new TypeReference<>() { }, pageLink); loadedEdgesTitle2.addAll(pageData.getData()); if (pageData.hasNext()) { @@ -532,10 +535,10 @@ public class EdgeControllerTest extends AbstractControllerTest { List loadedEdgesType1 = new ArrayList<>(); PageLink pageLink = new PageLink(15); - PageData pageData = null; + PageData pageData; do { pageData = doGetTypedWithPageLink("/api/tenant/edges?type={type}&", - new TypeReference>() { + new TypeReference<>() { }, pageLink, type1); loadedEdgesType1.addAll(pageData.getData()); if (pageData.hasNext()) { @@ -552,7 +555,7 @@ public class EdgeControllerTest extends AbstractControllerTest { pageLink = new PageLink(4); do { pageData = doGetTypedWithPageLink("/api/tenant/edges?type={type}&", - new TypeReference>() { + new TypeReference<>() { }, pageLink, type2); loadedEdgesType2.addAll(pageData.getData()); if (pageData.hasNext()) { @@ -572,7 +575,7 @@ public class EdgeControllerTest extends AbstractControllerTest { pageLink = new PageLink(4); pageData = doGetTypedWithPageLink("/api/tenant/edges?type={type}&", - new TypeReference>() { + new TypeReference<>() { }, pageLink, type1); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); @@ -584,7 +587,7 @@ public class EdgeControllerTest extends AbstractControllerTest { pageLink = new PageLink(4); pageData = doGetTypedWithPageLink("/api/tenant/edges?type={type}&", - new TypeReference>() { + new TypeReference<>() { }, pageLink, type2); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); @@ -613,15 +616,14 @@ public class EdgeControllerTest extends AbstractControllerTest { testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new Edge(), new Edge(), tenantId, customerId, tenantAdminUser.getId(), tenantAdminUser.getEmail(), - ActionType.ASSIGNED_TO_CUSTOMER, cntEntity, cntEntity, cntEntity * 2, - new String(), new String(), new String()); + ActionType.ASSIGNED_TO_CUSTOMER, cntEntity, cntEntity, cntEntity * 2, "", "", ""); List loadedEdges = new ArrayList<>(); PageLink pageLink = new PageLink(23); PageData pageData = null; do { pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/edges?", - new TypeReference>() { + new TypeReference<>() { }, pageLink); loadedEdges.addAll(pageData.getData()); if (pageData.hasNext()) { @@ -679,7 +681,7 @@ public class EdgeControllerTest extends AbstractControllerTest { PageData pageData = null; do { pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/edges?", - new TypeReference>() { + new TypeReference<>() { }, pageLink); loadedEdgesTitle1.addAll(pageData.getData()); if (pageData.hasNext()) { @@ -696,7 +698,7 @@ public class EdgeControllerTest extends AbstractControllerTest { pageLink = new PageLink(4, 0, title2); do { pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/edges?", - new TypeReference>() { + new TypeReference<>() { }, pageLink); loadedEdgesTitle2.addAll(pageData.getData()); if (pageData.hasNext()) { @@ -723,7 +725,7 @@ public class EdgeControllerTest extends AbstractControllerTest { pageLink = new PageLink(4, 0, title1); pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/edges?", - new TypeReference>() { + new TypeReference<>() { }, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); @@ -735,7 +737,7 @@ public class EdgeControllerTest extends AbstractControllerTest { pageLink = new PageLink(4, 0, title2); pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/edges?", - new TypeReference>() { + new TypeReference<>() { }, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); @@ -784,10 +786,10 @@ public class EdgeControllerTest extends AbstractControllerTest { List loadedEdgesType1 = new ArrayList<>(); PageLink pageLink = new PageLink(15, 0, title1); - PageData pageData = null; + PageData pageData; do { pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/edges?type={type}&", - new TypeReference>() { + new TypeReference<>() { }, pageLink, type1); loadedEdgesType1.addAll(pageData.getData()); if (pageData.hasNext()) { @@ -804,7 +806,7 @@ public class EdgeControllerTest extends AbstractControllerTest { pageLink = new PageLink(4, 0, title2); do { pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/edges?type={type}&", - new TypeReference>() { + new TypeReference<>() { }, pageLink, type2); loadedEdgesType2.addAll(pageData.getData()); if (pageData.hasNext()) { @@ -824,7 +826,7 @@ public class EdgeControllerTest extends AbstractControllerTest { pageLink = new PageLink(4, 0, title1); pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/edges?type={type}&", - new TypeReference>() { + new TypeReference<>() { }, pageLink, type1); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); @@ -836,7 +838,7 @@ public class EdgeControllerTest extends AbstractControllerTest { pageLink = new PageLink(4, 0, title2); pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/edges?type={type}&", - new TypeReference>() { + new TypeReference<>() { }, pageLink, type2); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); @@ -936,8 +938,9 @@ public class EdgeControllerTest extends AbstractControllerTest { for (AbstractMessage message : messages) { if (message instanceof QueueUpdateMsg) { QueueUpdateMsg queueUpdateMsg = (QueueUpdateMsg) message; - if (msgType.equals(queueUpdateMsg.getMsgType()) - && name.equals(queueUpdateMsg.getName())) { + Queue queue = JacksonUtil.fromStringIgnoreUnknownProperties(queueUpdateMsg.getEntity(), Queue.class); + Assert.assertNotNull(queue); + if (msgType.equals(queueUpdateMsg.getMsgType()) && name.equals(queue.getName())) { messages.remove(message); return true; } @@ -950,9 +953,11 @@ public class EdgeControllerTest extends AbstractControllerTest { for (AbstractMessage message : messages) { if (message instanceof RuleChainUpdateMsg) { RuleChainUpdateMsg ruleChainUpdateMsg = (RuleChainUpdateMsg) message; + RuleChain ruleChain = JacksonUtil.fromStringIgnoreUnknownProperties(ruleChainUpdateMsg.getEntity(), RuleChain.class); + Assert.assertNotNull(ruleChain); if (msgType.equals(ruleChainUpdateMsg.getMsgType()) - && name.equals(ruleChainUpdateMsg.getName()) - && ruleChainUpdateMsg.getRoot()) { + && name.equals(ruleChain.getName()) + && ruleChain.isRoot()) { messages.remove(message); return true; } @@ -965,7 +970,9 @@ public class EdgeControllerTest extends AbstractControllerTest { for (AbstractMessage message : messages) { if (message instanceof AdminSettingsUpdateMsg) { AdminSettingsUpdateMsg adminSettingsUpdateMsg = (AdminSettingsUpdateMsg) message; - if (key.equals(adminSettingsUpdateMsg.getKey())) { + AdminSettings adminSettings = JacksonUtil.fromStringIgnoreUnknownProperties(adminSettingsUpdateMsg.getEntity(), AdminSettings.class); + Assert.assertNotNull(adminSettings); + if (key.equals(adminSettings.getKey())) { messages.remove(message); return true; } @@ -978,8 +985,10 @@ public class EdgeControllerTest extends AbstractControllerTest { for (AbstractMessage message : messages) { if (message instanceof DeviceProfileUpdateMsg) { DeviceProfileUpdateMsg deviceProfileUpdateMsg = (DeviceProfileUpdateMsg) message; + DeviceProfile deviceProfile = JacksonUtil.fromStringIgnoreUnknownProperties(deviceProfileUpdateMsg.getEntity(), DeviceProfile.class); + Assert.assertNotNull(deviceProfile); if (msgType.equals(deviceProfileUpdateMsg.getMsgType()) - && name.equals(deviceProfileUpdateMsg.getName())) { + && name.equals(deviceProfile.getName())) { messages.remove(message); return true; } @@ -992,8 +1001,10 @@ public class EdgeControllerTest extends AbstractControllerTest { for (AbstractMessage message : messages) { if (message instanceof DeviceUpdateMsg) { DeviceUpdateMsg deviceUpdateMsg = (DeviceUpdateMsg) message; + Device device = JacksonUtil.fromStringIgnoreUnknownProperties(deviceUpdateMsg.getEntity(), Device.class); + Assert.assertNotNull(device); if (msgType.equals(deviceUpdateMsg.getMsgType()) - && name.equals(deviceUpdateMsg.getName())) { + && name.equals(device.getName())) { messages.remove(message); return true; } @@ -1006,8 +1017,10 @@ public class EdgeControllerTest extends AbstractControllerTest { for (AbstractMessage message : messages) { if (message instanceof AssetProfileUpdateMsg) { AssetProfileUpdateMsg assetProfileUpdateMsg = (AssetProfileUpdateMsg) message; + AssetProfile assetProfile = JacksonUtil.fromStringIgnoreUnknownProperties(assetProfileUpdateMsg.getEntity(), AssetProfile.class); + Assert.assertNotNull(assetProfile); if (msgType.equals(assetProfileUpdateMsg.getMsgType()) - && name.equals(assetProfileUpdateMsg.getName())) { + && name.equals(assetProfile.getName())) { messages.remove(message); return true; } @@ -1020,8 +1033,10 @@ public class EdgeControllerTest extends AbstractControllerTest { for (AbstractMessage message : messages) { if (message instanceof AssetUpdateMsg) { AssetUpdateMsg assetUpdateMsg = (AssetUpdateMsg) message; + Asset asset = JacksonUtil.fromStringIgnoreUnknownProperties(assetUpdateMsg.getEntity(), Asset.class); + Assert.assertNotNull(asset); if (msgType.equals(assetUpdateMsg.getMsgType()) - && name.equals(assetUpdateMsg.getName())) { + && name.equals(asset.getName())) { messages.remove(message); return true; } @@ -1034,9 +1049,11 @@ public class EdgeControllerTest extends AbstractControllerTest { for (AbstractMessage message : messages) { if (message instanceof UserUpdateMsg) { UserUpdateMsg userUpdateMsg = (UserUpdateMsg) message; + User user = JacksonUtil.fromStringIgnoreUnknownProperties(userUpdateMsg.getEntity(), User.class); + Assert.assertNotNull(user); if (msgType.equals(userUpdateMsg.getMsgType()) - && email.equals(userUpdateMsg.getEmail()) - && authority.name().equals(userUpdateMsg.getAuthority())) { + && email.equals(user.getEmail()) + && authority.equals(user.getAuthority())) { messages.remove(message); return true; } @@ -1049,8 +1066,10 @@ public class EdgeControllerTest extends AbstractControllerTest { for (AbstractMessage message : messages) { if (message instanceof CustomerUpdateMsg) { CustomerUpdateMsg customerUpdateMsg = (CustomerUpdateMsg) message; + Customer customer = JacksonUtil.fromStringIgnoreUnknownProperties(customerUpdateMsg.getEntity(), Customer.class); + Assert.assertNotNull(customer); if (msgType.equals(customerUpdateMsg.getMsgType()) - && title.equals(customerUpdateMsg.getTitle())) { + && title.equals(customer.getTitle())) { messages.remove(message); return true; } @@ -1063,9 +1082,10 @@ public class EdgeControllerTest extends AbstractControllerTest { for (AbstractMessage message : messages) { if (message instanceof TenantUpdateMsg) { TenantUpdateMsg tenantUpdateMsg = (TenantUpdateMsg) message; - TenantId tenantIdMsg = new TenantId(new UUID(tenantUpdateMsg.getIdMSB(), tenantUpdateMsg.getIdLSB())); + Tenant tenant = JacksonUtil.fromStringIgnoreUnknownProperties(tenantUpdateMsg.getEntity(), Tenant.class); + Assert.assertNotNull(tenant); if (UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE.equals(tenantUpdateMsg.getMsgType()) - && tenantId1.equals(tenantIdMsg)) { + && tenantId1.equals(tenant.getId())) { messages.remove(message); return true; } @@ -1078,9 +1098,10 @@ public class EdgeControllerTest extends AbstractControllerTest { for (AbstractMessage message : messages) { if (message instanceof TenantProfileUpdateMsg) { TenantProfileUpdateMsg tenantProfileUpdateMsg = (TenantProfileUpdateMsg) message; - TenantProfileId tenantProfileIdMsg = new TenantProfileId(new UUID(tenantProfileUpdateMsg.getIdMSB(), tenantProfileUpdateMsg.getIdLSB())); + TenantProfile tenantProfile = JacksonUtil.fromStringIgnoreUnknownProperties(tenantProfileUpdateMsg.getEntity(), TenantProfile.class); + Assert.assertNotNull(tenantProfile); if (UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE.equals(tenantProfileUpdateMsg.getMsgType()) - && tenantProfileId.equals(tenantProfileIdMsg)) { + && tenantProfileId.equals(tenantProfile.getId())) { messages.remove(message); return true; } diff --git a/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java index 2771a71eac..b2bce41bf6 100644 --- a/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java @@ -28,6 +28,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.TestPropertySource; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.cluster.TbClusterService; +import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.DataConstants; @@ -38,6 +39,7 @@ import org.thingsboard.server.common.data.OtaPackageInfo; import org.thingsboard.server.common.data.SaveOtaPackageInfoRequest; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.asset.Asset; @@ -70,6 +72,7 @@ import org.thingsboard.server.common.data.query.FilterPredicateValue; import org.thingsboard.server.common.data.query.NumericFilterPredicate; import org.thingsboard.server.common.data.queue.Queue; import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.data.security.model.JwtSettings; import org.thingsboard.server.controller.AbstractControllerTest; @@ -318,8 +321,9 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { Assert.assertTrue(tenantUpdateMsgOpt.isPresent()); TenantUpdateMsg tenantUpdateMsg = tenantUpdateMsgOpt.get(); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, tenantUpdateMsg.getMsgType()); - UUID tenantUUID = new UUID(tenantUpdateMsg.getIdMSB(), tenantUpdateMsg.getIdLSB()); - Tenant tenant = doGet("/api/tenant/" + tenantUUID, Tenant.class); + Tenant tenantMsg = JacksonUtil.fromStringIgnoreUnknownProperties(tenantUpdateMsg.getEntity(), Tenant.class); + Assert.assertNotNull(tenantMsg); + Tenant tenant = doGet("/api/tenant/" + tenantMsg.getUuidId(), Tenant.class); Assert.assertNotNull(tenant); testAutoGeneratedCodeByProtobuf(tenantUpdateMsg); } @@ -329,10 +333,11 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { Assert.assertTrue(tenantProfileUpdateMsgOpt.isPresent()); TenantProfileUpdateMsg tenantProfileUpdateMsg = tenantProfileUpdateMsgOpt.get(); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, tenantProfileUpdateMsg.getMsgType()); - UUID tenantProfileUUID = new UUID(tenantProfileUpdateMsg.getIdMSB(), tenantProfileUpdateMsg.getIdLSB()); + TenantProfile tenantProfile = JacksonUtil.fromStringIgnoreUnknownProperties(tenantProfileUpdateMsg.getEntity(), TenantProfile.class); + Assert.assertNotNull(tenantProfile); Tenant tenant = doGet("/api/tenant/" + tenantId.getId(), Tenant.class); Assert.assertNotNull(tenant); - Assert.assertEquals(tenantProfileUUID, tenant.getTenantProfileId().getId()); + Assert.assertEquals(tenantProfile.getId(), tenant.getTenantProfileId()); testAutoGeneratedCodeByProtobuf(tenantProfileUpdateMsg); } @@ -344,7 +349,11 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { // thermostat msg from device fetcher Assert.assertEquals(expectedMsgCnt, deviceProfileUpdateMsgList.size()); Optional thermostatProfileUpdateMsgOpt = - deviceProfileUpdateMsgList.stream().filter(dfum -> THERMOSTAT_DEVICE_PROFILE_NAME.equals(dfum.getName())).findAny(); + deviceProfileUpdateMsgList.stream().filter(dfum -> { + DeviceProfile deviceProfile = JacksonUtil.fromStringIgnoreUnknownProperties(dfum.getEntity(), DeviceProfile.class); + Assert.assertNotNull(deviceProfile); + return THERMOSTAT_DEVICE_PROFILE_NAME.equals(deviceProfile.getName()); + }).findAny(); Assert.assertTrue(thermostatProfileUpdateMsgOpt.isPresent()); DeviceProfileUpdateMsg thermostatProfileUpdateMsg = thermostatProfileUpdateMsgOpt.get(); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, thermostatProfileUpdateMsg.getMsgType()); @@ -419,8 +428,9 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { Assert.assertTrue(ruleChainMetadataUpdateOpt.isPresent()); RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = ruleChainMetadataUpdateOpt.get(); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, ruleChainMetadataUpdateMsg.getMsgType()); - UUID ruleChainUUID = new UUID(ruleChainMetadataUpdateMsg.getRuleChainIdMSB(), ruleChainMetadataUpdateMsg.getRuleChainIdLSB()); - Assert.assertEquals(expectedRuleChainUUID, ruleChainUUID); + RuleChainMetaData ruleChainMetaData = JacksonUtil.fromStringIgnoreUnknownProperties(ruleChainMetadataUpdateMsg.getEntity(), RuleChainMetaData.class); + Assert.assertNotNull(ruleChainMetaData); + Assert.assertEquals(expectedRuleChainUUID, ruleChainMetaData.getRuleChainId().getId()); } private void validateAdminSettings(int expectedMsgCnt) { @@ -428,25 +438,26 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { Assert.assertEquals(expectedMsgCnt, adminSettingsUpdateMsgs.size()); for (AdminSettingsUpdateMsg adminSettingsUpdateMsg : adminSettingsUpdateMsgs) { - if (adminSettingsUpdateMsg.getKey().equals("general")) { - validateGeneralAdminSettings(adminSettingsUpdateMsg); + AdminSettings adminSettings = JacksonUtil.fromStringIgnoreUnknownProperties(adminSettingsUpdateMsg.getEntity(), AdminSettings.class); + Assert.assertNotNull(adminSettings); + if (adminSettings.getKey().equals("general")) { + validateGeneralAdminSettings(adminSettings); } - if (adminSettingsUpdateMsg.getKey().equals("mail")) { - validateMailAdminSettings(adminSettingsUpdateMsg); + if (adminSettings.getKey().equals("mail")) { + validateMailAdminSettings(adminSettings); } - if (adminSettingsUpdateMsg.getKey().equals("connectivity")) { - validateConnectivityAdminSettings(adminSettingsUpdateMsg); + if (adminSettings.getKey().equals("connectivity")) { + validateConnectivityAdminSettings(adminSettings); } } } - private void validateGeneralAdminSettings(AdminSettingsUpdateMsg adminSettingsUpdateMsg) { - JsonNode jsonNode = JacksonUtil.toJsonNode(adminSettingsUpdateMsg.getJsonValue()); - Assert.assertNotNull(jsonNode.get("baseUrl")); + private void validateGeneralAdminSettings(AdminSettings adminSettings) { + Assert.assertNotNull(adminSettings.getJsonValue().get("baseUrl")); } - private void validateMailAdminSettings(AdminSettingsUpdateMsg adminSettingsUpdateMsg) { - JsonNode jsonNode = JacksonUtil.toJsonNode(adminSettingsUpdateMsg.getJsonValue()); + private void validateMailAdminSettings(AdminSettings adminSettings) { + JsonNode jsonNode = adminSettings.getJsonValue(); Assert.assertNotNull(jsonNode.get("mailFrom")); Assert.assertNotNull(jsonNode.get("smtpProtocol")); Assert.assertNotNull(jsonNode.get("smtpHost")); @@ -454,8 +465,8 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { Assert.assertNotNull(jsonNode.get("timeout")); } - private void validateConnectivityAdminSettings(AdminSettingsUpdateMsg adminSettingsUpdateMsg) { - JsonNode jsonNode = JacksonUtil.toJsonNode(adminSettingsUpdateMsg.getJsonValue()); + private void validateConnectivityAdminSettings(AdminSettings adminSettings) { + JsonNode jsonNode = adminSettings.getJsonValue(); Assert.assertNotNull(jsonNode.get("http")); Assert.assertNotNull(jsonNode.get("https")); Assert.assertNotNull(jsonNode.get("mqtt")); @@ -485,10 +496,10 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { UUID queueUUID = new UUID(queueUpdateMsg.getIdMSB(), queueUpdateMsg.getIdLSB()); Queue queue = doGet("/api/queues/" + queueUUID, Queue.class); Assert.assertNotNull(queue); - Assert.assertEquals(DataConstants.MAIN_QUEUE_NAME, queueUpdateMsg.getName()); - Assert.assertEquals(DataConstants.MAIN_QUEUE_TOPIC, queueUpdateMsg.getTopic()); - Assert.assertEquals(10, queueUpdateMsg.getPartitions()); - Assert.assertEquals(25, queueUpdateMsg.getPollInterval()); + Assert.assertEquals(DataConstants.MAIN_QUEUE_NAME, queue.getName()); + Assert.assertEquals(DataConstants.MAIN_QUEUE_TOPIC, queue.getTopic()); + Assert.assertEquals(10, queue.getPartitions()); + Assert.assertEquals(25, queue.getPollInterval()); testAutoGeneratedCodeByProtobuf(queueUpdateMsg); } @@ -500,7 +511,7 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { UUID userUUID = new UUID(userUpdateMsg.getIdMSB(), userUpdateMsg.getIdLSB()); User user = doGet("/api/user/" + userUUID, User.class); Assert.assertNotNull(user); - Assert.assertEquals("testtenant@thingsboard.org", userUpdateMsg.getEmail()); + Assert.assertEquals("testtenant@thingsboard.org", user.getEmail()); testAutoGeneratedCodeByProtobuf(userUpdateMsg); } diff --git a/application/src/test/java/org/thingsboard/server/edge/AlarmEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/AlarmEdgeTest.java index 259572851c..634b16e248 100644 --- a/application/src/test/java/org/thingsboard/server/edge/AlarmEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/AlarmEdgeTest.java @@ -19,13 +19,14 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.google.protobuf.AbstractMessage; import org.junit.Assert; import org.junit.Test; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Device; -import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.alarm.AlarmStatus; import org.thingsboard.server.common.data.id.AlarmId; +import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.service.DaoSqlTest; @@ -46,16 +47,14 @@ public class AlarmEdgeTest extends AbstractEdgeTest { public void testSendAlarmToCloud() throws Exception { Device device = saveDeviceOnCloudAndVerifyDeliveryToEdge(); - UUID alarmUUID = UUID.randomUUID(); + Alarm edgeAlarm = buildAlarmForUplinkMsg(device.getId()); + UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); AlarmUpdateMsg.Builder alarmUpdateMgBuilder = AlarmUpdateMsg.newBuilder(); - alarmUpdateMgBuilder.setIdMSB(alarmUUID.getMostSignificantBits()); - alarmUpdateMgBuilder.setIdLSB(alarmUUID.getLeastSignificantBits()); - alarmUpdateMgBuilder.setName("alarm from edge"); - alarmUpdateMgBuilder.setStatus(AlarmStatus.ACTIVE_UNACK.name()); - alarmUpdateMgBuilder.setSeverity(AlarmSeverity.CRITICAL.name()); + alarmUpdateMgBuilder.setIdMSB(edgeAlarm.getUuidId().getMostSignificantBits()); + alarmUpdateMgBuilder.setIdLSB(edgeAlarm.getUuidId().getLeastSignificantBits()); + alarmUpdateMgBuilder.setEntity(JacksonUtil.toString(edgeAlarm)); alarmUpdateMgBuilder.setOriginatorName(device.getName()); - alarmUpdateMgBuilder.setOriginatorType(EntityType.DEVICE.name()); testAutoGeneratedCodeByProtobuf(alarmUpdateMgBuilder); uplinkMsgBuilder.addAlarmUpdateMsg(alarmUpdateMgBuilder.build()); @@ -72,7 +71,7 @@ public class AlarmEdgeTest extends AbstractEdgeTest { Optional foundAlarm = alarms.stream().filter(alarm -> alarm.getType().equals("alarm from edge")).findAny(); Assert.assertTrue(foundAlarm.isPresent()); AlarmInfo alarmInfo = foundAlarm.get(); - Assert.assertEquals(new AlarmId(alarmUUID), alarmInfo.getId()); + Assert.assertEquals(edgeAlarm.getId(), alarmInfo.getId()); Assert.assertEquals(device.getId(), alarmInfo.getOriginator()); Assert.assertEquals(AlarmStatus.ACTIVE_UNACK, alarmInfo.getStatus()); Assert.assertEquals(AlarmSeverity.CRITICAL, alarmInfo.getSeverity()); @@ -92,14 +91,10 @@ public class AlarmEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof AlarmUpdateMsg); AlarmUpdateMsg alarmUpdateMsg = (AlarmUpdateMsg) latestMessage; + Alarm alarmMsg = JacksonUtil.fromStringIgnoreUnknownProperties(alarmUpdateMsg.getEntity(), Alarm.class); + Assert.assertNotNull(alarmMsg); + Assert.assertEquals(savedAlarm, alarmMsg); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, alarmUpdateMsg.getMsgType()); - Assert.assertEquals(savedAlarm.getUuidId().getMostSignificantBits(), alarmUpdateMsg.getIdMSB()); - Assert.assertEquals(savedAlarm.getUuidId().getLeastSignificantBits(), alarmUpdateMsg.getIdLSB()); - Assert.assertEquals(savedAlarm.getType(), alarmUpdateMsg.getType()); - Assert.assertEquals(savedAlarm.getName(), alarmUpdateMsg.getName()); - Assert.assertEquals(device.getName(), alarmUpdateMsg.getOriginatorName()); - Assert.assertEquals(savedAlarm.getStatus().name(), alarmUpdateMsg.getStatus()); - Assert.assertEquals(savedAlarm.getSeverity().name(), alarmUpdateMsg.getSeverity()); // ack alarm edgeImitator.expectMessageAmount(1); @@ -109,10 +104,12 @@ public class AlarmEdgeTest extends AbstractEdgeTest { Assert.assertTrue(latestMessage instanceof AlarmUpdateMsg); alarmUpdateMsg = (AlarmUpdateMsg) latestMessage; Assert.assertEquals(UpdateMsgType.ALARM_ACK_RPC_MESSAGE, alarmUpdateMsg.getMsgType()); - Assert.assertEquals(savedAlarm.getType(), alarmUpdateMsg.getType()); - Assert.assertEquals(savedAlarm.getName(), alarmUpdateMsg.getName()); + alarmMsg = JacksonUtil.fromStringIgnoreUnknownProperties(alarmUpdateMsg.getEntity(), Alarm.class); + Assert.assertNotNull(alarmMsg); + Assert.assertEquals(savedAlarm.getType(), alarmMsg.getType()); + Assert.assertEquals(savedAlarm.getName(), alarmMsg.getName()); Assert.assertEquals(device.getName(), alarmUpdateMsg.getOriginatorName()); - Assert.assertEquals(AlarmStatus.ACTIVE_ACK.name(), alarmUpdateMsg.getStatus()); + Assert.assertEquals(AlarmStatus.ACTIVE_ACK, alarmMsg.getStatus()); // clear alarm edgeImitator.expectMessageAmount(1); @@ -122,10 +119,12 @@ public class AlarmEdgeTest extends AbstractEdgeTest { Assert.assertTrue(latestMessage instanceof AlarmUpdateMsg); alarmUpdateMsg = (AlarmUpdateMsg) latestMessage; Assert.assertEquals(UpdateMsgType.ALARM_CLEAR_RPC_MESSAGE, alarmUpdateMsg.getMsgType()); - Assert.assertEquals(savedAlarm.getType(), alarmUpdateMsg.getType()); - Assert.assertEquals(savedAlarm.getName(), alarmUpdateMsg.getName()); + alarmMsg = JacksonUtil.fromStringIgnoreUnknownProperties(alarmUpdateMsg.getEntity(), Alarm.class); + Assert.assertNotNull(alarmMsg); + Assert.assertEquals(savedAlarm.getType(), alarmMsg.getType()); + Assert.assertEquals(savedAlarm.getName(), alarmMsg.getName()); Assert.assertEquals(device.getName(), alarmUpdateMsg.getOriginatorName()); - Assert.assertEquals(AlarmStatus.CLEARED_ACK.name(), alarmUpdateMsg.getStatus()); + Assert.assertEquals(AlarmStatus.CLEARED_ACK, alarmMsg.getStatus()); // delete alarm edgeImitator.expectMessageAmount(1); @@ -136,10 +135,21 @@ public class AlarmEdgeTest extends AbstractEdgeTest { Assert.assertTrue(latestMessage instanceof AlarmUpdateMsg); alarmUpdateMsg = (AlarmUpdateMsg) latestMessage; Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, alarmUpdateMsg.getMsgType()); - Assert.assertEquals(savedAlarm.getType(), alarmUpdateMsg.getType()); - Assert.assertEquals(savedAlarm.getName(), alarmUpdateMsg.getName()); + alarmMsg = JacksonUtil.fromStringIgnoreUnknownProperties(alarmUpdateMsg.getEntity(), Alarm.class); + Assert.assertNotNull(alarmMsg); + Assert.assertEquals(savedAlarm.getType(), alarmMsg.getType()); + Assert.assertEquals(savedAlarm.getName(), alarmMsg.getName()); Assert.assertEquals(device.getName(), alarmUpdateMsg.getOriginatorName()); - Assert.assertEquals(AlarmStatus.CLEARED_ACK.name(), alarmUpdateMsg.getStatus()); + Assert.assertEquals(AlarmStatus.CLEARED_ACK, alarmMsg.getStatus()); } + private Alarm buildAlarmForUplinkMsg(DeviceId deviceId) { + Alarm alarm = new Alarm(); + alarm.setId(new AlarmId(UUID.randomUUID())); + alarm.setTenantId(tenantId); + alarm.setType("alarm from edge"); + alarm.setOriginator(deviceId); + alarm.setSeverity(AlarmSeverity.CRITICAL); + return alarm; + } } diff --git a/application/src/test/java/org/thingsboard/server/edge/AssetEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/AssetEdgeTest.java index 0eb19a17fb..1e35b6266b 100644 --- a/application/src/test/java/org/thingsboard/server/edge/AssetEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/AssetEdgeTest.java @@ -15,17 +15,17 @@ */ package org.thingsboard.server.edge; -import com.datastax.oss.driver.api.core.uuid.Uuids; import com.fasterxml.jackson.core.type.TypeReference; import com.google.protobuf.AbstractMessage; import org.junit.Assert; import org.junit.Test; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Customer; -import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.asset.AssetInfo; import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.page.PageData; @@ -57,11 +57,10 @@ public class AssetEdgeTest extends AbstractEdgeTest { Optional assetUpdateMsgOpt = edgeImitator.findMessageByType(AssetUpdateMsg.class); Assert.assertTrue(assetUpdateMsgOpt.isPresent()); AssetUpdateMsg assetUpdateMsg = assetUpdateMsgOpt.get(); + Asset assetMsg = JacksonUtil.fromStringIgnoreUnknownProperties(assetUpdateMsg.getEntity(), Asset.class); + Assert.assertNotNull(assetMsg); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, assetUpdateMsg.getMsgType()); - Assert.assertEquals(savedAsset.getUuidId().getMostSignificantBits(), assetUpdateMsg.getIdMSB()); - Assert.assertEquals(savedAsset.getUuidId().getLeastSignificantBits(), assetUpdateMsg.getIdLSB()); - Assert.assertEquals(savedAsset.getName(), assetUpdateMsg.getName()); - Assert.assertEquals(savedAsset.getType(), assetUpdateMsg.getType()); + Assert.assertEquals(savedAsset, assetMsg); Optional assetProfileUpdateMsgOpt = edgeImitator.findMessageByType(AssetProfileUpdateMsg.class); Assert.assertTrue(assetProfileUpdateMsgOpt.isPresent()); AssetProfileUpdateMsg assetProfileUpdateMsg = assetProfileUpdateMsgOpt.get(); @@ -77,8 +76,10 @@ public class AssetEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof AssetUpdateMsg); assetUpdateMsg = (AssetUpdateMsg) latestMessage; + assetMsg = JacksonUtil.fromStringIgnoreUnknownProperties(assetUpdateMsg.getEntity(), Asset.class); + Assert.assertNotNull(assetMsg); + Assert.assertEquals(savedAsset.getName(), assetMsg.getName()); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, assetUpdateMsg.getMsgType()); - Assert.assertEquals(savedAsset.getName(), assetUpdateMsg.getName()); // unassign asset from edge edgeImitator.expectMessageAmount(1); @@ -107,11 +108,10 @@ public class AssetEdgeTest extends AbstractEdgeTest { assetUpdateMsgOpt = edgeImitator.findMessageByType(AssetUpdateMsg.class); Assert.assertTrue(assetUpdateMsgOpt.isPresent()); assetUpdateMsg = assetUpdateMsgOpt.get(); + assetMsg = JacksonUtil.fromStringIgnoreUnknownProperties(assetUpdateMsg.getEntity(), Asset.class); + Assert.assertNotNull(assetMsg); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, assetUpdateMsg.getMsgType()); - Assert.assertEquals(savedAsset.getUuidId().getMostSignificantBits(), assetUpdateMsg.getIdMSB()); - Assert.assertEquals(savedAsset.getUuidId().getLeastSignificantBits(), assetUpdateMsg.getIdLSB()); - Assert.assertEquals(savedAsset.getName(), assetUpdateMsg.getName()); - Assert.assertEquals(savedAsset.getType(), assetUpdateMsg.getType()); + Assert.assertEquals(savedAsset, assetMsg); assetProfileUpdateMsgOpt = edgeImitator.findMessageByType(AssetProfileUpdateMsg.class); Assert.assertTrue(assetProfileUpdateMsgOpt.isPresent()); assetProfileUpdateMsg = assetProfileUpdateMsgOpt.get(); @@ -135,9 +135,10 @@ public class AssetEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof AssetUpdateMsg); assetUpdateMsg = (AssetUpdateMsg) latestMessage; + assetMsg = JacksonUtil.fromStringIgnoreUnknownProperties(assetUpdateMsg.getEntity(), Asset.class); + Assert.assertNotNull(assetMsg); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, assetUpdateMsg.getMsgType()); - Assert.assertEquals(savedCustomer.getUuidId().getMostSignificantBits(), assetUpdateMsg.getCustomerIdMSB()); - Assert.assertEquals(savedCustomer.getUuidId().getLeastSignificantBits(), assetUpdateMsg.getCustomerIdLSB()); + Assert.assertEquals(savedCustomer.getId(), assetMsg.getCustomerId()); // unassign asset #2 from customer edgeImitator.expectMessageAmount(1); @@ -146,10 +147,10 @@ public class AssetEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof AssetUpdateMsg); assetUpdateMsg = (AssetUpdateMsg) latestMessage; + assetMsg = JacksonUtil.fromStringIgnoreUnknownProperties(assetUpdateMsg.getEntity(), Asset.class); + Assert.assertNotNull(assetMsg); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, assetUpdateMsg.getMsgType()); - Assert.assertEquals( - new CustomerId(EntityId.NULL_UUID), - new CustomerId(new UUID(assetUpdateMsg.getCustomerIdMSB(), assetUpdateMsg.getCustomerIdLSB()))); + Assert.assertEquals(new CustomerId(EntityId.NULL_UUID), assetMsg.getCustomerId()); // delete asset #2 - messages expected edgeImitator.expectMessageAmount(1); @@ -166,14 +167,13 @@ public class AssetEdgeTest extends AbstractEdgeTest { @Test public void testSendAssetToCloud() throws Exception { - UUID uuid = Uuids.timeBased(); + Asset asset = buildAssetForUplinkMsg("Asset Edge 2"); UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); AssetUpdateMsg.Builder assetUpdateMsgBuilder = AssetUpdateMsg.newBuilder(); - assetUpdateMsgBuilder.setIdMSB(uuid.getMostSignificantBits()); - assetUpdateMsgBuilder.setIdLSB(uuid.getLeastSignificantBits()); - assetUpdateMsgBuilder.setName("Asset Edge 2"); - assetUpdateMsgBuilder.setType("default"); + assetUpdateMsgBuilder.setIdMSB(asset.getUuidId().getMostSignificantBits()); + assetUpdateMsgBuilder.setIdLSB(asset.getUuidId().getLeastSignificantBits()); + assetUpdateMsgBuilder.setEntity(JacksonUtil.toString(asset)); assetUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); testAutoGeneratedCodeByProtobuf(assetUpdateMsgBuilder); uplinkMsgBuilder.addAssetUpdateMsg(assetUpdateMsgBuilder.build()); @@ -188,9 +188,9 @@ public class AssetEdgeTest extends AbstractEdgeTest { UplinkResponseMsg latestResponseMsg = edgeImitator.getLatestResponseMsg(); Assert.assertTrue(latestResponseMsg.getSuccess()); - Asset asset = doGet("/api/asset/" + uuid, Asset.class); - Assert.assertNotNull(asset); - Assert.assertEquals("Asset Edge 2", asset.getName()); + Asset foundAsset = doGet("/api/asset/" + asset.getUuidId(), Asset.class); + Assert.assertNotNull(foundAsset); + Assert.assertEquals("Asset Edge 2", foundAsset.getName()); } @Test @@ -198,14 +198,11 @@ public class AssetEdgeTest extends AbstractEdgeTest { String assetOnCloudName = StringUtils.randomAlphanumeric(15); Asset assetOnCloud = saveAsset(assetOnCloudName); - UUID uuid = Uuids.timeBased(); + Asset assetOnEdge = buildAssetForUplinkMsg(assetOnCloudName); UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); AssetUpdateMsg.Builder assetUpdateMsgBuilder = AssetUpdateMsg.newBuilder(); - assetUpdateMsgBuilder.setIdMSB(uuid.getMostSignificantBits()); - assetUpdateMsgBuilder.setIdLSB(uuid.getLeastSignificantBits()); - assetUpdateMsgBuilder.setName(assetOnCloudName); - assetUpdateMsgBuilder.setType("default"); + assetUpdateMsgBuilder.setEntity(JacksonUtil.toString(assetOnEdge)); assetUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); testAutoGeneratedCodeByProtobuf(assetUpdateMsgBuilder); uplinkMsgBuilder.addAssetUpdateMsg(assetUpdateMsgBuilder.build()); @@ -223,7 +220,9 @@ public class AssetEdgeTest extends AbstractEdgeTest { Optional assetUpdateMsgOpt = edgeImitator.findMessageByType(AssetUpdateMsg.class); Assert.assertTrue(assetUpdateMsgOpt.isPresent()); AssetUpdateMsg latestAssetUpdateMsg = assetUpdateMsgOpt.get(); - Assert.assertNotEquals(assetOnCloudName, latestAssetUpdateMsg.getName()); + Asset assetMsg = JacksonUtil.fromStringIgnoreUnknownProperties(latestAssetUpdateMsg.getEntity(), Asset.class); + Assert.assertNotNull(assetMsg); + Assert.assertNotEquals(assetOnCloudName, assetMsg.getName()); UUID newAssetId = new UUID(latestAssetUpdateMsg.getIdMSB(), latestAssetUpdateMsg.getIdLSB()); @@ -275,4 +274,12 @@ public class AssetEdgeTest extends AbstractEdgeTest { return savedAsset; } + private Asset buildAssetForUplinkMsg(String name) { + Asset asset = new Asset(); + asset.setId(new AssetId(UUID.randomUUID())); + asset.setTenantId(tenantId); + asset.setName(name); + asset.setType("test"); + return asset; + } } diff --git a/application/src/test/java/org/thingsboard/server/edge/AssetProfileEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/AssetProfileEdgeTest.java index ebf0af1938..600895c230 100644 --- a/application/src/test/java/org/thingsboard/server/edge/AssetProfileEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/AssetProfileEdgeTest.java @@ -15,13 +15,14 @@ */ package org.thingsboard.server.edge; -import com.datastax.oss.driver.api.core.uuid.Uuids; import com.google.protobuf.AbstractMessage; import com.google.protobuf.ByteString; import org.junit.Assert; import org.junit.Test; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.asset.AssetProfile; +import org.thingsboard.server.common.data.id.AssetProfileId; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.dao.service.DaoSqlTest; @@ -52,12 +53,12 @@ public class AssetProfileEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof AssetProfileUpdateMsg); AssetProfileUpdateMsg assetProfileUpdateMsg = (AssetProfileUpdateMsg) latestMessage; + AssetProfile assetProfileMsg = JacksonUtil.fromEdgeString(assetProfileUpdateMsg.getEntity(), AssetProfile.class); + Assert.assertNotNull(assetProfileMsg); + Assert.assertEquals(assetProfile, assetProfileMsg); + Assert.assertEquals("Building", assetProfileMsg.getName()); + Assert.assertEquals(buildingsRuleChainId, assetProfileMsg.getDefaultEdgeRuleChainId()); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, assetProfileUpdateMsg.getMsgType()); - Assert.assertEquals(assetProfile.getUuidId().getMostSignificantBits(), assetProfileUpdateMsg.getIdMSB()); - Assert.assertEquals(assetProfile.getUuidId().getLeastSignificantBits(), assetProfileUpdateMsg.getIdLSB()); - Assert.assertEquals("Building", assetProfileUpdateMsg.getName()); - Assert.assertEquals(buildingsRuleChainId.getId().getMostSignificantBits(), assetProfileUpdateMsg.getDefaultRuleChainIdMSB()); - Assert.assertEquals(buildingsRuleChainId.getId().getLeastSignificantBits(), assetProfileUpdateMsg.getDefaultRuleChainIdLSB()); // update asset profile assetProfile.setImage("IMAGE"); @@ -67,8 +68,10 @@ public class AssetProfileEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof AssetProfileUpdateMsg); assetProfileUpdateMsg = (AssetProfileUpdateMsg) latestMessage; + assetProfileMsg = JacksonUtil.fromEdgeString(assetProfileUpdateMsg.getEntity(), AssetProfile.class); + Assert.assertNotNull(assetProfileMsg); + Assert.assertEquals("IMAGE", assetProfileMsg.getImage()); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, assetProfileUpdateMsg.getMsgType()); - Assert.assertEquals(ByteString.copyFrom("IMAGE".getBytes(StandardCharsets.UTF_8)), assetProfileUpdateMsg.getImage()); // delete profile edgeImitator.expectMessageAmount(1); @@ -90,18 +93,15 @@ public class AssetProfileEdgeTest extends AbstractEdgeTest { RuleChainId edgeRuleChainId = createEdgeRuleChainAndAssignToEdge("Asset Profile Rule Chain"); DashboardId dashboardId = createDashboardAndAssignToEdge("Asset Profile Dashboard"); - UUID uuid = Uuids.timeBased(); + AssetProfile assetProfileOnEdge = buildAssetProfileForUplinkMsg("Asset Profile On Edge"); + assetProfileOnEdge.setDefaultEdgeRuleChainId(edgeRuleChainId); + assetProfileOnEdge.setDefaultDashboardId(dashboardId); UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); AssetProfileUpdateMsg.Builder assetProfileUpdateMsgBuilder = AssetProfileUpdateMsg.newBuilder(); - assetProfileUpdateMsgBuilder.setIdMSB(uuid.getMostSignificantBits()); - assetProfileUpdateMsgBuilder.setIdLSB(uuid.getLeastSignificantBits()); - assetProfileUpdateMsgBuilder.setName("Asset Profile On Edge"); - assetProfileUpdateMsgBuilder.setDefault(false); - assetProfileUpdateMsgBuilder.setDefaultRuleChainIdMSB(edgeRuleChainId.getId().getMostSignificantBits()); - assetProfileUpdateMsgBuilder.setDefaultRuleChainIdLSB(edgeRuleChainId.getId().getLeastSignificantBits()); - assetProfileUpdateMsgBuilder.setDefaultDashboardIdMSB(dashboardId.getId().getMostSignificantBits()); - assetProfileUpdateMsgBuilder.setDefaultDashboardIdLSB(dashboardId.getId().getLeastSignificantBits()); + assetProfileUpdateMsgBuilder.setIdMSB(assetProfileOnEdge.getUuidId().getMostSignificantBits()); + assetProfileUpdateMsgBuilder.setIdLSB(assetProfileOnEdge.getUuidId().getLeastSignificantBits()); + assetProfileUpdateMsgBuilder.setEntity(JacksonUtil.toString(assetProfileOnEdge)); assetProfileUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); testAutoGeneratedCodeByProtobuf(assetProfileUpdateMsgBuilder); uplinkMsgBuilder.addAssetProfileUpdateMsg(assetProfileUpdateMsgBuilder.build()); @@ -116,7 +116,7 @@ public class AssetProfileEdgeTest extends AbstractEdgeTest { UplinkResponseMsg latestResponseMsg = edgeImitator.getLatestResponseMsg(); Assert.assertTrue(latestResponseMsg.getSuccess()); - AssetProfile assetProfile = doGet("/api/assetProfile/" + uuid, AssetProfile.class); + AssetProfile assetProfile = doGet("/api/assetProfile/" + assetProfileOnEdge.getUuidId(), AssetProfile.class); Assert.assertNotNull(assetProfile); Assert.assertEquals("Asset Profile On Edge", assetProfile.getName()); Assert.assertEquals(dashboardId, assetProfile.getDefaultDashboardId()); @@ -149,13 +149,11 @@ public class AssetProfileEdgeTest extends AbstractEdgeTest { assetProfileOnCloud = doPost("/api/assetProfile", assetProfileOnCloud, AssetProfile.class); Assert.assertTrue(edgeImitator.waitForMessages()); - UUID uuid = Uuids.timeBased(); + AssetProfile assetProfileOnEdge = buildAssetProfileForUplinkMsg(assetProfileOnCloudName); UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); AssetProfileUpdateMsg.Builder assetProfileUpdateMsgBuilder = AssetProfileUpdateMsg.newBuilder(); - assetProfileUpdateMsgBuilder.setIdMSB(uuid.getMostSignificantBits()); - assetProfileUpdateMsgBuilder.setIdLSB(uuid.getLeastSignificantBits()); - assetProfileUpdateMsgBuilder.setName(assetProfileOnCloudName); + assetProfileUpdateMsgBuilder.setEntity(JacksonUtil.toString(assetProfileOnEdge)); assetProfileUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); uplinkMsgBuilder.addAssetProfileUpdateMsg(assetProfileUpdateMsgBuilder.build()); @@ -172,12 +170,23 @@ public class AssetProfileEdgeTest extends AbstractEdgeTest { Optional assetProfileUpdateMsgOpt = edgeImitator.findMessageByType(AssetProfileUpdateMsg.class); Assert.assertTrue(assetProfileUpdateMsgOpt.isPresent()); AssetProfileUpdateMsg latestAssetProfileUpdateMsg = assetProfileUpdateMsgOpt.get(); - Assert.assertNotEquals(assetProfileOnCloudName, latestAssetProfileUpdateMsg.getName()); + AssetProfile assetProfileMsg = JacksonUtil.fromEdgeString(latestAssetProfileUpdateMsg.getEntity(), AssetProfile.class); + Assert.assertNotNull(assetProfileMsg); + Assert.assertNotEquals(assetProfileOnCloudName, assetProfileMsg.getName()); - Assert.assertNotEquals(assetProfileOnCloud.getUuidId(), uuid); + Assert.assertNotEquals(assetProfileOnCloud.getUuidId(), assetProfileOnEdge.getUuidId()); - AssetProfile assetProfile = doGet("/api/assetProfile/" + uuid, AssetProfile.class); + AssetProfile assetProfile = doGet("/api/assetProfile/" + assetProfileMsg.getUuidId(), AssetProfile.class); Assert.assertNotNull(assetProfile); Assert.assertNotEquals(assetProfileOnCloudName, assetProfile.getName()); } + + private AssetProfile buildAssetProfileForUplinkMsg(String name) { + AssetProfile assetProfile = new AssetProfile(); + assetProfile.setId(new AssetProfileId(UUID.randomUUID())); + assetProfile.setTenantId(tenantId); + assetProfile.setName(name); + assetProfile.setDefault(false); + return assetProfile; + } } diff --git a/application/src/test/java/org/thingsboard/server/edge/CustomerEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/CustomerEdgeTest.java index 6cc15837d9..9f7e1b5641 100644 --- a/application/src/test/java/org/thingsboard/server/edge/CustomerEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/CustomerEdgeTest.java @@ -18,6 +18,7 @@ package org.thingsboard.server.edge; import com.google.protobuf.AbstractMessage; import org.junit.Assert; import org.junit.Test; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.id.CustomerId; @@ -57,10 +58,9 @@ public class CustomerEdgeTest extends AbstractEdgeTest { Optional customerUpdateOpt = edgeImitator.findMessageByType(CustomerUpdateMsg.class); Assert.assertTrue(customerUpdateOpt.isPresent()); CustomerUpdateMsg customerUpdateMsg = customerUpdateOpt.get(); + Customer customerMsg = JacksonUtil.fromEdgeString(customerUpdateMsg.getEntity(), Customer.class); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, customerUpdateMsg.getMsgType()); - Assert.assertEquals(savedCustomer.getUuidId().getMostSignificantBits(), customerUpdateMsg.getIdMSB()); - Assert.assertEquals(savedCustomer.getUuidId().getLeastSignificantBits(), customerUpdateMsg.getIdLSB()); - Assert.assertEquals(savedCustomer.getTitle(), customerUpdateMsg.getTitle()); + Assert.assertEquals(savedCustomer, customerMsg); testAutoGeneratedCodeByProtobuf(customerUpdateMsg); // update customer @@ -71,10 +71,9 @@ public class CustomerEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof CustomerUpdateMsg); customerUpdateMsg = (CustomerUpdateMsg) latestMessage; + customerMsg = JacksonUtil.fromEdgeString(customerUpdateMsg.getEntity(), Customer.class); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, customerUpdateMsg.getMsgType()); - Assert.assertEquals(savedCustomer.getUuidId().getMostSignificantBits(), customerUpdateMsg.getIdMSB()); - Assert.assertEquals(savedCustomer.getUuidId().getLeastSignificantBits(), customerUpdateMsg.getIdLSB()); - Assert.assertEquals(savedCustomer.getTitle(), customerUpdateMsg.getTitle()); + Assert.assertEquals(savedCustomer, customerMsg); // delete customer edgeImitator.expectMessageAmount(2); diff --git a/application/src/test/java/org/thingsboard/server/edge/DashboardEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/DashboardEdgeTest.java index 6180ced49c..8db2e0390f 100644 --- a/application/src/test/java/org/thingsboard/server/edge/DashboardEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/DashboardEdgeTest.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.edge; -import com.datastax.oss.driver.api.core.uuid.Uuids; import com.fasterxml.jackson.core.type.TypeReference; import com.google.protobuf.AbstractMessage; import org.junit.Assert; @@ -27,6 +26,7 @@ import org.thingsboard.server.common.data.DashboardInfo; import org.thingsboard.server.common.data.ShortCustomerInfo; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.service.DaoSqlTest; @@ -36,7 +36,6 @@ import org.thingsboard.server.gen.edge.v1.UplinkMsg; import java.util.List; import java.util.Optional; -import java.util.Set; import java.util.UUID; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -63,13 +62,12 @@ public class DashboardEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof DashboardUpdateMsg); DashboardUpdateMsg dashboardUpdateMsg = (DashboardUpdateMsg) latestMessage; + Dashboard dashboardMsg = JacksonUtil.fromEdgeString(dashboardUpdateMsg.getEntity(), Dashboard.class); + Assert.assertNotNull(dashboardMsg); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, dashboardUpdateMsg.getMsgType()); - Assert.assertEquals(savedDashboard.getUuidId().getMostSignificantBits(), dashboardUpdateMsg.getIdMSB()); - Assert.assertEquals(savedDashboard.getUuidId().getLeastSignificantBits(), dashboardUpdateMsg.getIdLSB()); - Assert.assertEquals(savedDashboard.getTitle(), dashboardUpdateMsg.getTitle()); - Assert.assertTrue(dashboardUpdateMsg.getMobileHide()); - Assert.assertEquals(IMAGE, dashboardUpdateMsg.getImage()); - Assert.assertEquals(MOBILE_ORDER, dashboardUpdateMsg.getMobileOrder()); + Assert.assertEquals(savedDashboard, dashboardMsg); + Assert.assertEquals(IMAGE, dashboardMsg.getImage()); + Assert.assertEquals(MOBILE_ORDER, dashboardMsg.getMobileOrder().intValue()); testAutoGeneratedCodeByProtobuf(dashboardUpdateMsg); // update dashboard @@ -80,8 +78,10 @@ public class DashboardEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof DashboardUpdateMsg); dashboardUpdateMsg = (DashboardUpdateMsg) latestMessage; + dashboardMsg = JacksonUtil.fromEdgeString(dashboardUpdateMsg.getEntity(), Dashboard.class); + Assert.assertNotNull(dashboardMsg); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, dashboardUpdateMsg.getMsgType()); - Assert.assertEquals(savedDashboard.getTitle(), dashboardUpdateMsg.getTitle()); + Assert.assertEquals(savedDashboard.getTitle(), dashboardMsg.getTitle()); // unassign dashboard from edge edgeImitator.expectMessageAmount(1); @@ -112,10 +112,12 @@ public class DashboardEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof DashboardUpdateMsg); dashboardUpdateMsg = (DashboardUpdateMsg) latestMessage; + dashboardMsg = JacksonUtil.fromEdgeString(dashboardUpdateMsg.getEntity(), Dashboard.class); + Assert.assertNotNull(dashboardMsg); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, dashboardUpdateMsg.getMsgType()); Assert.assertEquals(savedDashboard.getUuidId().getMostSignificantBits(), dashboardUpdateMsg.getIdMSB()); Assert.assertEquals(savedDashboard.getUuidId().getLeastSignificantBits(), dashboardUpdateMsg.getIdLSB()); - Assert.assertEquals(savedDashboard.getTitle(), dashboardUpdateMsg.getTitle()); + Assert.assertEquals(savedDashboard.getTitle(), dashboardMsg.getTitle()); // assign dashboard #2 to customer Customer customer = new Customer(); @@ -133,12 +135,12 @@ public class DashboardEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof DashboardUpdateMsg); dashboardUpdateMsg = (DashboardUpdateMsg) latestMessage; + dashboardMsg = JacksonUtil.fromEdgeString(dashboardUpdateMsg.getEntity(), Dashboard.class); + Assert.assertNotNull(dashboardMsg); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, dashboardUpdateMsg.getMsgType()); - Set assignedCustomers = - JacksonUtil.fromString(dashboardUpdateMsg.getAssignedCustomers(), new TypeReference<>() {}); - Assert.assertNotNull(assignedCustomers); - Assert.assertFalse(assignedCustomers.isEmpty()); - Assert.assertTrue(assignedCustomers.contains(new ShortCustomerInfo(savedCustomer.getId(), customer.getTitle(), customer.isPublic()))); + Assert.assertNotNull(dashboardMsg.getAssignedCustomers()); + Assert.assertFalse(dashboardMsg.getAssignedCustomers().isEmpty()); + Assert.assertTrue(dashboardMsg.getAssignedCustomers().contains(new ShortCustomerInfo(savedCustomer.getId(), customer.getTitle(), customer.isPublic()))); // unassign dashboard #2 from customer edgeImitator.expectMessageAmount(1); @@ -148,11 +150,11 @@ public class DashboardEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof DashboardUpdateMsg); dashboardUpdateMsg = (DashboardUpdateMsg) latestMessage; + dashboardMsg = JacksonUtil.fromEdgeString(dashboardUpdateMsg.getEntity(), Dashboard.class); + Assert.assertNotNull(dashboardMsg); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, dashboardUpdateMsg.getMsgType()); - assignedCustomers = - JacksonUtil.fromString(dashboardUpdateMsg.getAssignedCustomers(), new TypeReference<>() {}); - Assert.assertNotNull(assignedCustomers); - Assert.assertTrue(assignedCustomers.isEmpty()); + Assert.assertNotNull(dashboardMsg.getAssignedCustomers()); + Assert.assertTrue(dashboardMsg.getAssignedCustomers().isEmpty()); // delete dashboard #2 - messages expected edgeImitator.expectMessageAmount(1); @@ -169,14 +171,13 @@ public class DashboardEdgeTest extends AbstractEdgeTest { @Test public void testSendDashboardToCloud() throws Exception { - UUID uuid = Uuids.timeBased(); + Dashboard dashboard = buildDashboardForUplinkMsg(); UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); DashboardUpdateMsg.Builder dashboardUpdateMsgBuilder = DashboardUpdateMsg.newBuilder(); - dashboardUpdateMsgBuilder.setIdMSB(uuid.getMostSignificantBits()); - dashboardUpdateMsgBuilder.setIdLSB(uuid.getLeastSignificantBits()); - dashboardUpdateMsgBuilder.setTitle("Edge Test Dashboard"); - dashboardUpdateMsgBuilder.setConfiguration(""); + dashboardUpdateMsgBuilder.setIdMSB(dashboard.getUuidId().getMostSignificantBits()); + dashboardUpdateMsgBuilder.setIdLSB(dashboard.getUuidId().getLeastSignificantBits()); + dashboardUpdateMsgBuilder.setEntity(JacksonUtil.toString(dashboard)); dashboardUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); testAutoGeneratedCodeByProtobuf(dashboardUpdateMsgBuilder); uplinkMsgBuilder.addDashboardUpdateMsg(dashboardUpdateMsgBuilder.build()); @@ -188,9 +189,9 @@ public class DashboardEdgeTest extends AbstractEdgeTest { Assert.assertTrue(edgeImitator.waitForResponses()); - Dashboard dashboard = doGet("/api/dashboard/" + uuid, Dashboard.class); - Assert.assertNotNull(dashboard); - Assert.assertEquals("Edge Test Dashboard", dashboard.getName()); + Dashboard foundDashboard = doGet("/api/dashboard/" + dashboard.getUuidId(), Dashboard.class); + Assert.assertNotNull(foundDashboard); + Assert.assertEquals("Edge Test Dashboard", foundDashboard.getName()); } @Test @@ -237,4 +238,12 @@ public class DashboardEdgeTest extends AbstractEdgeTest { return savedDashboard; } + private Dashboard buildDashboardForUplinkMsg() { + Dashboard dashboard = new Dashboard(); + dashboard.setId(new DashboardId(UUID.randomUUID())); + dashboard.setTenantId(tenantId); + dashboard.setTitle("Edge Test Dashboard"); + return dashboard; + } + } 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 e6fee370f1..429bb2e968 100644 --- a/application/src/test/java/org/thingsboard/server/edge/DeviceEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/DeviceEdgeTest.java @@ -43,6 +43,7 @@ import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.edge.EdgeEventType; import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.page.PageData; @@ -117,11 +118,13 @@ public class DeviceEdgeTest extends AbstractEdgeTest { Optional deviceUpdateMsgOpt = edgeImitator.findMessageByType(DeviceUpdateMsg.class); Assert.assertTrue(deviceUpdateMsgOpt.isPresent()); deviceUpdateMsg = deviceUpdateMsgOpt.get(); + Device deviceFromMsg = JacksonUtil.fromStringIgnoreUnknownProperties(deviceUpdateMsg.getEntity(), Device.class); + Assert.assertNotNull(deviceFromMsg); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, deviceUpdateMsg.getMsgType()); - Assert.assertEquals(savedDevice.getUuidId().getMostSignificantBits(), deviceUpdateMsg.getIdMSB()); - Assert.assertEquals(savedDevice.getUuidId().getLeastSignificantBits(), deviceUpdateMsg.getIdLSB()); - Assert.assertEquals(savedDevice.getName(), deviceUpdateMsg.getName()); - Assert.assertEquals(savedDevice.getType(), deviceUpdateMsg.getType()); + Assert.assertEquals(savedDevice, deviceFromMsg); + Assert.assertEquals(savedDevice.getId(), deviceFromMsg.getId()); + Assert.assertEquals(savedDevice.getName(), deviceFromMsg.getName()); + Assert.assertEquals(savedDevice.getType(), deviceFromMsg.getType()); Optional deviceProfileUpdateMsgOpt = edgeImitator.findMessageByType(DeviceProfileUpdateMsg.class); Assert.assertTrue(deviceProfileUpdateMsgOpt.isPresent()); @@ -146,9 +149,10 @@ public class DeviceEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof DeviceUpdateMsg); deviceUpdateMsg = (DeviceUpdateMsg) latestMessage; + deviceFromMsg = JacksonUtil.fromStringIgnoreUnknownProperties(deviceUpdateMsg.getEntity(), Device.class); + Assert.assertNotNull(deviceFromMsg); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, deviceUpdateMsg.getMsgType()); - Assert.assertEquals(savedCustomer.getUuidId().getMostSignificantBits(), deviceUpdateMsg.getCustomerIdMSB()); - Assert.assertEquals(savedCustomer.getUuidId().getLeastSignificantBits(), deviceUpdateMsg.getCustomerIdLSB()); + Assert.assertEquals(savedCustomer.getId(), deviceFromMsg.getCustomerId()); // unassign device #2 from customer edgeImitator.expectMessageAmount(1); @@ -157,10 +161,12 @@ public class DeviceEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof DeviceUpdateMsg); deviceUpdateMsg = (DeviceUpdateMsg) latestMessage; + deviceFromMsg = JacksonUtil.fromStringIgnoreUnknownProperties(deviceUpdateMsg.getEntity(), Device.class); + Assert.assertNotNull(deviceFromMsg); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, deviceUpdateMsg.getMsgType()); Assert.assertEquals( new CustomerId(EntityId.NULL_UUID), - new CustomerId(new UUID(deviceUpdateMsg.getCustomerIdMSB(), deviceUpdateMsg.getCustomerIdLSB()))); + new CustomerId(new UUID(deviceFromMsg.getCustomerId().getId().getMostSignificantBits(), deviceFromMsg.getCustomerId().getId().getLeastSignificantBits()))); // delete device #2 - messages expected edgeImitator.expectMessageAmount(1); @@ -196,9 +202,8 @@ public class DeviceEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof DeviceCredentialsUpdateMsg); DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg = (DeviceCredentialsUpdateMsg) latestMessage; - Assert.assertEquals(deviceCredentials.getCredentialsType().name(), deviceCredentialsUpdateMsg.getCredentialsType()); - Assert.assertEquals(deviceCredentials.getCredentialsId(), deviceCredentialsUpdateMsg.getCredentialsId()); - Assert.assertFalse(deviceCredentialsUpdateMsg.hasCredentialsValue()); + DeviceCredentials deviceCredentialsMsg = JacksonUtil.fromStringIgnoreUnknownProperties(deviceCredentialsUpdateMsg.getEntity(), DeviceCredentials.class); + Assert.assertEquals(deviceCredentials, deviceCredentialsMsg); // update device credentials - X509_CERTIFICATE edgeImitator.expectMessageAmount(1); @@ -211,10 +216,12 @@ public class DeviceEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof DeviceCredentialsUpdateMsg); deviceCredentialsUpdateMsg = (DeviceCredentialsUpdateMsg) latestMessage; - Assert.assertEquals(deviceCredentials.getCredentialsType().name(), deviceCredentialsUpdateMsg.getCredentialsType()); - Assert.assertFalse(deviceCredentialsUpdateMsg.getCredentialsId().isEmpty()); - Assert.assertTrue(deviceCredentialsUpdateMsg.hasCredentialsValue()); - Assert.assertEquals(deviceCredentials.getCredentialsValue(), deviceCredentialsUpdateMsg.getCredentialsValue()); + deviceCredentialsMsg = JacksonUtil.fromStringIgnoreUnknownProperties(deviceCredentialsUpdateMsg.getEntity(), DeviceCredentials.class); + Assert.assertNotNull(deviceCredentialsMsg); + Assert.assertEquals(deviceCredentials.getCredentialsType(), deviceCredentialsMsg.getCredentialsType()); + Assert.assertFalse(deviceCredentialsMsg.getCredentialsId().isEmpty()); + Assert.assertNotNull(deviceCredentialsMsg.getCredentialsValue()); + Assert.assertEquals(deviceCredentials.getCredentialsValue(), deviceCredentialsMsg.getCredentialsValue()); } private void verifyUpdateFirmwareIdSoftwareIdAndDeviceData(Device savedDevice) throws InterruptedException { @@ -244,19 +251,13 @@ public class DeviceEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof DeviceUpdateMsg); DeviceUpdateMsg deviceUpdateMsg = (DeviceUpdateMsg) latestMessage; + Device deviceMsg = JacksonUtil.fromStringIgnoreUnknownProperties(deviceUpdateMsg.getEntity(), Device.class); + Assert.assertNotNull(deviceMsg); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, deviceUpdateMsg.getMsgType()); - Assert.assertEquals(savedDevice.getUuidId().getMostSignificantBits(), deviceUpdateMsg.getIdMSB()); - Assert.assertEquals(savedDevice.getUuidId().getLeastSignificantBits(), deviceUpdateMsg.getIdLSB()); - Assert.assertEquals(savedDevice.getName(), deviceUpdateMsg.getName()); - Assert.assertEquals(savedDevice.getType(), deviceUpdateMsg.getType()); - Assert.assertEquals(firmwareOtaPackageInfo.getUuidId().getMostSignificantBits(), deviceUpdateMsg.getFirmwareIdMSB()); - Assert.assertEquals(firmwareOtaPackageInfo.getUuidId().getLeastSignificantBits(), deviceUpdateMsg.getFirmwareIdLSB()); - Assert.assertEquals(softwareOtaPackageInfo.getUuidId().getMostSignificantBits(), deviceUpdateMsg.getSoftwareIdMSB()); - Assert.assertEquals(softwareOtaPackageInfo.getUuidId().getLeastSignificantBits(), deviceUpdateMsg.getSoftwareIdLSB()); - Optional deviceDataOpt = - dataDecodingEncodingService.decode(deviceUpdateMsg.getDeviceDataBytes().toByteArray()); - Assert.assertTrue(deviceDataOpt.isPresent()); - deviceData = deviceDataOpt.get(); + Assert.assertEquals(savedDevice, deviceMsg); + Assert.assertEquals(firmwareOtaPackageInfo.getId(), deviceMsg.getFirmwareId()); + Assert.assertEquals(softwareOtaPackageInfo.getId(), deviceMsg.getSoftwareId()); + deviceData = deviceMsg.getDeviceData(); Assert.assertTrue(deviceData.getTransportConfiguration() instanceof MqttDeviceTransportConfiguration); MqttDeviceTransportConfiguration mqttDeviceTransportConfiguration = (MqttDeviceTransportConfiguration) deviceData.getTransportConfiguration(); @@ -276,14 +277,13 @@ public class DeviceEdgeTest extends AbstractEdgeTest { loginTenantAdmin(); - UUID uuid = Uuids.timeBased(); + Device device = buildDeviceForUplinkMsg("Edge Device", DEFAULT_DEVICE_TYPE); UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); DeviceUpdateMsg.Builder deviceUpdateMsgBuilder = DeviceUpdateMsg.newBuilder(); - deviceUpdateMsgBuilder.setIdMSB(uuid.getMostSignificantBits()); - deviceUpdateMsgBuilder.setIdLSB(uuid.getLeastSignificantBits()); - deviceUpdateMsgBuilder.setName("Edge Device"); - deviceUpdateMsgBuilder.setType(DEFAULT_DEVICE_TYPE); + deviceUpdateMsgBuilder.setIdMSB(device.getUuidId().getMostSignificantBits()); + deviceUpdateMsgBuilder.setIdLSB(device.getUuidId().getLeastSignificantBits()); + deviceUpdateMsgBuilder.setEntity(JacksonUtil.toString(device)); deviceUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); uplinkMsgBuilder.addDeviceUpdateMsg(deviceUpdateMsgBuilder.build()); @@ -327,12 +327,11 @@ public class DeviceEdgeTest extends AbstractEdgeTest { public void testSendDeviceCredentialsUpdateToCloud() throws Exception { Device device = findDeviceByName("Edge Device 1"); + DeviceCredentials deviceCredentials = buildDeviceCredentialsForUplinkMsg(device.getId()); + UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); DeviceCredentialsUpdateMsg.Builder deviceCredentialsUpdateMsgBuilder = DeviceCredentialsUpdateMsg.newBuilder(); - deviceCredentialsUpdateMsgBuilder.setDeviceIdMSB(device.getUuidId().getMostSignificantBits()); - deviceCredentialsUpdateMsgBuilder.setDeviceIdLSB(device.getUuidId().getLeastSignificantBits()); - deviceCredentialsUpdateMsgBuilder.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN.name()); - deviceCredentialsUpdateMsgBuilder.setCredentialsId("NEW_TOKEN"); + deviceCredentialsUpdateMsgBuilder.setEntity(JacksonUtil.toString(deviceCredentials)); testAutoGeneratedCodeByProtobuf(deviceCredentialsUpdateMsgBuilder); uplinkMsgBuilder.addDeviceCredentialsUpdateMsg(deviceCredentialsUpdateMsgBuilder.build()); @@ -367,10 +366,10 @@ public class DeviceEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof DeviceCredentialsUpdateMsg); DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg = (DeviceCredentialsUpdateMsg) latestMessage; - Assert.assertEquals(deviceCredentialsUpdateMsg.getDeviceIdMSB(), device.getUuidId().getMostSignificantBits()); - Assert.assertEquals(deviceCredentialsUpdateMsg.getDeviceIdLSB(), device.getUuidId().getLeastSignificantBits()); - Assert.assertEquals(deviceCredentialsUpdateMsg.getCredentialsType(), deviceCredentials.getCredentialsType().name()); - Assert.assertEquals(deviceCredentialsUpdateMsg.getCredentialsId(), deviceCredentials.getCredentialsId()); + DeviceCredentials deviceCredentialsMsg = JacksonUtil.fromStringIgnoreUnknownProperties(deviceCredentialsUpdateMsg.getEntity(), DeviceCredentials.class); + Assert.assertNotNull(deviceCredentialsMsg); + Assert.assertEquals(device.getId(), deviceCredentialsMsg.getDeviceId()); + Assert.assertEquals(deviceCredentials, deviceCredentialsMsg); } @Test @@ -485,14 +484,13 @@ public class DeviceEdgeTest extends AbstractEdgeTest { String deviceOnCloudName = StringUtils.randomAlphanumeric(15); Device deviceOnCloud = saveDevice(deviceOnCloudName, DEFAULT_DEVICE_TYPE); - UUID uuid = Uuids.timeBased(); + Device deviceMsg = buildDeviceForUplinkMsg(deviceOnCloudName, "test"); UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); DeviceUpdateMsg.Builder deviceUpdateMsgBuilder = DeviceUpdateMsg.newBuilder(); - deviceUpdateMsgBuilder.setIdMSB(uuid.getMostSignificantBits()); - deviceUpdateMsgBuilder.setIdLSB(uuid.getLeastSignificantBits()); - deviceUpdateMsgBuilder.setName(deviceOnCloudName); - deviceUpdateMsgBuilder.setType("default"); + deviceUpdateMsgBuilder.setIdMSB(deviceMsg.getUuidId().getMostSignificantBits()); + deviceUpdateMsgBuilder.setIdLSB(deviceMsg.getUuidId().getLeastSignificantBits()); + deviceUpdateMsgBuilder.setEntity(JacksonUtil.toString(deviceMsg)); deviceUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); testAutoGeneratedCodeByProtobuf(deviceUpdateMsgBuilder); uplinkMsgBuilder.addDeviceUpdateMsg(deviceUpdateMsgBuilder.build()); @@ -509,7 +507,9 @@ public class DeviceEdgeTest extends AbstractEdgeTest { Optional deviceUpdateMsgOpt = edgeImitator.findMessageByType(DeviceUpdateMsg.class); Assert.assertTrue(deviceUpdateMsgOpt.isPresent()); DeviceUpdateMsg latestDeviceUpdateMsg = deviceUpdateMsgOpt.get(); - Assert.assertNotEquals(deviceOnCloudName, latestDeviceUpdateMsg.getName()); + Device deviceLatestMsg = JacksonUtil.fromStringIgnoreUnknownProperties(latestDeviceUpdateMsg.getEntity(), Device.class); + Assert.assertNotNull(deviceLatestMsg); + Assert.assertNotEquals(deviceOnCloudName, deviceLatestMsg.getName()); UUID newDeviceId = new UUID(latestDeviceUpdateMsg.getIdMSB(), latestDeviceUpdateMsg.getIdLSB()); @@ -522,8 +522,8 @@ public class DeviceEdgeTest extends AbstractEdgeTest { Optional deviceCredentialsUpdateMsgOpt = edgeImitator.findMessageByType(DeviceCredentialsRequestMsg.class); Assert.assertTrue(deviceCredentialsUpdateMsgOpt.isPresent()); DeviceCredentialsRequestMsg latestDeviceCredentialsRequestMsg = deviceCredentialsUpdateMsgOpt.get(); - Assert.assertEquals(uuid.getMostSignificantBits(), latestDeviceCredentialsRequestMsg.getDeviceIdMSB()); - Assert.assertEquals(uuid.getLeastSignificantBits(), latestDeviceCredentialsRequestMsg.getDeviceIdLSB()); + Assert.assertEquals(deviceMsg.getUuidId().getMostSignificantBits(), latestDeviceCredentialsRequestMsg.getDeviceIdMSB()); + Assert.assertEquals(device.getUuidId().getLeastSignificantBits(), latestDeviceCredentialsRequestMsg.getDeviceIdLSB()); newDeviceId = new UUID(latestDeviceCredentialsRequestMsg.getDeviceIdMSB(), latestDeviceCredentialsRequestMsg.getDeviceIdLSB()); @@ -534,14 +534,13 @@ public class DeviceEdgeTest extends AbstractEdgeTest { @Test public void testSendDeviceToCloud() throws Exception { - UUID uuid = Uuids.timeBased(); + Device deviceMsg = buildDeviceForUplinkMsg("Edge Device 2", "test"); UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); DeviceUpdateMsg.Builder deviceUpdateMsgBuilder = DeviceUpdateMsg.newBuilder(); - deviceUpdateMsgBuilder.setIdMSB(uuid.getMostSignificantBits()); - deviceUpdateMsgBuilder.setIdLSB(uuid.getLeastSignificantBits()); - deviceUpdateMsgBuilder.setName("Edge Device 2"); - deviceUpdateMsgBuilder.setType("default"); + deviceUpdateMsgBuilder.setIdMSB(deviceMsg.getUuidId().getMostSignificantBits()); + deviceUpdateMsgBuilder.setIdLSB(deviceMsg.getUuidId().getLeastSignificantBits()); + deviceUpdateMsgBuilder.setEntity(JacksonUtil.toString(deviceMsg)); deviceUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); uplinkMsgBuilder.addDeviceUpdateMsg(deviceUpdateMsgBuilder.build()); @@ -553,11 +552,11 @@ public class DeviceEdgeTest extends AbstractEdgeTest { Assert.assertTrue(edgeImitator.waitForResponses()); Assert.assertTrue(edgeImitator.waitForMessages()); - Optional deviceCredentialsRequestMsgOpt = edgeImitator.findMessageByType(DeviceCredentialsRequestMsg.class); - Assert.assertTrue(deviceCredentialsRequestMsgOpt.isPresent()); - DeviceCredentialsRequestMsg latestDeviceCredentialsRequestMsg = deviceCredentialsRequestMsgOpt.get(); - Assert.assertEquals(uuid.getMostSignificantBits(), latestDeviceCredentialsRequestMsg.getDeviceIdMSB()); - Assert.assertEquals(uuid.getLeastSignificantBits(), latestDeviceCredentialsRequestMsg.getDeviceIdLSB()); + AbstractMessage latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof DeviceCredentialsRequestMsg); + DeviceCredentialsRequestMsg latestDeviceCredentialsRequestMsg = (DeviceCredentialsRequestMsg) latestMessage; + Assert.assertEquals(deviceMsg.getUuidId().getMostSignificantBits(), latestDeviceCredentialsRequestMsg.getDeviceIdMSB()); + Assert.assertEquals(deviceMsg.getUuidId().getLeastSignificantBits(), latestDeviceCredentialsRequestMsg.getDeviceIdLSB()); UUID newDeviceId = new UUID(latestDeviceCredentialsRequestMsg.getDeviceIdMSB(), latestDeviceCredentialsRequestMsg.getDeviceIdLSB()); @@ -769,4 +768,22 @@ public class DeviceEdgeTest extends AbstractEdgeTest { } } } + + private Device buildDeviceForUplinkMsg(String name, String type) { + Device device = new Device(); + device.setId(new DeviceId(UUID.randomUUID())); + device.setTenantId(tenantId); + device.setType(type); + device.setName(name); + return device; + } + + + private DeviceCredentials buildDeviceCredentialsForUplinkMsg(DeviceId deviceId) { + DeviceCredentials deviceCredentials = new DeviceCredentials(); + deviceCredentials.setDeviceId(deviceId); + deviceCredentials.setCredentialsId("NEW_TOKEN"); + deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN); + return deviceCredentials; + } } diff --git a/application/src/test/java/org/thingsboard/server/edge/DeviceProfileEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/DeviceProfileEdgeTest.java index 9d48824bed..90d540d17f 100644 --- a/application/src/test/java/org/thingsboard/server/edge/DeviceProfileEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/DeviceProfileEdgeTest.java @@ -15,9 +15,7 @@ */ package org.thingsboard.server.edge; -import com.datastax.oss.driver.api.core.uuid.Uuids; import com.google.protobuf.AbstractMessage; -import com.google.protobuf.ByteString; import org.junit.Assert; import org.junit.Test; import org.thingsboard.common.util.JacksonUtil; @@ -44,6 +42,7 @@ import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.Abstrac import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.LwM2MBootstrapServerCredential; import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.NoSecLwM2MBootstrapServerCredential; import org.thingsboard.server.common.data.id.DashboardId; +import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.kv.DataType; import org.thingsboard.server.common.data.ota.OtaPackageType; @@ -82,11 +81,10 @@ public class DeviceProfileEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof DeviceProfileUpdateMsg); DeviceProfileUpdateMsg deviceProfileUpdateMsg = (DeviceProfileUpdateMsg) latestMessage; + DeviceProfile deviceProfileMsg = JacksonUtil.fromEdgeString(deviceProfileUpdateMsg.getEntity(), DeviceProfile.class); + Assert.assertNotNull(deviceProfileMsg); + Assert.assertEquals(deviceProfile, deviceProfileMsg); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, deviceProfileUpdateMsg.getMsgType()); - Assert.assertEquals(deviceProfile.getUuidId().getMostSignificantBits(), deviceProfileUpdateMsg.getIdMSB()); - Assert.assertEquals(deviceProfile.getUuidId().getLeastSignificantBits(), deviceProfileUpdateMsg.getIdLSB()); - Assert.assertEquals(thermostatsRuleChainId.getId().getMostSignificantBits(), deviceProfileUpdateMsg.getDefaultRuleChainIdMSB()); - Assert.assertEquals(thermostatsRuleChainId.getId().getLeastSignificantBits(), deviceProfileUpdateMsg.getDefaultRuleChainIdLSB()); // update device profile edgeImitator.expectMessageAmount(1); @@ -108,12 +106,9 @@ public class DeviceProfileEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof DeviceProfileUpdateMsg); deviceProfileUpdateMsg = (DeviceProfileUpdateMsg) latestMessage; - Assert.assertEquals(firmwareOtaPackageInfo.getUuidId().getMostSignificantBits(), deviceProfileUpdateMsg.getFirmwareIdMSB()); - Assert.assertEquals(firmwareOtaPackageInfo.getUuidId().getLeastSignificantBits(), deviceProfileUpdateMsg.getFirmwareIdLSB()); - Assert.assertEquals(softwareOtaPackageInfo.getUuidId().getMostSignificantBits(), deviceProfileUpdateMsg.getSoftwareIdMSB()); - Assert.assertEquals(softwareOtaPackageInfo.getUuidId().getLeastSignificantBits(), deviceProfileUpdateMsg.getSoftwareIdLSB()); - Assert.assertEquals(thermostatsDashboardId.getId().getMostSignificantBits(), deviceProfileUpdateMsg.getDefaultDashboardIdMSB()); - Assert.assertEquals(thermostatsDashboardId.getId().getLeastSignificantBits(), deviceProfileUpdateMsg.getDefaultDashboardIdLSB()); + deviceProfileMsg = JacksonUtil.fromEdgeString(deviceProfileUpdateMsg.getEntity(), DeviceProfile.class); + Assert.assertNotNull(deviceProfileMsg); + Assert.assertEquals(deviceProfile, deviceProfileMsg); // delete profile edgeImitator.expectMessageAmount(1); @@ -138,16 +133,13 @@ public class DeviceProfileEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof DeviceProfileUpdateMsg); DeviceProfileUpdateMsg deviceProfileUpdateMsg = (DeviceProfileUpdateMsg) latestMessage; + DeviceProfile deviceProfileMsg = JacksonUtil.fromEdgeString(deviceProfileUpdateMsg.getEntity(), DeviceProfile.class); + Assert.assertNotNull(deviceProfileMsg); + Assert.assertEquals(deviceProfile, deviceProfileMsg); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, deviceProfileUpdateMsg.getMsgType()); - Assert.assertEquals(deviceProfile.getUuidId().getMostSignificantBits(), deviceProfileUpdateMsg.getIdMSB()); - Assert.assertEquals(deviceProfile.getUuidId().getLeastSignificantBits(), deviceProfileUpdateMsg.getIdLSB()); - Assert.assertEquals(DeviceTransportType.SNMP.name(), deviceProfileUpdateMsg.getTransportType()); + Assert.assertEquals(DeviceTransportType.SNMP, deviceProfileMsg.getTransportType()); - Optional deviceProfileDataOpt = - dataDecodingEncodingService.decode(deviceProfileUpdateMsg.getProfileDataBytes().toByteArray()); - - Assert.assertTrue(deviceProfileDataOpt.isPresent()); - DeviceProfileData deviceProfileData = deviceProfileDataOpt.get(); + DeviceProfileData deviceProfileData = deviceProfileMsg.getProfileData(); Assert.assertTrue(deviceProfileData.getTransportConfiguration() instanceof SnmpDeviceProfileTransportConfiguration); SnmpDeviceProfileTransportConfiguration transportConfiguration = @@ -179,16 +171,13 @@ public class DeviceProfileEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof DeviceProfileUpdateMsg); DeviceProfileUpdateMsg deviceProfileUpdateMsg = (DeviceProfileUpdateMsg) latestMessage; + DeviceProfile deviceProfileMsg = JacksonUtil.fromEdgeString(deviceProfileUpdateMsg.getEntity(), DeviceProfile.class); + Assert.assertNotNull(deviceProfileMsg); + Assert.assertEquals(deviceProfile, deviceProfileMsg); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, deviceProfileUpdateMsg.getMsgType()); - Assert.assertEquals(deviceProfile.getUuidId().getMostSignificantBits(), deviceProfileUpdateMsg.getIdMSB()); - Assert.assertEquals(deviceProfile.getUuidId().getLeastSignificantBits(), deviceProfileUpdateMsg.getIdLSB()); - Assert.assertEquals(DeviceTransportType.LWM2M.name(), deviceProfileUpdateMsg.getTransportType()); + Assert.assertEquals(DeviceTransportType.LWM2M, deviceProfileMsg.getTransportType()); - Optional deviceProfileDataOpt = - dataDecodingEncodingService.decode(deviceProfileUpdateMsg.getProfileDataBytes().toByteArray()); - - Assert.assertTrue(deviceProfileDataOpt.isPresent()); - DeviceProfileData deviceProfileData = deviceProfileDataOpt.get(); + DeviceProfileData deviceProfileData = deviceProfileMsg.getProfileData(); Assert.assertTrue(deviceProfileData.getTransportConfiguration() instanceof Lwm2mDeviceProfileTransportConfiguration); Lwm2mDeviceProfileTransportConfiguration transportConfiguration = @@ -231,16 +220,13 @@ public class DeviceProfileEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof DeviceProfileUpdateMsg); DeviceProfileUpdateMsg deviceProfileUpdateMsg = (DeviceProfileUpdateMsg) latestMessage; + DeviceProfile deviceProfileMsg = JacksonUtil.fromEdgeString(deviceProfileUpdateMsg.getEntity(), DeviceProfile.class); + Assert.assertNotNull(deviceProfileMsg); + Assert.assertEquals(deviceProfile, deviceProfileMsg); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, deviceProfileUpdateMsg.getMsgType()); - Assert.assertEquals(deviceProfile.getUuidId().getMostSignificantBits(), deviceProfileUpdateMsg.getIdMSB()); - Assert.assertEquals(deviceProfile.getUuidId().getLeastSignificantBits(), deviceProfileUpdateMsg.getIdLSB()); - Assert.assertEquals(DeviceTransportType.COAP.name(), deviceProfileUpdateMsg.getTransportType()); - - Optional deviceProfileDataOpt = - dataDecodingEncodingService.decode(deviceProfileUpdateMsg.getProfileDataBytes().toByteArray()); + Assert.assertEquals(DeviceTransportType.COAP, deviceProfileMsg.getTransportType()); - Assert.assertTrue(deviceProfileDataOpt.isPresent()); - DeviceProfileData deviceProfileData = deviceProfileDataOpt.get(); + DeviceProfileData deviceProfileData = deviceProfileMsg.getProfileData(); Assert.assertTrue(deviceProfileData.getTransportConfiguration() instanceof CoapDeviceProfileTransportConfiguration); CoapDeviceProfileTransportConfiguration transportConfiguration = @@ -275,20 +261,15 @@ public class DeviceProfileEdgeTest extends AbstractEdgeTest { RuleChainId ruleChainId = createEdgeRuleChainAndAssignToEdge("Device Profile Rule Chain"); DashboardId dashboardId = createDashboardAndAssignToEdge("Device Profile Dashboard"); - UUID uuid = Uuids.timeBased(); + DeviceProfile deviceProfileMsg = buildDeviceProfileForUplinkMsg("Device Profile On Edge"); + deviceProfileMsg.setDefaultRuleChainId(ruleChainId); + deviceProfileMsg.setDefaultDashboardId(dashboardId); UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); DeviceProfileUpdateMsg.Builder deviceProfileUpdateMsgBuilder = DeviceProfileUpdateMsg.newBuilder(); - deviceProfileUpdateMsgBuilder.setIdMSB(uuid.getMostSignificantBits()); - deviceProfileUpdateMsgBuilder.setIdLSB(uuid.getLeastSignificantBits()); - deviceProfileUpdateMsgBuilder.setName("Device Profile On Edge"); - deviceProfileUpdateMsgBuilder.setDefault(false); - deviceProfileUpdateMsgBuilder.setType(DeviceProfileType.DEFAULT.name()); - deviceProfileUpdateMsgBuilder.setProfileDataBytes(ByteString.copyFrom(dataDecodingEncodingService.encode(createProfileData()))); - deviceProfileUpdateMsgBuilder.setDefaultRuleChainIdMSB(ruleChainId.getId().getMostSignificantBits()); - deviceProfileUpdateMsgBuilder.setDefaultRuleChainIdLSB(ruleChainId.getId().getLeastSignificantBits()); - deviceProfileUpdateMsgBuilder.setDefaultDashboardIdMSB(dashboardId.getId().getMostSignificantBits()); - deviceProfileUpdateMsgBuilder.setDefaultDashboardIdLSB(dashboardId.getId().getLeastSignificantBits()); + deviceProfileUpdateMsgBuilder.setIdMSB(deviceProfileMsg.getUuidId().getMostSignificantBits()); + deviceProfileUpdateMsgBuilder.setIdLSB(deviceProfileMsg.getUuidId().getLeastSignificantBits()); + deviceProfileUpdateMsgBuilder.setEntity(JacksonUtil.toString(deviceProfileMsg)); deviceProfileUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); testAutoGeneratedCodeByProtobuf(deviceProfileUpdateMsgBuilder); uplinkMsgBuilder.addDeviceProfileUpdateMsg(deviceProfileUpdateMsgBuilder.build()); @@ -303,7 +284,7 @@ public class DeviceProfileEdgeTest extends AbstractEdgeTest { UplinkResponseMsg latestResponseMsg = edgeImitator.getLatestResponseMsg(); Assert.assertTrue(latestResponseMsg.getSuccess()); - DeviceProfile deviceProfile = doGet("/api/deviceProfile/" + uuid, DeviceProfile.class); + DeviceProfile deviceProfile = doGet("/api/deviceProfile/" + deviceProfileMsg.getUuidId(), DeviceProfile.class); Assert.assertNotNull(deviceProfile); Assert.assertEquals("Device Profile On Edge", deviceProfile.getName()); @@ -422,16 +403,15 @@ public class DeviceProfileEdgeTest extends AbstractEdgeTest { deviceProfileOnCloud = doPost("/api/deviceProfile", deviceProfileOnCloud, DeviceProfile.class); Assert.assertTrue(edgeImitator.waitForMessages()); - UUID uuid = Uuids.timeBased(); + DeviceProfile deviceProfileMsg = buildDeviceProfileForUplinkMsg(deviceProfileOnCloudName); + deviceProfileMsg.setProfileData(deviceProfileOnCloud.getProfileData()); UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); DeviceProfileUpdateMsg.Builder deviceProfileUpdateMsgBuilder = DeviceProfileUpdateMsg.newBuilder(); - deviceProfileUpdateMsgBuilder.setIdMSB(uuid.getMostSignificantBits()); - deviceProfileUpdateMsgBuilder.setIdLSB(uuid.getLeastSignificantBits()); - deviceProfileUpdateMsgBuilder.setName(deviceProfileOnCloudName); - deviceProfileUpdateMsgBuilder.setType(DeviceProfileType.DEFAULT.name()); + deviceProfileUpdateMsgBuilder.setIdMSB(deviceProfileMsg.getUuidId().getMostSignificantBits()); + deviceProfileUpdateMsgBuilder.setIdLSB(deviceProfileMsg.getUuidId().getLeastSignificantBits()); + deviceProfileUpdateMsgBuilder.setEntity(JacksonUtil.toString(deviceProfileMsg)); deviceProfileUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); - deviceProfileUpdateMsgBuilder.setProfileDataBytes(ByteString.copyFrom(dataDecodingEncodingService.encode(deviceProfileOnCloud.getProfileData()))); uplinkMsgBuilder.addDeviceProfileUpdateMsg(deviceProfileUpdateMsgBuilder.build()); testAutoGeneratedCodeByProtobuf(uplinkMsgBuilder); @@ -447,12 +427,26 @@ public class DeviceProfileEdgeTest extends AbstractEdgeTest { Optional deviceProfileUpdateMsgOpt = edgeImitator.findMessageByType(DeviceProfileUpdateMsg.class); Assert.assertTrue(deviceProfileUpdateMsgOpt.isPresent()); DeviceProfileUpdateMsg latestDeviceProfileUpdateMsg = deviceProfileUpdateMsgOpt.get(); - Assert.assertNotEquals(deviceProfileOnCloudName, latestDeviceProfileUpdateMsg.getName()); + deviceProfileMsg = JacksonUtil.fromEdgeString(latestDeviceProfileUpdateMsg.getEntity(), DeviceProfile.class); + Assert.assertNotNull(deviceProfileMsg); + Assert.assertNotEquals(deviceProfileOnCloudName, deviceProfileMsg.getName()); - Assert.assertNotEquals(deviceProfileOnCloud.getUuidId(), uuid); + Assert.assertNotEquals(deviceProfileOnCloud.getId(), deviceProfileMsg.getId()); - DeviceProfile deviceProfile = doGet("/api/deviceProfile/" + uuid, DeviceProfile.class); + DeviceProfile deviceProfile = doGet("/api/deviceProfile/" + deviceProfileMsg.getUuidId(), DeviceProfile.class); Assert.assertNotNull(deviceProfile); Assert.assertNotEquals(deviceProfileOnCloudName, deviceProfile.getName()); } + + private DeviceProfile buildDeviceProfileForUplinkMsg(String name) { + DeviceProfile deviceProfile = new DeviceProfile(); + deviceProfile.setId(new DeviceProfileId(UUID.randomUUID())); + deviceProfile.setTenantId(tenantId); + deviceProfile.setName(name); + deviceProfile.setDefault(false); + deviceProfile.setType(DeviceProfileType.DEFAULT); + deviceProfile.setTransportType(DeviceTransportType.DEFAULT); + deviceProfile.setProfileData(createProfileData()); + return deviceProfile; + } } diff --git a/application/src/test/java/org/thingsboard/server/edge/EdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/EdgeTest.java index fd34b0d973..b8e784aad5 100644 --- a/application/src/test/java/org/thingsboard/server/edge/EdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/EdgeTest.java @@ -17,6 +17,7 @@ package org.thingsboard.server.edge; import org.junit.Assert; import org.junit.Test; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.id.CustomerId; @@ -54,10 +55,9 @@ public class EdgeTest extends AbstractEdgeTest { Optional customerUpdateOpt = edgeImitator.findMessageByType(CustomerUpdateMsg.class); Assert.assertTrue(customerUpdateOpt.isPresent()); CustomerUpdateMsg customerUpdateMsg = customerUpdateOpt.get(); + Customer customerMsg = JacksonUtil.fromEdgeString(customerUpdateMsg.getEntity(), Customer.class); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, customerUpdateMsg.getMsgType()); - Assert.assertEquals(savedCustomer.getUuidId().getMostSignificantBits(), customerUpdateMsg.getIdMSB()); - Assert.assertEquals(savedCustomer.getUuidId().getLeastSignificantBits(), customerUpdateMsg.getIdLSB()); - Assert.assertEquals(savedCustomer.getTitle(), customerUpdateMsg.getTitle()); + Assert.assertEquals(savedCustomer, customerMsg); // unassign edge from customer edgeImitator.expectMessageAmount(2); diff --git a/application/src/test/java/org/thingsboard/server/edge/EntityViewEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/EntityViewEdgeTest.java index d56ded7014..7ccda39192 100644 --- a/application/src/test/java/org/thingsboard/server/edge/EntityViewEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/EntityViewEdgeTest.java @@ -15,12 +15,12 @@ */ package org.thingsboard.server.edge; -import com.datastax.oss.driver.api.core.uuid.Uuids; import com.fasterxml.jackson.core.type.TypeReference; import com.google.protobuf.AbstractMessage; import com.google.protobuf.InvalidProtocolBufferException; import org.junit.Assert; import org.junit.Test; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntityView; @@ -30,10 +30,10 @@ import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.service.DaoSqlTest; -import org.thingsboard.server.gen.edge.v1.EdgeEntityType; import org.thingsboard.server.gen.edge.v1.EntityViewUpdateMsg; import org.thingsboard.server.gen.edge.v1.EntityViewsRequestMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; @@ -68,8 +68,10 @@ public class EntityViewEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof EntityViewUpdateMsg); EntityViewUpdateMsg entityViewUpdateMsg = (EntityViewUpdateMsg) latestMessage; + EntityView entityView = JacksonUtil.fromEdgeString(entityViewUpdateMsg.getEntity(), EntityView.class); + Assert.assertNotNull(entityView); + Assert.assertEquals(savedEntityView, entityView); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, entityViewUpdateMsg.getMsgType()); - Assert.assertEquals(savedEntityView.getName(), entityViewUpdateMsg.getName()); // request entity view(s) for device UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); @@ -131,9 +133,10 @@ public class EntityViewEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof EntityViewUpdateMsg); entityViewUpdateMsg = (EntityViewUpdateMsg) latestMessage; + EntityView entityViewMsg = JacksonUtil.fromEdgeString(entityViewUpdateMsg.getEntity(), EntityView.class); + Assert.assertNotNull(entityViewMsg); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, entityViewUpdateMsg.getMsgType()); - Assert.assertEquals(savedCustomer.getUuidId().getMostSignificantBits(), entityViewUpdateMsg.getCustomerIdMSB()); - Assert.assertEquals(savedCustomer.getUuidId().getLeastSignificantBits(), entityViewUpdateMsg.getCustomerIdLSB()); + Assert.assertEquals(savedCustomer.getId(), entityViewMsg.getCustomerId()); // unassign entity view #2 from customer edgeImitator.expectMessageAmount(1); @@ -142,10 +145,10 @@ public class EntityViewEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof EntityViewUpdateMsg); entityViewUpdateMsg = (EntityViewUpdateMsg) latestMessage; + entityViewMsg = JacksonUtil.fromEdgeString(entityViewUpdateMsg.getEntity(), EntityView.class); + Assert.assertNotNull(entityViewMsg); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, entityViewUpdateMsg.getMsgType()); - Assert.assertEquals( - new CustomerId(EntityId.NULL_UUID), - new CustomerId(new UUID(entityViewUpdateMsg.getCustomerIdMSB(), entityViewUpdateMsg.getCustomerIdLSB()))); + Assert.assertEquals(new CustomerId(EntityId.NULL_UUID), entityViewMsg.getCustomerId()); // delete entity view #2 - messages expected edgeImitator.expectMessageAmount(1); @@ -165,17 +168,13 @@ public class EntityViewEdgeTest extends AbstractEdgeTest { public void testSendEntityViewToCloud() throws Exception { Device device = findDeviceByName("Edge Device 1"); - UUID uuid = Uuids.timeBased(); + EntityView entityViewMsg = buildEntityViewForUplinkMsg(device, "Edge EntityView 2"); UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); EntityViewUpdateMsg.Builder entityViewUpdateMsgBuilder = EntityViewUpdateMsg.newBuilder(); - entityViewUpdateMsgBuilder.setIdMSB(uuid.getMostSignificantBits()); - entityViewUpdateMsgBuilder.setIdLSB(uuid.getLeastSignificantBits()); - entityViewUpdateMsgBuilder.setName("Edge EntityView 2"); - entityViewUpdateMsgBuilder.setType("test"); - entityViewUpdateMsgBuilder.setEntityType(EdgeEntityType.DEVICE); - entityViewUpdateMsgBuilder.setEntityIdMSB(device.getUuidId().getMostSignificantBits()); - entityViewUpdateMsgBuilder.setEntityIdLSB(device.getUuidId().getLeastSignificantBits()); + entityViewUpdateMsgBuilder.setIdMSB(entityViewMsg.getUuidId().getMostSignificantBits()); + entityViewUpdateMsgBuilder.setIdLSB(entityViewMsg.getUuidId().getLeastSignificantBits()); + entityViewUpdateMsgBuilder.setEntity(JacksonUtil.toString(entityViewMsg)); entityViewUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); testAutoGeneratedCodeByProtobuf(entityViewUpdateMsgBuilder); uplinkMsgBuilder.addEntityViewUpdateMsg(entityViewUpdateMsgBuilder.build()); @@ -192,7 +191,7 @@ public class EntityViewEdgeTest extends AbstractEdgeTest { UplinkResponseMsg latestResponseMsg = edgeImitator.getLatestResponseMsg(); Assert.assertTrue(latestResponseMsg.getSuccess()); - EntityView entityView = doGet("/api/entityView/" + uuid, EntityView.class); + EntityView entityView = doGet("/api/entityView/" + entityViewMsg.getUuidId(), EntityView.class); Assert.assertNotNull(entityView); Assert.assertEquals("Edge EntityView 2", entityView.getName()); } @@ -204,17 +203,13 @@ public class EntityViewEdgeTest extends AbstractEdgeTest { String entityViewOnCloudName = StringUtils.randomAlphanumeric(15); EntityView entityViewOnCloud = saveEntityView(entityViewOnCloudName, device.getId()); - UUID uuid = Uuids.timeBased(); + EntityView entityViewMsg = buildEntityViewForUplinkMsg(device, entityViewOnCloudName); UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); EntityViewUpdateMsg.Builder entityViewUpdateMsgBuilder = EntityViewUpdateMsg.newBuilder(); - entityViewUpdateMsgBuilder.setIdMSB(uuid.getMostSignificantBits()); - entityViewUpdateMsgBuilder.setIdLSB(uuid.getLeastSignificantBits()); - entityViewUpdateMsgBuilder.setName(entityViewOnCloudName); - entityViewUpdateMsgBuilder.setType("test"); - entityViewUpdateMsgBuilder.setEntityType(EdgeEntityType.DEVICE); - entityViewUpdateMsgBuilder.setEntityIdMSB(device.getUuidId().getMostSignificantBits()); - entityViewUpdateMsgBuilder.setEntityIdLSB(device.getUuidId().getLeastSignificantBits()); + entityViewUpdateMsgBuilder.setIdMSB(entityViewMsg.getUuidId().getMostSignificantBits()); + entityViewUpdateMsgBuilder.setIdLSB(entityViewMsg.getUuidId().getLeastSignificantBits()); + entityViewUpdateMsgBuilder.setEntity(JacksonUtil.toString(entityViewMsg)); entityViewUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); testAutoGeneratedCodeByProtobuf(entityViewUpdateMsgBuilder); uplinkMsgBuilder.addEntityViewUpdateMsg(entityViewUpdateMsgBuilder.build()); @@ -232,13 +227,12 @@ public class EntityViewEdgeTest extends AbstractEdgeTest { Optional entityViewUpdateMsgOpt = edgeImitator.findMessageByType(EntityViewUpdateMsg.class); Assert.assertTrue(entityViewUpdateMsgOpt.isPresent()); EntityViewUpdateMsg latestEntityViewUpdateMsg = entityViewUpdateMsgOpt.get(); - Assert.assertNotEquals(entityViewOnCloudName, latestEntityViewUpdateMsg.getName()); - - UUID newEntityViewId = new UUID(latestEntityViewUpdateMsg.getIdMSB(), latestEntityViewUpdateMsg.getIdLSB()); - - Assert.assertNotEquals(entityViewOnCloud.getId().getId(), newEntityViewId); + entityViewMsg = JacksonUtil.fromEdgeString(latestEntityViewUpdateMsg.getEntity(), EntityView.class); + Assert.assertNotNull(entityViewMsg); + Assert.assertNotEquals(entityViewOnCloudName, entityViewMsg.getName()); + Assert.assertNotEquals(entityViewOnCloud.getId(), entityViewMsg.getId()); - EntityView entityView = doGet("/api/entityView/" + newEntityViewId, EntityView.class); + EntityView entityView = doGet("/api/entityView/" + entityViewMsg.getUuidId(), EntityView.class); Assert.assertNotNull(entityView); Assert.assertNotEquals(entityViewOnCloudName, entityView.getName()); } @@ -274,14 +268,11 @@ public class EntityViewEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof EntityViewUpdateMsg); EntityViewUpdateMsg entityViewUpdateMsg = (EntityViewUpdateMsg) latestMessage; + EntityView entityViewMsg = JacksonUtil.fromEdgeString(entityViewUpdateMsg.getEntity(), EntityView.class); + Assert.assertNotNull(entityViewMsg); + Assert.assertEquals(entityView, entityViewMsg); + Assert.assertEquals(device.getId(), entityViewMsg.getEntityId()); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, entityViewUpdateMsg.getMsgType()); - Assert.assertEquals(entityView.getType(), entityViewUpdateMsg.getType()); - Assert.assertEquals(entityView.getName(), entityViewUpdateMsg.getName()); - Assert.assertEquals(entityView.getUuidId().getMostSignificantBits(), entityViewUpdateMsg.getIdMSB()); - Assert.assertEquals(entityView.getUuidId().getLeastSignificantBits(), entityViewUpdateMsg.getIdLSB()); - Assert.assertEquals(device.getUuidId().getMostSignificantBits(), entityViewUpdateMsg.getEntityIdMSB()); - Assert.assertEquals(device.getUuidId().getLeastSignificantBits(), entityViewUpdateMsg.getEntityIdLSB()); - Assert.assertEquals(device.getId().getEntityType().name(), entityViewUpdateMsg.getEntityType().name()); testAutoGeneratedCodeByProtobuf(entityViewUpdateMsg); } @@ -309,4 +300,15 @@ public class EntityViewEdgeTest extends AbstractEdgeTest { return doPost("/api/entityView", entityView, EntityView.class); } + + private EntityView buildEntityViewForUplinkMsg(Device device, String name) { + EntityView entityView = new EntityView(); + entityView.setId(new EntityViewId(UUID.randomUUID())); + entityView.setTenantId(tenantId); + entityView.setName(name); + entityView.setType("test"); + entityView.setEntityId(device.getId()); + return entityView; + } + } diff --git a/application/src/test/java/org/thingsboard/server/edge/OtaPackageEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/OtaPackageEdgeTest.java index 5525d63f41..50e0be2cc1 100644 --- a/application/src/test/java/org/thingsboard/server/edge/OtaPackageEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/OtaPackageEdgeTest.java @@ -16,17 +16,16 @@ package org.thingsboard.server.edge; import com.google.protobuf.AbstractMessage; -import com.google.protobuf.ByteString; import org.junit.Assert; import org.junit.Test; import org.springframework.mock.web.MockMultipartFile; import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.OtaPackage; import org.thingsboard.server.common.data.OtaPackageInfo; import org.thingsboard.server.common.data.SaveOtaPackageInfoRequest; import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; -import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.gen.edge.v1.OtaPackageUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; @@ -59,22 +58,22 @@ public class OtaPackageEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof OtaPackageUpdateMsg); OtaPackageUpdateMsg otaPackageUpdateMsg = (OtaPackageUpdateMsg) latestMessage; + OtaPackage otaPackage = JacksonUtil.fromEdgeString(otaPackageUpdateMsg.getEntity(), OtaPackage.class); + Assert.assertNotNull(otaPackage); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, otaPackageUpdateMsg.getMsgType()); - Assert.assertEquals(savedFirmwareInfo.getUuidId().getMostSignificantBits(), otaPackageUpdateMsg.getIdMSB()); - Assert.assertEquals(savedFirmwareInfo.getUuidId().getLeastSignificantBits(), otaPackageUpdateMsg.getIdLSB()); - Assert.assertEquals(thermostatDeviceProfile.getUuidId().getMostSignificantBits(), otaPackageUpdateMsg.getDeviceProfileIdMSB()); - Assert.assertEquals(thermostatDeviceProfile.getUuidId().getLeastSignificantBits(), otaPackageUpdateMsg.getDeviceProfileIdLSB()); - Assert.assertEquals(FIRMWARE, OtaPackageType.valueOf(otaPackageUpdateMsg.getType())); - Assert.assertEquals("My firmware #1", otaPackageUpdateMsg.getTitle()); - Assert.assertEquals("v1.0", otaPackageUpdateMsg.getVersion()); - Assert.assertEquals("My firmware #1 v1.0", otaPackageUpdateMsg.getTag()); - Assert.assertEquals("http://localhost:8080/v1/package", otaPackageUpdateMsg.getUrl()); - Assert.assertFalse(otaPackageUpdateMsg.hasData()); - Assert.assertFalse(otaPackageUpdateMsg.hasFileName()); - Assert.assertFalse(otaPackageUpdateMsg.hasContentType()); - Assert.assertFalse(otaPackageUpdateMsg.hasChecksumAlgorithm()); - Assert.assertFalse(otaPackageUpdateMsg.hasChecksum()); - Assert.assertFalse(otaPackageUpdateMsg.hasDataSize()); + Assert.assertEquals(savedFirmwareInfo.getId(), otaPackage.getId()); + Assert.assertEquals(thermostatDeviceProfile.getId(), otaPackage.getDeviceProfileId()); + Assert.assertEquals(FIRMWARE, otaPackage.getType()); + Assert.assertEquals("My firmware #1", otaPackage.getTitle()); + Assert.assertEquals("v1.0", otaPackage.getVersion()); + Assert.assertEquals("My firmware #1 v1.0", otaPackage.getTag()); + Assert.assertEquals("http://localhost:8080/v1/package", otaPackage.getUrl()); + Assert.assertNull(otaPackage.getData()); + Assert.assertNull(otaPackage.getFileName()); + Assert.assertNull(otaPackage.getContentType()); + Assert.assertNull(otaPackage.getChecksumAlgorithm()); + Assert.assertNull(otaPackage.getChecksum()); + Assert.assertNull(otaPackage.getDataSize()); // delete ota package edgeImitator.expectMessageAmount(1); @@ -113,22 +112,22 @@ public class OtaPackageEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof OtaPackageUpdateMsg); OtaPackageUpdateMsg otaPackageUpdateMsg = (OtaPackageUpdateMsg) latestMessage; + OtaPackage otaPackage = JacksonUtil.fromEdgeString(otaPackageUpdateMsg.getEntity(), OtaPackage.class); + Assert.assertNotNull(otaPackage); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, otaPackageUpdateMsg.getMsgType()); - Assert.assertEquals(savedFirmwareInfo.getUuidId().getMostSignificantBits(), otaPackageUpdateMsg.getIdMSB()); - Assert.assertEquals(savedFirmwareInfo.getUuidId().getLeastSignificantBits(), otaPackageUpdateMsg.getIdLSB()); - Assert.assertEquals(thermostatDeviceProfile.getUuidId().getMostSignificantBits(), otaPackageUpdateMsg.getDeviceProfileIdMSB()); - Assert.assertEquals(thermostatDeviceProfile.getUuidId().getLeastSignificantBits(), otaPackageUpdateMsg.getDeviceProfileIdLSB()); - Assert.assertEquals(FIRMWARE, OtaPackageType.valueOf(otaPackageUpdateMsg.getType())); - Assert.assertEquals("My firmware #2", otaPackageUpdateMsg.getTitle()); - Assert.assertEquals("v2.0", otaPackageUpdateMsg.getVersion()); - Assert.assertEquals("My firmware #2 v2.0", otaPackageUpdateMsg.getTag()); - Assert.assertFalse(otaPackageUpdateMsg.hasUrl()); - Assert.assertEquals("firmware.bin", otaPackageUpdateMsg.getFileName()); - Assert.assertEquals("image/png", otaPackageUpdateMsg.getContentType()); - Assert.assertEquals(ChecksumAlgorithm.SHA256.name(), otaPackageUpdateMsg.getChecksumAlgorithm()); - Assert.assertEquals("62467691cf583d4fa78b18fafaf9801f505e0ef03baf0603fd4b0cd004cd1e75", otaPackageUpdateMsg.getChecksum()); - Assert.assertEquals(3L, otaPackageUpdateMsg.getDataSize()); - Assert.assertEquals(ByteString.copyFrom(new byte[]{1, 3, 5}), otaPackageUpdateMsg.getData()); + Assert.assertEquals(savedFirmwareInfo.getId(), otaPackage.getId()); + Assert.assertEquals(thermostatDeviceProfile.getId(), otaPackage.getDeviceProfileId()); + Assert.assertEquals(FIRMWARE, otaPackage.getType()); + Assert.assertEquals("My firmware #2", otaPackage.getTitle()); + Assert.assertEquals("v2.0", otaPackage.getVersion()); + Assert.assertEquals("My firmware #2 v2.0", otaPackage.getTag()); + Assert.assertFalse(otaPackage.hasUrl()); + Assert.assertEquals("firmware.bin", otaPackage.getFileName()); + Assert.assertEquals("image/png", otaPackage.getContentType()); + Assert.assertEquals(ChecksumAlgorithm.SHA256, otaPackage.getChecksumAlgorithm()); + Assert.assertEquals("62467691cf583d4fa78b18fafaf9801f505e0ef03baf0603fd4b0cd004cd1e75", otaPackage.getChecksum()); + Assert.assertEquals(3L, otaPackage.getDataSize().longValue()); + Assert.assertEquals(ByteBuffer.wrap(new byte[]{1, 3, 5}), otaPackage.getData()); // delete ota package edgeImitator.expectMessageAmount(1); diff --git a/application/src/test/java/org/thingsboard/server/edge/QueueEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/QueueEdgeTest.java index 32e8e7f222..faf8f6826b 100644 --- a/application/src/test/java/org/thingsboard/server/edge/QueueEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/QueueEdgeTest.java @@ -18,6 +18,7 @@ package org.thingsboard.server.edge; import com.google.protobuf.AbstractMessage; import org.junit.Assert; import org.junit.Test; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.queue.ProcessingStrategy; import org.thingsboard.server.common.data.queue.ProcessingStrategyType; import org.thingsboard.server.common.data.queue.Queue; @@ -63,24 +64,22 @@ public class QueueEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof QueueUpdateMsg); QueueUpdateMsg queueUpdateMsg = (QueueUpdateMsg) latestMessage; + Queue queueMsg = JacksonUtil.fromEdgeString(queueUpdateMsg.getEntity(), Queue.class); + Assert.assertNotNull(queueMsg); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, queueUpdateMsg.getMsgType()); - Assert.assertEquals(savedQueue.getUuidId().getMostSignificantBits(), queueUpdateMsg.getIdMSB()); - Assert.assertEquals(savedQueue.getUuidId().getLeastSignificantBits(), queueUpdateMsg.getIdLSB()); - Assert.assertEquals(savedQueue.getTenantId().getId().getMostSignificantBits(), queueUpdateMsg.getTenantIdMSB()); - Assert.assertEquals(savedQueue.getTenantId().getId().getLeastSignificantBits(), queueUpdateMsg.getTenantIdLSB()); - Assert.assertEquals("EdgeMain", queueUpdateMsg.getName()); - Assert.assertEquals("tb_rule_engine.EdgeMain", queueUpdateMsg.getTopic()); - Assert.assertEquals(25, queueUpdateMsg.getPollInterval()); - Assert.assertEquals(10, queueUpdateMsg.getPartitions()); - Assert.assertFalse(queueUpdateMsg.getConsumerPerPartition()); - Assert.assertEquals(2000, queueUpdateMsg.getPackProcessingTimeout()); - Assert.assertEquals(SubmitStrategyType.SEQUENTIAL_BY_ORIGINATOR.name(), queueUpdateMsg.getSubmitStrategy().getType()); - Assert.assertEquals(0, queueUpdateMsg.getSubmitStrategy().getBatchSize()); - Assert.assertEquals(ProcessingStrategyType.RETRY_ALL.name(), queueUpdateMsg.getProcessingStrategy().getType()); - Assert.assertEquals(3, queueUpdateMsg.getProcessingStrategy().getRetries()); - Assert.assertEquals(0.7, queueUpdateMsg.getProcessingStrategy().getFailurePercentage(), 1); - Assert.assertEquals(3, queueUpdateMsg.getProcessingStrategy().getPauseBetweenRetries()); - Assert.assertEquals(5, queueUpdateMsg.getProcessingStrategy().getMaxPauseBetweenRetries()); + Assert.assertEquals(savedQueue, queueMsg); + Assert.assertEquals("EdgeMain", queueMsg.getName()); + Assert.assertEquals("tb_rule_engine.EdgeMain", queueMsg.getTopic()); + Assert.assertEquals(25, queueMsg.getPollInterval()); + Assert.assertEquals(10, queueMsg.getPartitions()); + Assert.assertEquals(2000, queueMsg.getPackProcessingTimeout()); + Assert.assertEquals(SubmitStrategyType.SEQUENTIAL_BY_ORIGINATOR, queueMsg.getSubmitStrategy().getType()); + Assert.assertEquals(0, queueMsg.getSubmitStrategy().getBatchSize()); + Assert.assertEquals(ProcessingStrategyType.RETRY_ALL, queueMsg.getProcessingStrategy().getType()); + Assert.assertEquals(3, queueMsg.getProcessingStrategy().getRetries()); + Assert.assertEquals(0.7, queueMsg.getProcessingStrategy().getFailurePercentage(), 1); + Assert.assertEquals(3, queueMsg.getProcessingStrategy().getPauseBetweenRetries()); + Assert.assertEquals(5, queueMsg.getProcessingStrategy().getMaxPauseBetweenRetries()); // update queue edgeImitator.expectMessageAmount(1); @@ -90,8 +89,10 @@ public class QueueEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof QueueUpdateMsg); queueUpdateMsg = (QueueUpdateMsg) latestMessage; + queueMsg = JacksonUtil.fromEdgeString(queueUpdateMsg.getEntity(), Queue.class); + Assert.assertNotNull(queueMsg); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, queueUpdateMsg.getMsgType()); - Assert.assertEquals(50, queueUpdateMsg.getPollInterval()); + Assert.assertEquals(50, queueMsg.getPollInterval()); // delete queue edgeImitator.expectMessageAmount(1); diff --git a/application/src/test/java/org/thingsboard/server/edge/RelationEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/RelationEdgeTest.java index 04e6f3a3fb..a26dd9512a 100644 --- a/application/src/test/java/org/thingsboard/server/edge/RelationEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/RelationEdgeTest.java @@ -15,13 +15,14 @@ */ package org.thingsboard.server.edge; +import com.fasterxml.jackson.databind.node.TextNode; import com.google.protobuf.AbstractMessage; import org.junit.Assert; import org.junit.Test; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.asset.Asset; -import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.EntityIdFactory; +import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.dao.service.DaoSqlTest; @@ -30,8 +31,6 @@ import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.UplinkMsg; -import java.util.UUID; - import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @DaoSqlTest @@ -54,15 +53,10 @@ public class RelationEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof RelationUpdateMsg); RelationUpdateMsg relationUpdateMsg = (RelationUpdateMsg) latestMessage; + EntityRelation entityRelation = JacksonUtil.fromEdgeString(relationUpdateMsg.getEntity(), EntityRelation.class); + Assert.assertNotNull(entityRelation); + Assert.assertEquals(relation, entityRelation); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, relationUpdateMsg.getMsgType()); - Assert.assertEquals(relationUpdateMsg.getType(), relation.getType()); - Assert.assertEquals(relationUpdateMsg.getFromIdMSB(), relation.getFrom().getId().getMostSignificantBits()); - Assert.assertEquals(relationUpdateMsg.getFromIdLSB(), relation.getFrom().getId().getLeastSignificantBits()); - Assert.assertEquals(relationUpdateMsg.getToEntityType(), relation.getTo().getEntityType().name()); - Assert.assertEquals(relationUpdateMsg.getFromIdMSB(), relation.getFrom().getId().getMostSignificantBits()); - Assert.assertEquals(relationUpdateMsg.getToIdLSB(), relation.getTo().getId().getLeastSignificantBits()); - Assert.assertEquals(relationUpdateMsg.getToEntityType(), relation.getTo().getEntityType().name()); - Assert.assertEquals(relationUpdateMsg.getTypeGroup(), relation.getTypeGroup().name()); // delete relation edgeImitator.expectMessageAmount(1); @@ -78,15 +72,10 @@ public class RelationEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof RelationUpdateMsg); relationUpdateMsg = (RelationUpdateMsg) latestMessage; + entityRelation = JacksonUtil.fromEdgeString(relationUpdateMsg.getEntity(), EntityRelation.class); + Assert.assertNotNull(entityRelation); + Assert.assertEquals(relation, entityRelation); Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, relationUpdateMsg.getMsgType()); - Assert.assertEquals(relationUpdateMsg.getType(), relation.getType()); - Assert.assertEquals(relationUpdateMsg.getFromIdMSB(), relation.getFrom().getId().getMostSignificantBits()); - Assert.assertEquals(relationUpdateMsg.getFromIdLSB(), relation.getFrom().getId().getLeastSignificantBits()); - Assert.assertEquals(relationUpdateMsg.getToEntityType(), relation.getTo().getEntityType().name()); - Assert.assertEquals(relationUpdateMsg.getFromIdMSB(), relation.getFrom().getId().getMostSignificantBits()); - Assert.assertEquals(relationUpdateMsg.getToIdLSB(), relation.getTo().getId().getLeastSignificantBits()); - Assert.assertEquals(relationUpdateMsg.getToEntityType(), relation.getTo().getEntityType().name()); - Assert.assertEquals(relationUpdateMsg.getTypeGroup(), relation.getTypeGroup().name()); } @Test @@ -94,17 +83,11 @@ public class RelationEdgeTest extends AbstractEdgeTest { Device device1 = saveDeviceOnCloudAndVerifyDeliveryToEdge(); Device device2 = saveDeviceOnCloudAndVerifyDeliveryToEdge(); + EntityRelation entityRelation = buildEntityRelationForUplinkMsg(device1.getId(), device2.getId()); + UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); RelationUpdateMsg.Builder relationUpdateMsgBuilder = RelationUpdateMsg.newBuilder(); - relationUpdateMsgBuilder.setType("test"); - relationUpdateMsgBuilder.setTypeGroup(RelationTypeGroup.COMMON.name()); - relationUpdateMsgBuilder.setToIdMSB(device1.getId().getId().getMostSignificantBits()); - relationUpdateMsgBuilder.setToIdLSB(device1.getId().getId().getLeastSignificantBits()); - relationUpdateMsgBuilder.setToEntityType(device1.getId().getEntityType().name()); - relationUpdateMsgBuilder.setFromIdMSB(device2.getId().getId().getMostSignificantBits()); - relationUpdateMsgBuilder.setFromIdLSB(device2.getId().getId().getLeastSignificantBits()); - relationUpdateMsgBuilder.setFromEntityType(device2.getId().getEntityType().name()); - relationUpdateMsgBuilder.setAdditionalInfo("{}"); + relationUpdateMsgBuilder.setEntity(JacksonUtil.toString(entityRelation)); testAutoGeneratedCodeByProtobuf(relationUpdateMsgBuilder); uplinkMsgBuilder.addRelationUpdateMsg(relationUpdateMsgBuilder.build()); @@ -158,17 +141,19 @@ public class RelationEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof RelationUpdateMsg); RelationUpdateMsg relationUpdateMsg = (RelationUpdateMsg) latestMessage; + EntityRelation entityRelation = JacksonUtil.fromEdgeString(relationUpdateMsg.getEntity(), EntityRelation.class); + Assert.assertNotNull(entityRelation); + Assert.assertEquals(relation, entityRelation); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, relationUpdateMsg.getMsgType()); - Assert.assertEquals(relation.getType(), relationUpdateMsg.getType()); - - UUID fromUUID = new UUID(relationUpdateMsg.getFromIdMSB(), relationUpdateMsg.getFromIdLSB()); - EntityId fromEntityId = EntityIdFactory.getByTypeAndUuid(relationUpdateMsg.getFromEntityType(), fromUUID); - Assert.assertEquals(relation.getFrom(), fromEntityId); - - UUID toUUID = new UUID(relationUpdateMsg.getToIdMSB(), relationUpdateMsg.getToIdLSB()); - EntityId toEntityId = EntityIdFactory.getByTypeAndUuid(relationUpdateMsg.getToEntityType(), toUUID); - Assert.assertEquals(relation.getTo(), toEntityId); + } - Assert.assertEquals(relation.getTypeGroup().name(), relationUpdateMsg.getTypeGroup()); + private EntityRelation buildEntityRelationForUplinkMsg(DeviceId deviceId1, DeviceId deviceId2) { + EntityRelation relation = new EntityRelation(); + relation.setType("test"); + relation.setTypeGroup(RelationTypeGroup.COMMON); + relation.setTo(deviceId1); + relation.setFrom(deviceId2); + relation.setAdditionalInfo(TextNode.valueOf("{}")); + return relation; } } diff --git a/application/src/test/java/org/thingsboard/server/edge/RuleChainEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/RuleChainEdgeTest.java index 848b115c72..0322bd849e 100644 --- a/application/src/test/java/org/thingsboard/server/edge/RuleChainEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/RuleChainEdgeTest.java @@ -19,7 +19,6 @@ import com.google.protobuf.AbstractMessage; import org.junit.Assert; import org.junit.Test; import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.rule.engine.metadata.TbGetAttributesNode; import org.thingsboard.rule.engine.metadata.TbGetAttributesNodeConfiguration; import org.thingsboard.rule.engine.util.TbMsgSource; import org.thingsboard.server.common.data.edge.Edge; @@ -32,7 +31,6 @@ import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.gen.edge.v1.RuleChainMetadataRequestMsg; import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; import org.thingsboard.server.gen.edge.v1.RuleChainUpdateMsg; -import org.thingsboard.server.gen.edge.v1.RuleNodeProto; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.UplinkMsg; @@ -40,7 +38,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Optional; -import java.util.UUID; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -64,11 +61,12 @@ public class RuleChainEdgeTest extends AbstractEdgeTest { Optional ruleChainUpdateMsgOpt = edgeImitator.findMessageByType(RuleChainUpdateMsg.class); Assert.assertTrue(ruleChainUpdateMsgOpt.isPresent()); RuleChainUpdateMsg ruleChainUpdateMsg = ruleChainUpdateMsgOpt.get(); + RuleChain ruleChainMsg = JacksonUtil.fromEdgeString(ruleChainUpdateMsg.getEntity(), RuleChain.class); + Assert.assertNotNull(ruleChainMsg); Assert.assertTrue(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE.equals(ruleChainUpdateMsg.getMsgType()) || UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE.equals(ruleChainUpdateMsg.getMsgType())); - Assert.assertEquals(ruleChainUpdateMsg.getIdMSB(), savedRuleChain.getUuidId().getMostSignificantBits()); - Assert.assertEquals(ruleChainUpdateMsg.getIdLSB(), savedRuleChain.getUuidId().getLeastSignificantBits()); - Assert.assertEquals(ruleChainUpdateMsg.getName(), savedRuleChain.getName()); + Assert.assertEquals(savedRuleChain.getId(), ruleChainMsg.getId()); + Assert.assertEquals(savedRuleChain.getName(), ruleChainMsg.getName()); testRuleChainMetadataRequestMsg(savedRuleChain.getId()); @@ -113,8 +111,9 @@ public class RuleChainEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof RuleChainMetadataUpdateMsg); RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = (RuleChainMetadataUpdateMsg) latestMessage; - Assert.assertEquals(ruleChainMetadataUpdateMsg.getRuleChainIdMSB(), edgeRootRuleChainId.getId().getMostSignificantBits()); - Assert.assertEquals(ruleChainMetadataUpdateMsg.getRuleChainIdLSB(), edgeRootRuleChainId.getId().getLeastSignificantBits()); + RuleChainMetaData ruleChainMetadataMsg = JacksonUtil.fromEdgeString(ruleChainMetadataUpdateMsg.getEntity(), RuleChainMetaData.class); + Assert.assertNotNull(ruleChainMetadataMsg); + Assert.assertEquals(edgeRootRuleChainId, ruleChainMetadataMsg.getRuleChainId()); testAutoGeneratedCodeByProtobuf(ruleChainMetadataUpdateMsg); } @@ -138,12 +137,12 @@ public class RuleChainEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof RuleChainMetadataUpdateMsg); RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = (RuleChainMetadataUpdateMsg) latestMessage; - RuleChainId receivedRuleChainId = - new RuleChainId(new UUID(ruleChainMetadataUpdateMsg.getRuleChainIdMSB(), ruleChainMetadataUpdateMsg.getRuleChainIdLSB())); - Assert.assertEquals(ruleChainId, receivedRuleChainId); + RuleChainMetaData ruleChainMetadataMsg = JacksonUtil.fromEdgeString(ruleChainMetadataUpdateMsg.getEntity(), RuleChainMetaData.class); + Assert.assertNotNull(ruleChainMetadataMsg); + Assert.assertEquals(ruleChainId, ruleChainMetadataMsg.getRuleChainId()); - for (RuleNodeProto ruleNodeProto : ruleChainMetadataUpdateMsg.getNodesList()) { - Assert.assertEquals(CONFIGURATION_VERSION, ruleNodeProto.getConfigurationVersion()); + for (RuleNode ruleNode : ruleChainMetadataMsg.getNodes()) { + Assert.assertEquals(CONFIGURATION_VERSION, ruleNode.getConfigurationVersion()); } } @@ -208,10 +207,11 @@ public class RuleChainEdgeTest extends AbstractEdgeTest { Optional ruleChainUpdateMsgOpt = edgeImitator.findMessageByType(RuleChainUpdateMsg.class); Assert.assertTrue(ruleChainUpdateMsgOpt.isPresent()); RuleChainUpdateMsg ruleChainUpdateMsg = ruleChainUpdateMsgOpt.get(); + RuleChain ruleChainMsg = JacksonUtil.fromEdgeString(ruleChainUpdateMsg.getEntity(), RuleChain.class); + Assert.assertNotNull(ruleChainMsg); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, ruleChainUpdateMsg.getMsgType()); - Assert.assertEquals(ruleChainUpdateMsg.getIdMSB(), savedRuleChain.getUuidId().getMostSignificantBits()); - Assert.assertEquals(ruleChainUpdateMsg.getIdLSB(), savedRuleChain.getUuidId().getLeastSignificantBits()); - Assert.assertTrue(ruleChainUpdateMsg.getRoot()); + Assert.assertTrue(ruleChainMsg.isRoot()); + Assert.assertEquals(savedRuleChain.getId(), ruleChainMsg.getId()); // revert root rule chain edgeImitator.expectMessageAmount(1); diff --git a/application/src/test/java/org/thingsboard/server/edge/TenantEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/TenantEdgeTest.java index 95fdc9f56a..97ac2eacc5 100644 --- a/application/src/test/java/org/thingsboard/server/edge/TenantEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/TenantEdgeTest.java @@ -17,16 +17,15 @@ package org.thingsboard.server.edge; import org.junit.Assert; import org.junit.Test; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.TenantProfile; -import org.thingsboard.server.common.data.id.TenantProfileId; import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg; import org.thingsboard.server.gen.edge.v1.TenantUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import java.util.Optional; -import java.util.UUID; @DaoSqlTest public class TenantEdgeTest extends AbstractEdgeTest { @@ -49,15 +48,14 @@ public class TenantEdgeTest extends AbstractEdgeTest { Optional tenantProfileUpdateMsgOpt = edgeImitator.findMessageByType(TenantProfileUpdateMsg.class); Assert.assertTrue(tenantProfileUpdateMsgOpt.isPresent()); TenantProfileUpdateMsg tenantProfileUpdateMsg = tenantProfileUpdateMsgOpt.get(); + Tenant tenantMsg = JacksonUtil.fromEdgeString(tenantUpdateMsg.getEntity(), Tenant.class); + Assert.assertNotNull(tenantMsg); + Assert.assertEquals(savedTenant, tenantMsg); + TenantProfile tenantProfileMsg = JacksonUtil.fromEdgeString(tenantProfileUpdateMsg.getEntity(), TenantProfile.class); + Assert.assertNotNull(tenantProfileMsg); + Assert.assertEquals(tenantMsg.getTenantProfileId(), tenantProfileMsg.getId()); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, tenantUpdateMsg.getMsgType()); - Assert.assertEquals(savedTenant.getUuidId().getMostSignificantBits(), tenantUpdateMsg.getIdMSB()); - Assert.assertEquals(savedTenant.getUuidId().getLeastSignificantBits(), tenantUpdateMsg.getIdLSB()); - Assert.assertEquals(savedTenant.getTitle(), tenantUpdateMsg.getTitle()); - Assert.assertEquals("Updated Title for Tenant Edge Test", tenantUpdateMsg.getTitle()); - Assert.assertEquals(savedTenant.getTenantProfileId(), new TenantProfileId - (new UUID(tenantUpdateMsg.getProfileIdMSB(), tenantUpdateMsg.getProfileIdLSB()))); - Assert.assertEquals(savedTenant.getTenantProfileId(), new TenantProfileId - (new UUID(tenantProfileUpdateMsg.getIdMSB(), tenantProfileUpdateMsg.getIdLSB()))); + Assert.assertEquals("Updated Title for Tenant Edge Test", tenantMsg.getTitle()); //change tenant profile for tenant TenantProfile tenantProfile = createTenantProfile(); @@ -68,18 +66,17 @@ public class TenantEdgeTest extends AbstractEdgeTest { tenantUpdateMsgOpt = edgeImitator.findMessageByType(TenantUpdateMsg.class); Assert.assertTrue(tenantUpdateMsgOpt.isPresent()); tenantUpdateMsg = tenantUpdateMsgOpt.get(); + tenantMsg = JacksonUtil.fromEdgeString(tenantUpdateMsg.getEntity(), Tenant.class); + Assert.assertNotNull(tenantMsg); tenantProfileUpdateMsgOpt = edgeImitator.findMessageByType(TenantProfileUpdateMsg.class); Assert.assertTrue(tenantProfileUpdateMsgOpt.isPresent()); tenantProfileUpdateMsg = tenantProfileUpdateMsgOpt.get(); + tenantProfileMsg = JacksonUtil.fromEdgeString(tenantProfileUpdateMsg.getEntity(), TenantProfile.class); + Assert.assertNotNull(tenantProfileMsg); // tenant update Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, tenantUpdateMsg.getMsgType()); - Assert.assertEquals(savedTenant.getUuidId().getMostSignificantBits(), tenantUpdateMsg.getIdMSB()); - Assert.assertEquals(savedTenant.getUuidId().getLeastSignificantBits(), tenantUpdateMsg.getIdLSB()); - Assert.assertEquals(savedTenant.getTitle(), tenantUpdateMsg.getTitle()); - Assert.assertEquals(savedTenant.getTenantProfileId(), new TenantProfileId - (new UUID(tenantUpdateMsg.getProfileIdMSB(), tenantUpdateMsg.getProfileIdLSB()))); - Assert.assertEquals(savedTenant.getTenantProfileId(), new TenantProfileId - (new UUID(tenantProfileUpdateMsg.getIdMSB(), tenantProfileUpdateMsg.getIdLSB()))); + Assert.assertEquals(savedTenant, tenantMsg); + Assert.assertEquals(savedTenant.getTenantProfileId(), tenantProfileMsg.getId()); } private TenantProfile createTenantProfile() { diff --git a/application/src/test/java/org/thingsboard/server/edge/TenantProfileEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/TenantProfileEdgeTest.java index 6f665ce462..695c3a1dea 100644 --- a/application/src/test/java/org/thingsboard/server/edge/TenantProfileEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/TenantProfileEdgeTest.java @@ -18,6 +18,7 @@ package org.thingsboard.server.edge; import com.google.protobuf.AbstractMessage; import org.junit.Assert; import org.junit.Test; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.queue.ProcessingStrategy; @@ -55,12 +56,12 @@ public class TenantProfileEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof TenantProfileUpdateMsg); TenantProfileUpdateMsg tenantProfileUpdateMsg = (TenantProfileUpdateMsg) latestMessage; + TenantProfile tenantProfileMsg = JacksonUtil.fromEdgeString(tenantProfileUpdateMsg.getEntity(), TenantProfile.class); + Assert.assertNotNull(tenantProfileMsg); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, tenantProfileUpdateMsg.getMsgType()); - Assert.assertEquals(edgeTenantProfile.getUuidId().getMostSignificantBits(), tenantProfileUpdateMsg.getIdMSB()); - Assert.assertEquals(edgeTenantProfile.getUuidId().getLeastSignificantBits(), tenantProfileUpdateMsg.getIdLSB()); - Assert.assertEquals(edgeTenantProfile.getDescription(), tenantProfileUpdateMsg.getDescription()); - Assert.assertEquals("Updated tenant profile Edge Test", tenantProfileUpdateMsg.getDescription()); - Assert.assertEquals("Tenant Profile Edge Test", tenantProfileUpdateMsg.getName()); + Assert.assertEquals(edgeTenantProfile, tenantProfileMsg); + Assert.assertEquals("Updated tenant profile Edge Test", tenantProfileMsg.getDescription()); + Assert.assertEquals("Tenant Profile Edge Test", tenantProfileMsg.getName()); loginTenantAdmin(); } diff --git a/application/src/test/java/org/thingsboard/server/edge/UserEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/UserEdgeTest.java index 3dc398b9f3..2254f8d051 100644 --- a/application/src/test/java/org/thingsboard/server/edge/UserEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/UserEdgeTest.java @@ -20,10 +20,12 @@ import org.junit.Assert; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.common.data.security.UserCredentials; import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.UplinkMsg; @@ -57,13 +59,14 @@ public class UserEdgeTest extends AbstractEdgeTest { Optional userUpdateMsgOpt = edgeImitator.findMessageByType(UserUpdateMsg.class); Assert.assertTrue(userUpdateMsgOpt.isPresent()); UserUpdateMsg userUpdateMsg = userUpdateMsgOpt.get(); + User userMsg = JacksonUtil.fromEdgeString(userUpdateMsg.getEntity(), User.class); + Assert.assertNotNull(userMsg); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, userUpdateMsg.getMsgType()); - Assert.assertEquals(savedTenantAdmin.getUuidId().getMostSignificantBits(), userUpdateMsg.getIdMSB()); - Assert.assertEquals(savedTenantAdmin.getUuidId().getLeastSignificantBits(), userUpdateMsg.getIdLSB()); - Assert.assertEquals(savedTenantAdmin.getAuthority().name(), userUpdateMsg.getAuthority()); - Assert.assertEquals(savedTenantAdmin.getEmail(), userUpdateMsg.getEmail()); - Assert.assertEquals(savedTenantAdmin.getFirstName(), userUpdateMsg.getFirstName()); - Assert.assertEquals(savedTenantAdmin.getLastName(), userUpdateMsg.getLastName()); + Assert.assertEquals(savedTenantAdmin.getId(), userMsg.getId()); + Assert.assertEquals(savedTenantAdmin.getAuthority(), userMsg.getAuthority()); + Assert.assertEquals(savedTenantAdmin.getEmail(), userMsg.getEmail()); + Assert.assertEquals(savedTenantAdmin.getFirstName(), userMsg.getFirstName()); + Assert.assertEquals(savedTenantAdmin.getLastName(), userMsg.getLastName()); Optional userCredentialsUpdateMsgOpt = edgeImitator.findMessageByType(UserCredentialsUpdateMsg.class); Assert.assertTrue(userCredentialsUpdateMsgOpt.isPresent()); @@ -75,8 +78,10 @@ public class UserEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof UserUpdateMsg); userUpdateMsg = (UserUpdateMsg) latestMessage; + userMsg = JacksonUtil.fromEdgeString(userUpdateMsg.getEntity(), User.class); + Assert.assertNotNull(userMsg); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, userUpdateMsg.getMsgType()); - Assert.assertEquals(savedTenantAdmin.getLastName(), userUpdateMsg.getLastName()); + Assert.assertEquals(savedTenantAdmin.getLastName(), userMsg.getLastName()); // update user credentials login(savedTenantAdmin.getEmail(), "tenant"); @@ -90,9 +95,10 @@ public class UserEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof UserCredentialsUpdateMsg); UserCredentialsUpdateMsg userCredentialsUpdateMsg = (UserCredentialsUpdateMsg) latestMessage; - Assert.assertEquals(savedTenantAdmin.getUuidId().getMostSignificantBits(), userCredentialsUpdateMsg.getUserIdMSB()); - Assert.assertEquals(savedTenantAdmin.getUuidId().getLeastSignificantBits(), userCredentialsUpdateMsg.getUserIdLSB()); - Assert.assertTrue(passwordEncoder.matches(changePasswordRequest.getNewPassword(), userCredentialsUpdateMsg.getPassword())); + UserCredentials userCredentialsMsg = JacksonUtil.fromEdgeString(userCredentialsUpdateMsg.getEntity(), UserCredentials.class); + Assert.assertNotNull(userCredentialsMsg); + Assert.assertEquals(savedTenantAdmin.getId(), userCredentialsMsg.getUserId()); + Assert.assertTrue(passwordEncoder.matches(changePasswordRequest.getNewPassword(), userCredentialsMsg.getPassword())); loginTenantAdmin(); @@ -138,15 +144,15 @@ public class UserEdgeTest extends AbstractEdgeTest { Optional userUpdateMsgOpt = edgeImitator.findMessageByType(UserUpdateMsg.class); Assert.assertTrue(userUpdateMsgOpt.isPresent()); UserUpdateMsg userUpdateMsg = userUpdateMsgOpt.get(); + User userMsg = JacksonUtil.fromEdgeString(userUpdateMsg.getEntity(), User.class); + Assert.assertNotNull(userMsg); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, userUpdateMsg.getMsgType()); - Assert.assertEquals(savedCustomerUser.getUuidId().getMostSignificantBits(), userUpdateMsg.getIdMSB()); - Assert.assertEquals(savedCustomerUser.getUuidId().getLeastSignificantBits(), userUpdateMsg.getIdLSB()); - Assert.assertEquals(savedCustomerUser.getCustomerId().getId().getMostSignificantBits(), userUpdateMsg.getCustomerIdMSB()); - Assert.assertEquals(savedCustomerUser.getCustomerId().getId().getLeastSignificantBits(), userUpdateMsg.getCustomerIdLSB()); - Assert.assertEquals(savedCustomerUser.getAuthority().name(), userUpdateMsg.getAuthority()); - Assert.assertEquals(savedCustomerUser.getEmail(), userUpdateMsg.getEmail()); - Assert.assertEquals(savedCustomerUser.getFirstName(), userUpdateMsg.getFirstName()); - Assert.assertEquals(savedCustomerUser.getLastName(), userUpdateMsg.getLastName()); + Assert.assertEquals(savedCustomerUser.getId(), userMsg.getId()); + Assert.assertEquals(savedCustomerUser.getCustomerId(), userMsg.getCustomerId()); + Assert.assertEquals(savedCustomerUser.getAuthority(), userMsg.getAuthority()); + Assert.assertEquals(savedCustomerUser.getEmail(), userMsg.getEmail()); + Assert.assertEquals(savedCustomerUser.getFirstName(), userMsg.getFirstName()); + Assert.assertEquals(savedCustomerUser.getLastName(), userMsg.getLastName()); // update user edgeImitator.expectMessageAmount(1); @@ -156,8 +162,10 @@ public class UserEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof UserUpdateMsg); userUpdateMsg = (UserUpdateMsg) latestMessage; + userMsg = JacksonUtil.fromEdgeString(userUpdateMsg.getEntity(), User.class); + Assert.assertNotNull(userMsg); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, userUpdateMsg.getMsgType()); - Assert.assertEquals(savedCustomerUser.getLastName(), userUpdateMsg.getLastName()); + Assert.assertEquals(savedCustomerUser.getLastName(), userMsg.getLastName()); // update user credentials login(savedCustomerUser.getEmail(), "customer"); @@ -171,9 +179,10 @@ public class UserEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof UserCredentialsUpdateMsg); UserCredentialsUpdateMsg userCredentialsUpdateMsg = (UserCredentialsUpdateMsg) latestMessage; - Assert.assertEquals(savedCustomerUser.getUuidId().getMostSignificantBits(), userCredentialsUpdateMsg.getUserIdMSB()); - Assert.assertEquals(savedCustomerUser.getUuidId().getLeastSignificantBits(), userCredentialsUpdateMsg.getUserIdLSB()); - Assert.assertTrue(passwordEncoder.matches(changePasswordRequest.getNewPassword(), userCredentialsUpdateMsg.getPassword())); + UserCredentials userCredentialsMsg = JacksonUtil.fromEdgeString(userCredentialsUpdateMsg.getEntity(), UserCredentials.class); + Assert.assertNotNull(userCredentialsMsg); + Assert.assertEquals(savedCustomerUser.getId(), userCredentialsMsg.getUserId()); + Assert.assertTrue(passwordEncoder.matches(changePasswordRequest.getNewPassword(), userCredentialsMsg.getPassword())); loginTenantAdmin(); @@ -211,8 +220,9 @@ public class UserEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof UserCredentialsUpdateMsg); UserCredentialsUpdateMsg userCredentialsUpdateMsg = (UserCredentialsUpdateMsg) latestMessage; - Assert.assertEquals(tenantAdminUserId.getId().getMostSignificantBits(), userCredentialsUpdateMsg.getUserIdMSB()); - Assert.assertEquals(tenantAdminUserId.getId().getLeastSignificantBits(), userCredentialsUpdateMsg.getUserIdLSB()); + UserCredentials userCredentialsMsg = JacksonUtil.fromEdgeString(userCredentialsUpdateMsg.getEntity(), UserCredentials.class); + Assert.assertNotNull(userCredentialsMsg); + Assert.assertEquals(tenantAdminUserId, userCredentialsMsg.getUserId()); } @Test @@ -235,8 +245,9 @@ public class UserEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof UserCredentialsUpdateMsg); UserCredentialsUpdateMsg userCredentialsUpdateMsg = (UserCredentialsUpdateMsg) latestMessage; - Assert.assertEquals(userCredentialsUpdateMsg.getUserIdMSB(), tenantAdminUserId.getId().getMostSignificantBits()); - Assert.assertEquals(userCredentialsUpdateMsg.getUserIdLSB(), tenantAdminUserId.getId().getLeastSignificantBits()); + UserCredentials userCredentialsMsg = JacksonUtil.fromEdgeString(userCredentialsUpdateMsg.getEntity(), UserCredentials.class); + Assert.assertNotNull(userCredentialsMsg); + Assert.assertEquals(tenantAdminUserId, userCredentialsMsg.getUserId()); testAutoGeneratedCodeByProtobuf(userCredentialsUpdateMsg); } diff --git a/application/src/test/java/org/thingsboard/server/edge/WidgetEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/WidgetEdgeTest.java index e958741cd2..bb6d09f7bc 100644 --- a/application/src/test/java/org/thingsboard/server/edge/WidgetEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/WidgetEdgeTest.java @@ -43,11 +43,12 @@ public class WidgetEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof WidgetsBundleUpdateMsg); WidgetsBundleUpdateMsg widgetsBundleUpdateMsg = (WidgetsBundleUpdateMsg) latestMessage; + WidgetsBundle widgetsBundleMsg = JacksonUtil.fromEdgeString(widgetsBundleUpdateMsg.getEntity(), WidgetsBundle.class); + Assert.assertNotNull(widgetsBundleMsg); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, widgetsBundleUpdateMsg.getMsgType()); - Assert.assertEquals(savedWidgetsBundle.getUuidId().getMostSignificantBits(), widgetsBundleUpdateMsg.getIdMSB()); - Assert.assertEquals(savedWidgetsBundle.getUuidId().getLeastSignificantBits(), widgetsBundleUpdateMsg.getIdLSB()); - Assert.assertEquals(savedWidgetsBundle.getAlias(), widgetsBundleUpdateMsg.getAlias()); - Assert.assertEquals(savedWidgetsBundle.getTitle(), widgetsBundleUpdateMsg.getTitle()); + Assert.assertEquals(savedWidgetsBundle.getId(), widgetsBundleMsg.getId()); + Assert.assertEquals(savedWidgetsBundle.getAlias(), widgetsBundleMsg.getAlias()); + Assert.assertEquals(savedWidgetsBundle.getTitle(), widgetsBundleMsg.getTitle()); testAutoGeneratedCodeByProtobuf(widgetsBundleUpdateMsg); // create widget type @@ -64,15 +65,10 @@ public class WidgetEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof WidgetTypeUpdateMsg); WidgetTypeUpdateMsg widgetTypeUpdateMsg = (WidgetTypeUpdateMsg) latestMessage; + WidgetType widgetsType = JacksonUtil.fromEdgeString(widgetTypeUpdateMsg.getEntity(), WidgetType.class); + Assert.assertNotNull(widgetsType); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, widgetTypeUpdateMsg.getMsgType()); - Assert.assertEquals(savedWidgetType.getUuidId().getMostSignificantBits(), widgetTypeUpdateMsg.getIdMSB()); - Assert.assertEquals(savedWidgetType.getUuidId().getLeastSignificantBits(), widgetTypeUpdateMsg.getIdLSB()); - Assert.assertEquals(savedWidgetType.getFqn(), widgetTypeUpdateMsg.getFqn()); - Assert.assertEquals(savedWidgetType.getName(), widgetTypeUpdateMsg.getName()); - Assert.assertTrue(widgetTypeUpdateMsg.getDeprecated()); - Assert.assertEquals("bundle_alias", widgetTypeUpdateMsg.getBundleAlias()); - Assert.assertEquals("type_alias", widgetTypeUpdateMsg.getAlias()); - Assert.assertEquals(JacksonUtil.toJsonNode(widgetTypeUpdateMsg.getDescriptorJson()), savedWidgetType.getDescriptor()); + Assert.assertEquals(savedWidgetType, widgetsType); // update widget bundle edgeImitator.expectMessageAmount(1); @@ -82,8 +78,10 @@ public class WidgetEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof WidgetsBundleUpdateMsg); widgetsBundleUpdateMsg = (WidgetsBundleUpdateMsg) latestMessage; + widgetsBundleMsg = JacksonUtil.fromEdgeString(widgetsBundleUpdateMsg.getEntity(), WidgetsBundle.class); + Assert.assertNotNull(widgetsBundleMsg); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, widgetsBundleUpdateMsg.getMsgType()); - Assert.assertEquals(savedWidgetsBundle.getTitle(), widgetsBundleUpdateMsg.getTitle()); + Assert.assertEquals(savedWidgetsBundle.getTitle(), widgetsBundleMsg.getTitle()); // update widget type edgeImitator.expectMessageAmount(1); @@ -93,8 +91,10 @@ public class WidgetEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof WidgetTypeUpdateMsg); widgetTypeUpdateMsg = (WidgetTypeUpdateMsg) latestMessage; + widgetsType = JacksonUtil.fromEdgeString(widgetTypeUpdateMsg.getEntity(), WidgetType.class); + Assert.assertNotNull(widgetsType); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, widgetTypeUpdateMsg.getMsgType()); - Assert.assertEquals(savedWidgetType.getName(), widgetTypeUpdateMsg.getName()); + Assert.assertEquals(savedWidgetType.getName(), widgetsType.getName()); // delete widget type edgeImitator.expectMessageAmount(1); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java index fe08934f79..abc9a53202 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java @@ -32,6 +32,8 @@ import java.util.Set; @ApiModel public class DashboardInfo extends BaseData implements HasName, HasTenantId, HasTitle { + private static final long serialVersionUID = -9080404114760433799L; + private TenantId tenantId; @NoXss @Length(fieldName = "title") diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/UserCredentialsId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/UserCredentialsId.java index 543ac1ba6f..d0a29695c0 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/UserCredentialsId.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/UserCredentialsId.java @@ -15,11 +15,15 @@ */ package org.thingsboard.server.common.data.id; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + import java.util.UUID; public class UserCredentialsId extends UUIDBased { - public UserCredentialsId(UUID id){ + @JsonCreator + public UserCredentialsId(@JsonProperty("id")UUID id){ super(id); } } diff --git a/common/edge-api/src/main/proto/edge.proto b/common/edge-api/src/main/proto/edge.proto index 71e84304ed..7d5c57928f 100644 --- a/common/edge-api/src/main/proto/edge.proto +++ b/common/edge-api/src/main/proto/edge.proto @@ -36,6 +36,7 @@ enum EdgeVersion { V_3_4_0 = 2; V_3_6_0 = 3; V_3_6_1 = 4; + V_3_6_2 = 5; } /** @@ -138,25 +139,28 @@ message RuleChainUpdateMsg { UpdateMsgType msgType = 1; int64 idMSB = 2; int64 idLSB = 3; - string name = 4; - optional int64 firstRuleNodeIdMSB = 5; - optional int64 firstRuleNodeIdLSB = 6; - bool root = 7; - bool debugMode = 8; - string configuration = 9; + string name = 4 [deprecated = true]; + optional int64 firstRuleNodeIdMSB = 5 [deprecated = true]; + optional int64 firstRuleNodeIdLSB = 6 [deprecated = true]; + bool root = 7 [deprecated = true]; + bool debugMode = 8 [deprecated = true]; + string configuration = 9 [deprecated = true]; + string entity = 10; } message RuleChainMetadataUpdateMsg { UpdateMsgType msgType = 1; - int64 ruleChainIdMSB = 2; - int64 ruleChainIdLSB = 3; - int32 firstNodeIndex = 4; - repeated RuleNodeProto nodes = 5; - repeated NodeConnectionInfoProto connections = 6; - repeated RuleChainConnectionInfoProto ruleChainConnections = 7; + int64 ruleChainIdMSB = 2 [deprecated = true]; + int64 ruleChainIdLSB = 3 [deprecated = true]; + int32 firstNodeIndex = 4 [deprecated = true]; + repeated RuleNodeProto nodes = 5 [deprecated = true]; + repeated NodeConnectionInfoProto connections = 6 [deprecated = true]; + repeated RuleChainConnectionInfoProto ruleChainConnections = 7 [deprecated = true]; + string entity = 8; } message RuleNodeProto { + option deprecated = true; int64 idMSB = 1; int64 idLSB = 2; string type = 3; @@ -169,12 +173,14 @@ message RuleNodeProto { } message NodeConnectionInfoProto { + option deprecated = true; int32 fromIndex = 1; int32 toIndex = 2; string type = 3; } message RuleChainConnectionInfoProto { + option deprecated = true; int32 fromIndex = 1; int64 targetRuleChainIdMSB = 2; int64 targetRuleChainIdLSB = 3; @@ -186,184 +192,196 @@ message DashboardUpdateMsg { UpdateMsgType msgType = 1; int64 idMSB = 2; int64 idLSB = 3; - optional int64 customerIdMSB = 4; - optional int64 customerIdLSB = 5; - string title = 6; - string configuration = 7; - optional string assignedCustomers = 8; - optional string image = 9; - optional int32 mobileOrder = 10; - bool mobileHide = 11; + optional int64 customerIdMSB = 4 [deprecated = true]; + optional int64 customerIdLSB = 5 [deprecated = true]; + string title = 6 [deprecated = true]; + string configuration = 7 [deprecated = true]; + optional string assignedCustomers = 8 [deprecated = true]; + optional string image = 9 [deprecated = true]; + optional int32 mobileOrder = 10 [deprecated = true]; + bool mobileHide = 11 [deprecated = true]; + string entity = 12; } message DeviceUpdateMsg { UpdateMsgType msgType = 1; int64 idMSB = 2; int64 idLSB = 3; - optional int64 customerIdMSB = 4; - optional int64 customerIdLSB = 5; - optional int64 deviceProfileIdMSB = 6; - optional int64 deviceProfileIdLSB = 7; - string name = 8; - string type = 9; - optional string label = 10; - optional string additionalInfo = 11; + optional int64 customerIdMSB = 4 [deprecated = true]; + optional int64 customerIdLSB = 5 [deprecated = true]; + optional int64 deviceProfileIdMSB = 6 [deprecated = true]; + optional int64 deviceProfileIdLSB = 7 [deprecated = true]; + string name = 8 [deprecated = true]; + string type = 9 [deprecated = true]; + optional string label = 10 [deprecated = true]; + optional string additionalInfo = 11 [deprecated = true]; optional string conflictName = 12 [deprecated = true]; - optional int64 firmwareIdMSB = 13; - optional int64 firmwareIdLSB = 14; - optional bytes deviceDataBytes = 15; - optional int64 softwareIdMSB = 16; - optional int64 softwareIdLSB = 17; + optional int64 firmwareIdMSB = 13 [deprecated = true]; + optional int64 firmwareIdLSB = 14 [deprecated = true]; + optional bytes deviceDataBytes = 15 [deprecated = true]; + optional int64 softwareIdMSB = 16 [deprecated = true]; + optional int64 softwareIdLSB = 17 [deprecated = true]; + string entity = 18; } message DeviceProfileUpdateMsg { UpdateMsgType msgType = 1; int64 idMSB = 2; int64 idLSB = 3; - string name = 4; - optional string description = 5; - bool default = 6; - string type = 7; - optional string transportType = 8; - optional string provisionType = 9; - int64 defaultRuleChainIdMSB = 10; - int64 defaultRuleChainIdLSB = 11; - string defaultQueueName = 12; - bytes profileDataBytes = 13; - optional string provisionDeviceKey = 14; - optional bytes image = 15; - optional int64 firmwareIdMSB = 16; - optional int64 firmwareIdLSB = 17; - optional int64 softwareIdMSB = 18; - optional int64 softwareIdLSB = 19; - optional int64 defaultDashboardIdMSB = 20; - optional int64 defaultDashboardIdLSB = 21; + string name = 4 [deprecated = true]; + optional string description = 5 [deprecated = true]; + bool default = 6 [deprecated = true]; + string type = 7 [deprecated = true]; + optional string transportType = 8 [deprecated = true]; + optional string provisionType = 9 [deprecated = true]; + int64 defaultRuleChainIdMSB = 10 [deprecated = true]; + int64 defaultRuleChainIdLSB = 11 [deprecated = true]; + string defaultQueueName = 12 [deprecated = true]; + bytes profileDataBytes = 13 [deprecated = true]; + optional string provisionDeviceKey = 14 [deprecated = true]; + optional bytes image = 15 [deprecated = true]; + optional int64 firmwareIdMSB = 16 [deprecated = true]; + optional int64 firmwareIdLSB = 17 [deprecated = true]; + optional int64 softwareIdMSB = 18 [deprecated = true]; + optional int64 softwareIdLSB = 19 [deprecated = true]; + optional int64 defaultDashboardIdMSB = 20 [deprecated = true]; + optional int64 defaultDashboardIdLSB = 21 [deprecated = true]; + string entity = 22; } message AssetProfileUpdateMsg { UpdateMsgType msgType = 1; int64 idMSB = 2; int64 idLSB = 3; - string name = 4; - optional string description = 5; - bool default = 6; - int64 defaultRuleChainIdMSB = 7; - int64 defaultRuleChainIdLSB = 8; - int64 defaultDashboardIdMSB = 9; - int64 defaultDashboardIdLSB = 10; - optional string defaultQueueName = 11; - optional bytes image = 12; + string name = 4 [deprecated = true]; + optional string description = 5 [deprecated = true]; + bool default = 6 [deprecated = true]; + int64 defaultRuleChainIdMSB = 7 [deprecated = true]; + int64 defaultRuleChainIdLSB = 8 [deprecated = true]; + int64 defaultDashboardIdMSB = 9 [deprecated = true]; + int64 defaultDashboardIdLSB = 10 [deprecated = true]; + optional string defaultQueueName = 11 [deprecated = true]; + optional bytes image = 12 [deprecated = true]; + string entity = 13; } message DeviceCredentialsUpdateMsg { - int64 deviceIdMSB = 1; - int64 deviceIdLSB = 2; - string credentialsType = 3; - string credentialsId = 4; - optional string credentialsValue = 5; + int64 deviceIdMSB = 1 [deprecated = true]; + int64 deviceIdLSB = 2 [deprecated = true]; + string credentialsType = 3 [deprecated = true]; + string credentialsId = 4 [deprecated = true]; + optional string credentialsValue = 5 [deprecated = true]; + string entity = 6; } message AssetUpdateMsg { UpdateMsgType msgType = 1; int64 idMSB = 2; int64 idLSB = 3; - optional int64 customerIdMSB = 4; - optional int64 customerIdLSB = 5; - string name = 6; - string type = 7; - optional string label = 8; - optional string additionalInfo = 9; - optional int64 assetProfileIdMSB = 10; - optional int64 assetProfileIdLSB = 11; + optional int64 customerIdMSB = 4 [deprecated = true]; + optional int64 customerIdLSB = 5 [deprecated = true]; + string name = 6 [deprecated = true]; + string type = 7 [deprecated = true]; + optional string label = 8 [deprecated = true]; + optional string additionalInfo = 9 [deprecated = true]; + optional int64 assetProfileIdMSB = 10 [deprecated = true]; + optional int64 assetProfileIdLSB = 11 [deprecated = true]; + string entity = 12; } message EntityViewUpdateMsg { UpdateMsgType msgType = 1; int64 idMSB = 2; int64 idLSB = 3; - optional int64 customerIdMSB = 4; - optional int64 customerIdLSB = 5; - string name = 6; - string type = 7; - int64 entityIdMSB = 8; - int64 entityIdLSB = 9; - EdgeEntityType entityType = 10; - optional string additionalInfo = 11; + optional int64 customerIdMSB = 4 [deprecated = true]; + optional int64 customerIdLSB = 5 [deprecated = true]; + string name = 6 [deprecated = true]; + string type = 7 [deprecated = true]; + int64 entityIdMSB = 8 [deprecated = true]; + int64 entityIdLSB = 9 [deprecated = true]; + EdgeEntityType entityType = 10 [deprecated = true]; + optional string additionalInfo = 11 [deprecated = true]; + string entity = 12; } message AlarmUpdateMsg { UpdateMsgType msgType = 1; int64 idMSB = 2; int64 idLSB = 3; - string name = 4; - string type = 5; - string originatorType = 6; + string name = 4 [deprecated = true]; + string type = 5 [deprecated = true]; + string originatorType = 6 [deprecated = true]; string originatorName = 7; - string severity = 8; - string status = 9; - int64 startTs = 10; - int64 endTs = 11; - int64 ackTs = 12; - int64 clearTs = 13; - string details = 14; - bool propagate = 15; - bool propagateToOwner = 16; - bool propagateToTenant = 17; + string severity = 8 [deprecated = true]; + string status = 9 [deprecated = true]; + int64 startTs = 10 [deprecated = true]; + int64 endTs = 11 [deprecated = true]; + int64 ackTs = 12 [deprecated = true]; + int64 clearTs = 13 [deprecated = true]; + string details = 14 [deprecated = true]; + bool propagate = 15 [deprecated = true]; + bool propagateToOwner = 16 [deprecated = true]; + bool propagateToTenant = 17 [deprecated = true]; + string entity = 18; } message CustomerUpdateMsg { UpdateMsgType msgType = 1; int64 idMSB = 2; int64 idLSB = 3; - string title = 4; - optional string country = 5; - optional string state = 6; - optional string city = 7; - optional string address = 8; - optional string address2 = 9; - optional string zip = 10; - optional string phone = 11; - optional string email = 12; - optional string additionalInfo = 13; + string title = 4 [deprecated = true]; + optional string country = 5 [deprecated = true]; + optional string state = 6 [deprecated = true]; + optional string city = 7 [deprecated = true]; + optional string address = 8 [deprecated = true]; + optional string address2 = 9 [deprecated = true]; + optional string zip = 10 [deprecated = true]; + optional string phone = 11 [deprecated = true]; + optional string email = 12 [deprecated = true]; + optional string additionalInfo = 13 [deprecated = true]; + string entity = 14; } message RelationUpdateMsg { UpdateMsgType msgType = 1; - int64 fromIdMSB = 2; - int64 fromIdLSB = 3; - string fromEntityType = 4; - int64 toIdMSB = 5; - int64 toIdLSB = 6; - string toEntityType = 7; - string type = 8; - optional string typeGroup = 9; - string additionalInfo = 10; + int64 fromIdMSB = 2 [deprecated = true]; + int64 fromIdLSB = 3 [deprecated = true]; + string fromEntityType = 4 [deprecated = true]; + int64 toIdMSB = 5 [deprecated = true]; + int64 toIdLSB = 6 [deprecated = true]; + string toEntityType = 7 [deprecated = true]; + string type = 8 [deprecated = true]; + optional string typeGroup = 9 [deprecated = true]; + string additionalInfo = 10 [deprecated = true]; + string entity = 11; } message UserUpdateMsg { UpdateMsgType msgType = 1; int64 idMSB = 2; int64 idLSB = 3; - optional int64 customerIdMSB = 4; - optional int64 customerIdLSB = 5; - string email = 6; - string authority = 7; - optional string firstName = 8; - optional string lastName = 9; - optional string additionalInfo = 10; + optional int64 customerIdMSB = 4 [deprecated = true]; + optional int64 customerIdLSB = 5 [deprecated = true]; + string email = 6 [deprecated = true]; + string authority = 7 [deprecated = true]; + optional string firstName = 8 [deprecated = true]; + optional string lastName = 9 [deprecated = true]; + optional string additionalInfo = 10 [deprecated = true]; + string entity = 11; } message WidgetsBundleUpdateMsg { UpdateMsgType msgType = 1; int64 idMSB = 2; int64 idLSB = 3; - string title = 4; - string alias = 5; - optional bytes image = 6; - bool isSystem = 7; - optional string description = 8; + string title = 4 [deprecated = true]; + string alias = 5 [deprecated = true]; + optional bytes image = 6 [deprecated = true]; + bool isSystem = 7 [deprecated = true]; + optional string description = 8 [deprecated = true]; optional string widgets = 9; - optional int32 order = 10; + optional int32 order = 10 [deprecated = true]; + string entity = 11; } message WidgetTypeUpdateMsg { @@ -372,57 +390,62 @@ message WidgetTypeUpdateMsg { int64 idLSB = 3; optional string bundleAlias = 4 [deprecated = true]; optional string alias = 5 [deprecated = true]; - optional string name = 6; - optional string descriptorJson = 7; - bool isSystem = 8; - optional string image = 9; - optional string description = 10; - optional string fqn = 11; - bool deprecated = 12; + optional string name = 6 [deprecated = true]; + optional string descriptorJson = 7 [deprecated = true]; + bool isSystem = 8 [deprecated = true]; + optional string image = 9 [deprecated = true]; + optional string description = 10 [deprecated = true]; + optional string fqn = 11 [deprecated = true]; + bool deprecated = 12 [deprecated = true]; repeated string tags = 13; + string entity = 14; } message AdminSettingsUpdateMsg { - bool isSystem = 1; - string key = 2; - string jsonValue = 3; + bool isSystem = 1 [deprecated = true]; + string key = 2 [deprecated = true]; + string jsonValue = 3 [deprecated = true]; + string entity = 4; } message UserCredentialsUpdateMsg { - int64 userIdMSB = 1; - int64 userIdLSB = 2; - bool enabled = 3; - string password = 4; + int64 userIdMSB = 1 [deprecated = true]; + int64 userIdLSB = 2 [deprecated = true]; + bool enabled = 3 [deprecated = true]; + string password = 4 [deprecated = true]; + string entity = 5; } message TenantUpdateMsg { UpdateMsgType msgType = 1; - int64 idMSB = 2; - int64 idLSB = 3; - string title = 4; - int64 profileIdMSB = 5; - int64 profileIdLSB = 6; - string region = 7; - optional string country = 8; - optional string state = 9; - optional string city = 10; - optional string address = 11; - optional string address2 = 12; - optional string zip = 13; - optional string phone = 14; - optional string email = 15; - optional string additionalInfo = 16; + int64 idMSB = 2 [deprecated = true]; + int64 idLSB = 3 [deprecated = true]; + string title = 4 [deprecated = true]; + int64 profileIdMSB = 5 [deprecated = true]; + int64 profileIdLSB = 6 [deprecated = true]; + string region = 7 [deprecated = true]; + optional string country = 8 [deprecated = true]; + optional string state = 9 [deprecated = true]; + optional string city = 10 [deprecated = true]; + optional string address = 11 [deprecated = true]; + optional string address2 = 12 [deprecated = true]; + optional string zip = 13 [deprecated = true]; + optional string phone = 14 [deprecated = true]; + optional string email = 15 [deprecated = true]; + optional string additionalInfo = 16 [deprecated = true]; + string entity = 17; } message TenantProfileUpdateMsg { UpdateMsgType msgType = 1; - int64 idMSB = 2; - int64 idLSB = 3; - string name = 4; - optional string description = 5; - bool default = 6; - bool isolatedRuleChain = 7; - bytes profileDataBytes = 8; + int64 idMSB = 2 [deprecated = true]; + int64 idLSB = 3 [deprecated = true]; + string name = 4 [deprecated = true]; + optional string description = 5 [deprecated = true]; + bool default = 6 [deprecated = true]; + bool isolatedRuleChain = 7 [deprecated = true]; + bytes profileDataBytes = 8 [deprecated = true]; + string entity = 9; } message ResourceUpdateMsg { @@ -519,44 +542,48 @@ message OtaPackageUpdateMsg { UpdateMsgType msgType = 1; int64 idMSB = 2; int64 idLSB = 3; - int64 deviceProfileIdMSB = 4; - int64 deviceProfileIdLSB = 5; - string type = 6; - string title = 7; - string version = 8; - string tag = 9; - optional string url = 10; - optional string fileName = 11; - optional string contentType = 12; - optional string checksumAlgorithm = 13; - optional string checksum = 14; - optional int64 dataSize = 15; - optional bytes data = 16; - optional string additionalInfo = 17; + int64 deviceProfileIdMSB = 4 [deprecated = true]; + int64 deviceProfileIdLSB = 5 [deprecated = true]; + string type = 6 [deprecated = true]; + string title = 7 [deprecated = true]; + string version = 8 [deprecated = true]; + string tag = 9 [deprecated = true]; + optional string url = 10 [deprecated = true]; + optional string fileName = 11 [deprecated = true]; + optional string contentType = 12 [deprecated = true]; + optional string checksumAlgorithm = 13 [deprecated = true]; + optional string checksum = 14 [deprecated = true]; + optional int64 dataSize = 15 [deprecated = true]; + optional bytes data = 16 [deprecated = true]; + optional string additionalInfo = 17 [deprecated = true]; + string entity = 18; } message QueueUpdateMsg { UpdateMsgType msgType = 1; int64 idMSB = 2; int64 idLSB = 3; - int64 tenantIdMSB = 4; - int64 tenantIdLSB = 5; - string name = 6; - string topic = 7; - int32 pollInterval = 8; - int32 partitions = 9; - bool consumerPerPartition = 10; - int64 packProcessingTimeout = 11; - SubmitStrategyProto submitStrategy = 12; - ProcessingStrategyProto processingStrategy = 13; + int64 tenantIdMSB = 4 [deprecated = true]; + int64 tenantIdLSB = 5 [deprecated = true]; + string name = 6 [deprecated = true]; + string topic = 7 [deprecated = true]; + int32 pollInterval = 8 [deprecated = true]; + int32 partitions = 9 [deprecated = true]; + bool consumerPerPartition = 10 [deprecated = true]; + int64 packProcessingTimeout = 11 [deprecated = true]; + SubmitStrategyProto submitStrategy = 12 [deprecated = true]; + ProcessingStrategyProto processingStrategy = 13 [deprecated = true]; + string entity = 14; } message SubmitStrategyProto { + option deprecated = true; string type = 1; int32 batchSize = 2; } message ProcessingStrategyProto { + option deprecated = true; string type = 1; int32 retries = 2; double failurePercentage = 3; diff --git a/common/util/src/main/java/org/thingsboard/common/util/JacksonUtil.java b/common/util/src/main/java/org/thingsboard/common/util/JacksonUtil.java index ee2f91ec4d..499c0b55a5 100644 --- a/common/util/src/main/java/org/thingsboard/common/util/JacksonUtil.java +++ b/common/util/src/main/java/org/thingsboard/common/util/JacksonUtil.java @@ -19,6 +19,7 @@ import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.json.JsonWriteFeature; import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.MapperFeature; @@ -61,6 +62,9 @@ public class JacksonUtil { .configure(JsonWriteFeature.QUOTE_FIELD_NAMES.mappedFeature(), false) .configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true) .build(); + public static final ObjectMapper IGNORE_UNKNOWN_PROPERTIES_JSON_MAPPER = JsonMapper.builder() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .build(); public static ObjectMapper getObjectMapperWithJavaTimeModule() { return new ObjectMapper().registerModule(new JavaTimeModule()); @@ -106,6 +110,14 @@ public class JacksonUtil { } } + public static T fromStringIgnoreUnknownProperties(String string, Class clazz) { + try { + return string != null ? IGNORE_UNKNOWN_PROPERTIES_JSON_MAPPER.readValue(string, clazz) : null; + } catch (IOException e) { + throw new IllegalArgumentException("The given string value cannot be transformed to Json object: " + string, e); + } + } + public static T fromBytes(byte[] bytes, Class clazz) { try { return bytes != null ? OBJECT_MAPPER.readValue(bytes, clazz) : null; diff --git a/common/util/src/test/java/org/thingsboard/common/util/JacksonUtilTest.java b/common/util/src/test/java/org/thingsboard/common/util/JacksonUtilTest.java index 2ba2384acd..808fb22834 100644 --- a/common/util/src/test/java/org/thingsboard/common/util/JacksonUtilTest.java +++ b/common/util/src/test/java/org/thingsboard/common/util/JacksonUtilTest.java @@ -15,10 +15,15 @@ */ package org.thingsboard.common.util; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.Assert; import org.junit.Test; +import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.id.AssetId; + +import java.util.UUID; public class JacksonUtilTest { @@ -32,4 +37,22 @@ public class JacksonUtilTest { Assert.assertThrows(IllegalArgumentException.class, () -> JacksonUtil.toJsonNode(data)); // syntax exception due to missing quotes in the field name! } + @Test + public void fail_on_unknown_properties_edge_mapper_test() throws JsonProcessingException { + Asset asset = new Asset(); + asset.setId(new AssetId(UUID.randomUUID())); + asset.setName("Test"); + asset.setType("type"); + String serializedAsset = JacksonUtil.toString(asset); + JsonNode jsonNode = JacksonUtil.IGNORE_UNKNOWN_PROPERTIES_JSON_MAPPER.readTree(serializedAsset); + // case: add new field to serialized Asset string and check for backward compatibility with original Asset object + ((ObjectNode) jsonNode).put("test", (String) null); + serializedAsset = JacksonUtil.toString(jsonNode); + // deserialize with FAIL_ON_UNKNOWN_PROPERTIES = false + Asset result = JacksonUtil.fromStringIgnoreUnknownProperties(serializedAsset, Asset.class); + Assert.assertNotNull(result); + Assert.assertEquals(asset.getId(), result.getId()); + Assert.assertEquals(asset.getName(), result.getName()); + Assert.assertEquals(asset.getType(), result.getType()); + } } \ No newline at end of file From 4c06a26e02a99b8414911e0535ea1d6aab9e2336 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Wed, 27 Sep 2023 10:31:37 +0300 Subject: [PATCH 109/252] Refactoring asset and device processors, add fix for customerId --- .../rpc/processor/alarm/AlarmEdgeProcessor.java | 5 +++-- .../rpc/processor/alarm/BaseAlarmProcessor.java | 2 +- .../rpc/processor/asset/AssetEdgeProcessor.java | 9 +++++++++ .../asset/AssetProfileEdgeProcessor.java | 4 ++-- .../rpc/processor/asset/BaseAssetProcessor.java | 4 ++++ .../asset/BaseAssetProfileProcessor.java | 7 ++++--- .../dashboard/BaseDashboardProcessor.java | 2 +- .../device/BaseDeviceProfileProcessor.java | 7 ++++--- .../processor/device/DeviceEdgeProcessor.java | 13 +++++++++++-- .../device/DeviceProfileEdgeProcessor.java | 4 ++-- .../entityview/BaseEntityViewProcessor.java | 2 +- .../relation/RelationEdgeProcessor.java | 4 ++-- .../server/edge/AssetProfileEdgeTest.java | 8 ++++---- .../server/edge/CustomerEdgeTest.java | 4 ++-- .../server/edge/DashboardEdgeTest.java | 10 +++++----- .../thingsboard/server/edge/DeviceEdgeTest.java | 2 +- .../server/edge/DeviceProfileEdgeTest.java | 12 ++++++------ .../org/thingsboard/server/edge/EdgeTest.java | 2 +- .../server/edge/EntityViewEdgeTest.java | 10 +++++----- .../server/edge/OtaPackageEdgeTest.java | 4 ++-- .../thingsboard/server/edge/QueueEdgeTest.java | 4 ++-- .../server/edge/RelationEdgeTest.java | 6 +++--- .../server/edge/RuleChainEdgeTest.java | 8 ++++---- .../thingsboard/server/edge/TenantEdgeTest.java | 8 ++++---- .../server/edge/TenantProfileEdgeTest.java | 2 +- .../thingsboard/server/edge/UserEdgeTest.java | 16 ++++++++-------- .../thingsboard/server/edge/WidgetEdgeTest.java | 8 ++++---- 27 files changed, 96 insertions(+), 71 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/AlarmEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/AlarmEdgeProcessor.java index c15b79356e..1a06f91149 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/AlarmEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/AlarmEdgeProcessor.java @@ -47,11 +47,11 @@ import java.util.UUID; @TbCoreComponent public class AlarmEdgeProcessor extends BaseAlarmProcessor { - public ListenableFuture processAlarmMsgFromEdge(TenantId tenantId, EdgeId edgeId, AlarmUpdateMsg alarmUpdateMsg) { + public ListenableFuture processAlarmMsgFromEdge(TenantId tenantId, EdgeId edgeId, AlarmUpdateMsg alarmUpdateMsg, EdgeVersion edgeVersion) { log.trace("[{}] processAlarmMsgFromEdge [{}]", tenantId, alarmUpdateMsg); try { edgeSynchronizationManager.getEdgeId().set(edgeId); - return processAlarmMsg(tenantId, alarmUpdateMsg); + return processAlarmMsg(tenantId, alarmUpdateMsg, edgeVersion); } finally { edgeSynchronizationManager.getEdgeId().remove(); } @@ -123,4 +123,5 @@ public class AlarmEdgeProcessor extends BaseAlarmProcessor { } while (pageData != null && pageData.hasNext()); return futures; } + } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java index 284b20d71e..b89c2d4dc4 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java @@ -52,7 +52,7 @@ public abstract class BaseAlarmProcessor extends BaseEdgeProcessor { AlarmId alarmId = new AlarmId(new UUID(alarmUpdateMsg.getIdMSB(), alarmUpdateMsg.getIdLSB())); boolean isEdgeProtoDeprecated = EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion); Alarm alarm = isEdgeProtoDeprecated ? createDeprecatedAlarm(tenantId, alarmUpdateMsg) - : JacksonUtil.fromEdgeString(alarmUpdateMsg.getEntity(), Alarm.class); + : JacksonUtil.fromStringIgnoreUnknownProperties(alarmUpdateMsg.getEntity(), Alarm.class); if (alarm == null) { throw new RuntimeException("[{" + tenantId + "}] alarmUpdateMsg {" + alarmUpdateMsg + "} cannot be converted to alarm"); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessor.java index 8b04d838e8..2a140175ed 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessor.java @@ -29,6 +29,7 @@ import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.edge.EdgeEventType; import org.thingsboard.server.common.data.id.AssetId; +import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.msg.TbMsgType; @@ -145,4 +146,12 @@ public class AssetEdgeProcessor extends BaseAssetProcessor { } return downlinkMsg; } + + @Override + protected void setCustomerId(TenantId tenantId, CustomerId customerId, Asset asset, AssetUpdateMsg assetUpdateMsg, boolean isEdgeVersionDeprecated) { + CustomerId customerUUID = isEdgeVersionDeprecated + ? safeGetCustomerId(assetUpdateMsg.getCustomerIdMSB(), assetUpdateMsg.getCustomerIdLSB()) + : asset.getCustomerId() != null ? asset.getCustomerId() : customerId; + asset.setCustomerId(customerUUID); + } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetProfileEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetProfileEdgeProcessor.java index c9b9329531..b7b6690a87 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetProfileEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetProfileEdgeProcessor.java @@ -143,10 +143,10 @@ public class AssetProfileEdgeProcessor extends BaseAssetProfileProcessor { } @Override - protected void setDefaultDashboardId(TenantId tenantId, AssetProfile assetProfile, AssetProfileUpdateMsg assetProfileUpdateMsg, boolean isEdgeVersionDeprecated) { + protected void setDefaultDashboardId(TenantId tenantId, DashboardId dashboardId, AssetProfile assetProfile, AssetProfileUpdateMsg assetProfileUpdateMsg, boolean isEdgeVersionDeprecated) { UUID defaultDashboardUUID = isEdgeVersionDeprecated ? safeGetUUID(assetProfileUpdateMsg.getDefaultDashboardIdMSB(), assetProfileUpdateMsg.getDefaultDashboardIdLSB()) - : assetProfile.getDefaultDashboardId() != null ? assetProfile.getDefaultDashboardId().getId() : null; + : assetProfile.getDefaultDashboardId() != null ? assetProfile.getDefaultDashboardId().getId() : (dashboardId != null ? dashboardId.getId() : null); assetProfile.setDefaultDashboardId(defaultDashboardUUID != null ? new DashboardId(defaultDashboardUUID) : null); } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProcessor.java index 343fd2f80e..46a58588d7 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProcessor.java @@ -60,6 +60,7 @@ public abstract class BaseAssetProcessor extends BaseEdgeProcessor { assetNameUpdated = true; } asset.setName(assetName); + setCustomerId(tenantId, created ? null : assetById.getCustomerId(), asset, assetUpdateMsg, isEdgeProtoDeprecated); assetValidator.validate(asset, Asset::getTenantId); if (created) { @@ -92,4 +93,7 @@ public abstract class BaseAssetProcessor extends BaseEdgeProcessor { asset.setCustomerId(customerId); return asset; } + + protected abstract void setCustomerId(TenantId tenantId, CustomerId customerId, Asset asset, AssetUpdateMsg assetUpdateMsg, boolean isEdgeVersionDeprecated); + } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProfileProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProfileProcessor.java index 378d229e06..d829df4a32 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProfileProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProfileProcessor.java @@ -22,6 +22,7 @@ import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.id.AssetProfileId; +import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg; @@ -39,7 +40,7 @@ public abstract class BaseAssetProfileProcessor extends BaseEdgeProcessor { try { AssetProfile assetProfile = isEdgeProtoDeprecated ? createAssetProfile(tenantId, assetProfileId, assetProfileUpdateMsg) - : JacksonUtil.fromEdgeString(assetProfileUpdateMsg.getEntity(), AssetProfile.class); + : JacksonUtil.fromStringIgnoreUnknownProperties(assetProfileUpdateMsg.getEntity(), AssetProfile.class); if (assetProfile == null) { throw new RuntimeException("[{" + tenantId + "}] assetProfileUpdateMsg {" + assetProfileUpdateMsg + "} cannot be converted to asset profile"); } @@ -62,7 +63,7 @@ public abstract class BaseAssetProfileProcessor extends BaseEdgeProcessor { setDefaultRuleChainId(tenantId, assetProfile); setDefaultEdgeRuleChainId(assetProfile, created ? null : assetProfileById.getDefaultRuleChainId(), assetProfileUpdateMsg, isEdgeProtoDeprecated); - setDefaultDashboardId(tenantId, assetProfile, assetProfileUpdateMsg, isEdgeProtoDeprecated); + setDefaultDashboardId(tenantId, created ? null : assetProfileById.getDefaultDashboardId(), assetProfile, assetProfileUpdateMsg, isEdgeProtoDeprecated); assetProfileValidator.validate(assetProfile, AssetProfile::getTenantId); if (created) { @@ -95,5 +96,5 @@ public abstract class BaseAssetProfileProcessor extends BaseEdgeProcessor { protected abstract void setDefaultEdgeRuleChainId(AssetProfile assetProfile, RuleChainId ruleChainId, AssetProfileUpdateMsg assetProfileUpdateMsg, boolean isEdgeVersionDeprecated); - protected abstract void setDefaultDashboardId(TenantId tenantId, AssetProfile assetProfile, AssetProfileUpdateMsg assetProfileUpdateMsg, boolean isEdgeVersionDeprecated); + protected abstract void setDefaultDashboardId(TenantId tenantId, DashboardId dashboardId, AssetProfile assetProfile, AssetProfileUpdateMsg assetProfileUpdateMsg, boolean isEdgeVersionDeprecated); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/BaseDashboardProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/BaseDashboardProcessor.java index 4474b29b4d..53db17a3ba 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/BaseDashboardProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/BaseDashboardProcessor.java @@ -35,7 +35,7 @@ public abstract class BaseDashboardProcessor extends BaseEdgeProcessor { boolean created = false; Dashboard dashboard = isEdgeProtoDeprecated ? createDashboard(tenantId, deprecatedDashboardId, dashboardUpdateMsg) - : JacksonUtil.fromEdgeString(dashboardUpdateMsg.getEntity(), Dashboard.class); + : JacksonUtil.fromStringIgnoreUnknownProperties(dashboardUpdateMsg.getEntity(), Dashboard.class); if (dashboard == null) { throw new RuntimeException("[{" + tenantId + "}] dashboardUpdateMsg {" + dashboardUpdateMsg + "} cannot be converted to dashboard"); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProfileProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProfileProcessor.java index a48a578e48..38d781fb13 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProfileProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProfileProcessor.java @@ -26,6 +26,7 @@ import org.thingsboard.server.common.data.DeviceProfileType; import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.device.profile.DeviceProfileData; +import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.RuleChainId; @@ -51,7 +52,7 @@ public abstract class BaseDeviceProfileProcessor extends BaseEdgeProcessor { try { DeviceProfile deviceProfile = isEdgeVersionProtoDeprecated ? createDeviceProfile(tenantId, deviceProfileId, deviceProfileUpdateMsg) - : JacksonUtil.fromEdgeString(deviceProfileUpdateMsg.getEntity(), DeviceProfile.class); + : JacksonUtil.fromStringIgnoreUnknownProperties(deviceProfileUpdateMsg.getEntity(), DeviceProfile.class); if (deviceProfile == null) { throw new RuntimeException("[{" + tenantId + "}] deviceProfileUpdateMsg {" + deviceProfileUpdateMsg + "} cannot be converted to device profile"); } @@ -74,7 +75,7 @@ public abstract class BaseDeviceProfileProcessor extends BaseEdgeProcessor { setDefaultRuleChainId(tenantId, deviceProfile); setDefaultEdgeRuleChainId(deviceProfile, created ? null : deviceProfileById.getDefaultRuleChainId(), deviceProfileUpdateMsg, isEdgeVersionProtoDeprecated); - setDefaultDashboardId(tenantId, deviceProfile, deviceProfileUpdateMsg, isEdgeVersionProtoDeprecated); + setDefaultDashboardId(tenantId, created ? null : deviceProfileById.getDefaultDashboardId(), deviceProfile, deviceProfileUpdateMsg, isEdgeVersionProtoDeprecated); deviceProfileValidator.validate(deviceProfile, DeviceProfile::getTenantId); if (created) { @@ -127,5 +128,5 @@ public abstract class BaseDeviceProfileProcessor extends BaseEdgeProcessor { protected abstract void setDefaultEdgeRuleChainId(DeviceProfile deviceProfile, RuleChainId ruleChainId, DeviceProfileUpdateMsg deviceProfileUpdateMsg, boolean isEdgeVersionDeprecated); - protected abstract void setDefaultDashboardId(TenantId tenantId, DeviceProfile deviceProfile, DeviceProfileUpdateMsg deviceProfileUpdateMsg, boolean isEdgeVersionDeprecated); + protected abstract void setDefaultDashboardId(TenantId tenantId, DashboardId dashboardId, DeviceProfile deviceProfile, DeviceProfileUpdateMsg deviceProfileUpdateMsg, boolean isEdgeVersionDeprecated); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java index 17eec5eb0c..20cb591320 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java @@ -32,6 +32,7 @@ import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.edge.EdgeEventType; +import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; @@ -96,12 +97,12 @@ public class DeviceEdgeProcessor extends BaseDeviceProcessor { } } - public ListenableFuture processDeviceCredentialsMsgFromEdge(TenantId tenantId, EdgeId edgeId, DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg) { + public ListenableFuture processDeviceCredentialsMsgFromEdge(TenantId tenantId, EdgeId edgeId, DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg, EdgeVersion edgeVersion) { log.debug("[{}] Executing processDeviceCredentialsMsgFromEdge, deviceCredentialsUpdateMsg [{}]", tenantId, deviceCredentialsUpdateMsg); try { edgeSynchronizationManager.getEdgeId().set(edgeId); - updateDeviceCredentials(tenantId, deviceCredentialsUpdateMsg); + updateDeviceCredentials(tenantId, deviceCredentialsUpdateMsg, edgeVersion); } finally { edgeSynchronizationManager.getEdgeId().remove(); } @@ -286,4 +287,12 @@ public class DeviceEdgeProcessor extends BaseDeviceProcessor { .addDeviceCredentialsRequestMsg(deviceCredentialsRequestMsg); return builder.build(); } + + @Override + protected void setCustomerId(TenantId tenantId, CustomerId customerId, Device device, DeviceUpdateMsg deviceUpdateMsg, boolean isEdgeVersionDeprecated) { + CustomerId customerUUID = isEdgeVersionDeprecated + ? safeGetCustomerId(deviceUpdateMsg.getCustomerIdMSB(), deviceUpdateMsg.getCustomerIdLSB()) + : device.getCustomerId() != null ? device.getCustomerId() : customerId; + device.setCustomerId(customerUUID); + } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceProfileEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceProfileEdgeProcessor.java index 523422bec7..419b7f90c8 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceProfileEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceProfileEdgeProcessor.java @@ -143,10 +143,10 @@ public class DeviceProfileEdgeProcessor extends BaseDeviceProfileProcessor { } @Override - protected void setDefaultDashboardId(TenantId tenantId, DeviceProfile deviceProfile, DeviceProfileUpdateMsg deviceProfileUpdateMsg, boolean isEdgeVersionDeprecated) { + protected void setDefaultDashboardId(TenantId tenantId, DashboardId dashboardId, DeviceProfile deviceProfile, DeviceProfileUpdateMsg deviceProfileUpdateMsg, boolean isEdgeVersionDeprecated) { UUID defaultDashboardUUID = isEdgeVersionDeprecated ? safeGetUUID(deviceProfileUpdateMsg.getDefaultDashboardIdMSB(), deviceProfileUpdateMsg.getDefaultDashboardIdLSB()) - : deviceProfile.getDefaultDashboardId() != null ? deviceProfile.getDefaultDashboardId().getId() : null; + : deviceProfile.getDefaultDashboardId() != null ? deviceProfile.getDefaultDashboardId().getId() : (dashboardId != null ? dashboardId.getId() : null); deviceProfile.setDefaultDashboardId(defaultDashboardUUID != null ? new DashboardId(defaultDashboardUUID) : null); } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/BaseEntityViewProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/BaseEntityViewProcessor.java index 3d13b7a044..236c00a861 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/BaseEntityViewProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/BaseEntityViewProcessor.java @@ -40,7 +40,7 @@ public abstract class BaseEntityViewProcessor extends BaseEdgeProcessor { boolean entityViewNameUpdated = false; EntityView entityView = isEdgeProtoDeprecated ? createEntityView(tenantId, entityViewId, entityViewUpdateMsg) - : JacksonUtil.fromEdgeString(entityViewUpdateMsg.getEntity(), EntityView.class); + : JacksonUtil.fromStringIgnoreUnknownProperties(entityViewUpdateMsg.getEntity(), EntityView.class); if (entityView == null) { throw new RuntimeException("[{" + tenantId + "}] entityViewUpdateMsg {" + entityViewUpdateMsg + "} cannot be converted to entity view"); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessor.java index 84065d1c8a..8d75be10ee 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessor.java @@ -46,12 +46,12 @@ import java.util.Set; @TbCoreComponent public class RelationEdgeProcessor extends BaseRelationProcessor { - public ListenableFuture processRelationMsgFromEdge(TenantId tenantId, Edge edge, RelationUpdateMsg relationUpdateMsg) { + public ListenableFuture processRelationMsgFromEdge(TenantId tenantId, Edge edge, RelationUpdateMsg relationUpdateMsg, EdgeVersion edgeVersion) { log.trace("[{}] executing processRelationMsgFromEdge [{}] from edge [{}]", tenantId, relationUpdateMsg, edge.getId()); try { edgeSynchronizationManager.getEdgeId().set(edge.getId()); - return processRelationMsg(tenantId, relationUpdateMsg); + return processRelationMsg(tenantId, relationUpdateMsg, edgeVersion); } finally { edgeSynchronizationManager.getEdgeId().remove(); } diff --git a/application/src/test/java/org/thingsboard/server/edge/AssetProfileEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/AssetProfileEdgeTest.java index 600895c230..7c93a4440d 100644 --- a/application/src/test/java/org/thingsboard/server/edge/AssetProfileEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/AssetProfileEdgeTest.java @@ -53,7 +53,7 @@ public class AssetProfileEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof AssetProfileUpdateMsg); AssetProfileUpdateMsg assetProfileUpdateMsg = (AssetProfileUpdateMsg) latestMessage; - AssetProfile assetProfileMsg = JacksonUtil.fromEdgeString(assetProfileUpdateMsg.getEntity(), AssetProfile.class); + AssetProfile assetProfileMsg = JacksonUtil.fromStringIgnoreUnknownProperties(assetProfileUpdateMsg.getEntity(), AssetProfile.class); Assert.assertNotNull(assetProfileMsg); Assert.assertEquals(assetProfile, assetProfileMsg); Assert.assertEquals("Building", assetProfileMsg.getName()); @@ -68,7 +68,7 @@ public class AssetProfileEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof AssetProfileUpdateMsg); assetProfileUpdateMsg = (AssetProfileUpdateMsg) latestMessage; - assetProfileMsg = JacksonUtil.fromEdgeString(assetProfileUpdateMsg.getEntity(), AssetProfile.class); + assetProfileMsg = JacksonUtil.fromStringIgnoreUnknownProperties(assetProfileUpdateMsg.getEntity(), AssetProfile.class); Assert.assertNotNull(assetProfileMsg); Assert.assertEquals("IMAGE", assetProfileMsg.getImage()); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, assetProfileUpdateMsg.getMsgType()); @@ -94,7 +94,7 @@ public class AssetProfileEdgeTest extends AbstractEdgeTest { DashboardId dashboardId = createDashboardAndAssignToEdge("Asset Profile Dashboard"); AssetProfile assetProfileOnEdge = buildAssetProfileForUplinkMsg("Asset Profile On Edge"); - assetProfileOnEdge.setDefaultEdgeRuleChainId(edgeRuleChainId); + assetProfileOnEdge.setDefaultRuleChainId(edgeRuleChainId); assetProfileOnEdge.setDefaultDashboardId(dashboardId); UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); @@ -170,7 +170,7 @@ public class AssetProfileEdgeTest extends AbstractEdgeTest { Optional assetProfileUpdateMsgOpt = edgeImitator.findMessageByType(AssetProfileUpdateMsg.class); Assert.assertTrue(assetProfileUpdateMsgOpt.isPresent()); AssetProfileUpdateMsg latestAssetProfileUpdateMsg = assetProfileUpdateMsgOpt.get(); - AssetProfile assetProfileMsg = JacksonUtil.fromEdgeString(latestAssetProfileUpdateMsg.getEntity(), AssetProfile.class); + AssetProfile assetProfileMsg = JacksonUtil.fromStringIgnoreUnknownProperties(latestAssetProfileUpdateMsg.getEntity(), AssetProfile.class); Assert.assertNotNull(assetProfileMsg); Assert.assertNotEquals(assetProfileOnCloudName, assetProfileMsg.getName()); diff --git a/application/src/test/java/org/thingsboard/server/edge/CustomerEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/CustomerEdgeTest.java index 9f7e1b5641..125aa41e18 100644 --- a/application/src/test/java/org/thingsboard/server/edge/CustomerEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/CustomerEdgeTest.java @@ -58,7 +58,7 @@ public class CustomerEdgeTest extends AbstractEdgeTest { Optional customerUpdateOpt = edgeImitator.findMessageByType(CustomerUpdateMsg.class); Assert.assertTrue(customerUpdateOpt.isPresent()); CustomerUpdateMsg customerUpdateMsg = customerUpdateOpt.get(); - Customer customerMsg = JacksonUtil.fromEdgeString(customerUpdateMsg.getEntity(), Customer.class); + Customer customerMsg = JacksonUtil.fromStringIgnoreUnknownProperties(customerUpdateMsg.getEntity(), Customer.class); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, customerUpdateMsg.getMsgType()); Assert.assertEquals(savedCustomer, customerMsg); testAutoGeneratedCodeByProtobuf(customerUpdateMsg); @@ -71,7 +71,7 @@ public class CustomerEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof CustomerUpdateMsg); customerUpdateMsg = (CustomerUpdateMsg) latestMessage; - customerMsg = JacksonUtil.fromEdgeString(customerUpdateMsg.getEntity(), Customer.class); + customerMsg = JacksonUtil.fromStringIgnoreUnknownProperties(customerUpdateMsg.getEntity(), Customer.class); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, customerUpdateMsg.getMsgType()); Assert.assertEquals(savedCustomer, customerMsg); diff --git a/application/src/test/java/org/thingsboard/server/edge/DashboardEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/DashboardEdgeTest.java index 8db2e0390f..fc342bf682 100644 --- a/application/src/test/java/org/thingsboard/server/edge/DashboardEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/DashboardEdgeTest.java @@ -62,7 +62,7 @@ public class DashboardEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof DashboardUpdateMsg); DashboardUpdateMsg dashboardUpdateMsg = (DashboardUpdateMsg) latestMessage; - Dashboard dashboardMsg = JacksonUtil.fromEdgeString(dashboardUpdateMsg.getEntity(), Dashboard.class); + Dashboard dashboardMsg = JacksonUtil.fromStringIgnoreUnknownProperties(dashboardUpdateMsg.getEntity(), Dashboard.class); Assert.assertNotNull(dashboardMsg); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, dashboardUpdateMsg.getMsgType()); Assert.assertEquals(savedDashboard, dashboardMsg); @@ -78,7 +78,7 @@ public class DashboardEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof DashboardUpdateMsg); dashboardUpdateMsg = (DashboardUpdateMsg) latestMessage; - dashboardMsg = JacksonUtil.fromEdgeString(dashboardUpdateMsg.getEntity(), Dashboard.class); + dashboardMsg = JacksonUtil.fromStringIgnoreUnknownProperties(dashboardUpdateMsg.getEntity(), Dashboard.class); Assert.assertNotNull(dashboardMsg); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, dashboardUpdateMsg.getMsgType()); Assert.assertEquals(savedDashboard.getTitle(), dashboardMsg.getTitle()); @@ -112,7 +112,7 @@ public class DashboardEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof DashboardUpdateMsg); dashboardUpdateMsg = (DashboardUpdateMsg) latestMessage; - dashboardMsg = JacksonUtil.fromEdgeString(dashboardUpdateMsg.getEntity(), Dashboard.class); + dashboardMsg = JacksonUtil.fromStringIgnoreUnknownProperties(dashboardUpdateMsg.getEntity(), Dashboard.class); Assert.assertNotNull(dashboardMsg); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, dashboardUpdateMsg.getMsgType()); Assert.assertEquals(savedDashboard.getUuidId().getMostSignificantBits(), dashboardUpdateMsg.getIdMSB()); @@ -135,7 +135,7 @@ public class DashboardEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof DashboardUpdateMsg); dashboardUpdateMsg = (DashboardUpdateMsg) latestMessage; - dashboardMsg = JacksonUtil.fromEdgeString(dashboardUpdateMsg.getEntity(), Dashboard.class); + dashboardMsg = JacksonUtil.fromStringIgnoreUnknownProperties(dashboardUpdateMsg.getEntity(), Dashboard.class); Assert.assertNotNull(dashboardMsg); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, dashboardUpdateMsg.getMsgType()); Assert.assertNotNull(dashboardMsg.getAssignedCustomers()); @@ -150,7 +150,7 @@ public class DashboardEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof DashboardUpdateMsg); dashboardUpdateMsg = (DashboardUpdateMsg) latestMessage; - dashboardMsg = JacksonUtil.fromEdgeString(dashboardUpdateMsg.getEntity(), Dashboard.class); + dashboardMsg = JacksonUtil.fromStringIgnoreUnknownProperties(dashboardUpdateMsg.getEntity(), Dashboard.class); Assert.assertNotNull(dashboardMsg); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, dashboardUpdateMsg.getMsgType()); Assert.assertNotNull(dashboardMsg.getAssignedCustomers()); 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 429bb2e968..9c3f0a9bb7 100644 --- a/application/src/test/java/org/thingsboard/server/edge/DeviceEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/DeviceEdgeTest.java @@ -782,7 +782,7 @@ public class DeviceEdgeTest extends AbstractEdgeTest { private DeviceCredentials buildDeviceCredentialsForUplinkMsg(DeviceId deviceId) { DeviceCredentials deviceCredentials = new DeviceCredentials(); deviceCredentials.setDeviceId(deviceId); - deviceCredentials.setCredentialsId("NEW_TOKEN"); + deviceCredentials.setCredentialsValue("NEW_TOKEN"); deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN); return deviceCredentials; } diff --git a/application/src/test/java/org/thingsboard/server/edge/DeviceProfileEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/DeviceProfileEdgeTest.java index 90d540d17f..325872d919 100644 --- a/application/src/test/java/org/thingsboard/server/edge/DeviceProfileEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/DeviceProfileEdgeTest.java @@ -81,7 +81,7 @@ public class DeviceProfileEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof DeviceProfileUpdateMsg); DeviceProfileUpdateMsg deviceProfileUpdateMsg = (DeviceProfileUpdateMsg) latestMessage; - DeviceProfile deviceProfileMsg = JacksonUtil.fromEdgeString(deviceProfileUpdateMsg.getEntity(), DeviceProfile.class); + DeviceProfile deviceProfileMsg = JacksonUtil.fromStringIgnoreUnknownProperties(deviceProfileUpdateMsg.getEntity(), DeviceProfile.class); Assert.assertNotNull(deviceProfileMsg); Assert.assertEquals(deviceProfile, deviceProfileMsg); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, deviceProfileUpdateMsg.getMsgType()); @@ -106,7 +106,7 @@ public class DeviceProfileEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof DeviceProfileUpdateMsg); deviceProfileUpdateMsg = (DeviceProfileUpdateMsg) latestMessage; - deviceProfileMsg = JacksonUtil.fromEdgeString(deviceProfileUpdateMsg.getEntity(), DeviceProfile.class); + deviceProfileMsg = JacksonUtil.fromStringIgnoreUnknownProperties(deviceProfileUpdateMsg.getEntity(), DeviceProfile.class); Assert.assertNotNull(deviceProfileMsg); Assert.assertEquals(deviceProfile, deviceProfileMsg); @@ -133,7 +133,7 @@ public class DeviceProfileEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof DeviceProfileUpdateMsg); DeviceProfileUpdateMsg deviceProfileUpdateMsg = (DeviceProfileUpdateMsg) latestMessage; - DeviceProfile deviceProfileMsg = JacksonUtil.fromEdgeString(deviceProfileUpdateMsg.getEntity(), DeviceProfile.class); + DeviceProfile deviceProfileMsg = JacksonUtil.fromStringIgnoreUnknownProperties(deviceProfileUpdateMsg.getEntity(), DeviceProfile.class); Assert.assertNotNull(deviceProfileMsg); Assert.assertEquals(deviceProfile, deviceProfileMsg); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, deviceProfileUpdateMsg.getMsgType()); @@ -171,7 +171,7 @@ public class DeviceProfileEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof DeviceProfileUpdateMsg); DeviceProfileUpdateMsg deviceProfileUpdateMsg = (DeviceProfileUpdateMsg) latestMessage; - DeviceProfile deviceProfileMsg = JacksonUtil.fromEdgeString(deviceProfileUpdateMsg.getEntity(), DeviceProfile.class); + DeviceProfile deviceProfileMsg = JacksonUtil.fromStringIgnoreUnknownProperties(deviceProfileUpdateMsg.getEntity(), DeviceProfile.class); Assert.assertNotNull(deviceProfileMsg); Assert.assertEquals(deviceProfile, deviceProfileMsg); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, deviceProfileUpdateMsg.getMsgType()); @@ -220,7 +220,7 @@ public class DeviceProfileEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof DeviceProfileUpdateMsg); DeviceProfileUpdateMsg deviceProfileUpdateMsg = (DeviceProfileUpdateMsg) latestMessage; - DeviceProfile deviceProfileMsg = JacksonUtil.fromEdgeString(deviceProfileUpdateMsg.getEntity(), DeviceProfile.class); + DeviceProfile deviceProfileMsg = JacksonUtil.fromStringIgnoreUnknownProperties(deviceProfileUpdateMsg.getEntity(), DeviceProfile.class); Assert.assertNotNull(deviceProfileMsg); Assert.assertEquals(deviceProfile, deviceProfileMsg); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, deviceProfileUpdateMsg.getMsgType()); @@ -427,7 +427,7 @@ public class DeviceProfileEdgeTest extends AbstractEdgeTest { Optional deviceProfileUpdateMsgOpt = edgeImitator.findMessageByType(DeviceProfileUpdateMsg.class); Assert.assertTrue(deviceProfileUpdateMsgOpt.isPresent()); DeviceProfileUpdateMsg latestDeviceProfileUpdateMsg = deviceProfileUpdateMsgOpt.get(); - deviceProfileMsg = JacksonUtil.fromEdgeString(latestDeviceProfileUpdateMsg.getEntity(), DeviceProfile.class); + deviceProfileMsg = JacksonUtil.fromStringIgnoreUnknownProperties(latestDeviceProfileUpdateMsg.getEntity(), DeviceProfile.class); Assert.assertNotNull(deviceProfileMsg); Assert.assertNotEquals(deviceProfileOnCloudName, deviceProfileMsg.getName()); diff --git a/application/src/test/java/org/thingsboard/server/edge/EdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/EdgeTest.java index b8e784aad5..7330e2f5fc 100644 --- a/application/src/test/java/org/thingsboard/server/edge/EdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/EdgeTest.java @@ -55,7 +55,7 @@ public class EdgeTest extends AbstractEdgeTest { Optional customerUpdateOpt = edgeImitator.findMessageByType(CustomerUpdateMsg.class); Assert.assertTrue(customerUpdateOpt.isPresent()); CustomerUpdateMsg customerUpdateMsg = customerUpdateOpt.get(); - Customer customerMsg = JacksonUtil.fromEdgeString(customerUpdateMsg.getEntity(), Customer.class); + Customer customerMsg = JacksonUtil.fromStringIgnoreUnknownProperties(customerUpdateMsg.getEntity(), Customer.class); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, customerUpdateMsg.getMsgType()); Assert.assertEquals(savedCustomer, customerMsg); diff --git a/application/src/test/java/org/thingsboard/server/edge/EntityViewEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/EntityViewEdgeTest.java index 7ccda39192..903f49f756 100644 --- a/application/src/test/java/org/thingsboard/server/edge/EntityViewEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/EntityViewEdgeTest.java @@ -68,7 +68,7 @@ public class EntityViewEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof EntityViewUpdateMsg); EntityViewUpdateMsg entityViewUpdateMsg = (EntityViewUpdateMsg) latestMessage; - EntityView entityView = JacksonUtil.fromEdgeString(entityViewUpdateMsg.getEntity(), EntityView.class); + EntityView entityView = JacksonUtil.fromStringIgnoreUnknownProperties(entityViewUpdateMsg.getEntity(), EntityView.class); Assert.assertNotNull(entityView); Assert.assertEquals(savedEntityView, entityView); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, entityViewUpdateMsg.getMsgType()); @@ -133,7 +133,7 @@ public class EntityViewEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof EntityViewUpdateMsg); entityViewUpdateMsg = (EntityViewUpdateMsg) latestMessage; - EntityView entityViewMsg = JacksonUtil.fromEdgeString(entityViewUpdateMsg.getEntity(), EntityView.class); + EntityView entityViewMsg = JacksonUtil.fromStringIgnoreUnknownProperties(entityViewUpdateMsg.getEntity(), EntityView.class); Assert.assertNotNull(entityViewMsg); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, entityViewUpdateMsg.getMsgType()); Assert.assertEquals(savedCustomer.getId(), entityViewMsg.getCustomerId()); @@ -145,7 +145,7 @@ public class EntityViewEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof EntityViewUpdateMsg); entityViewUpdateMsg = (EntityViewUpdateMsg) latestMessage; - entityViewMsg = JacksonUtil.fromEdgeString(entityViewUpdateMsg.getEntity(), EntityView.class); + entityViewMsg = JacksonUtil.fromStringIgnoreUnknownProperties(entityViewUpdateMsg.getEntity(), EntityView.class); Assert.assertNotNull(entityViewMsg); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, entityViewUpdateMsg.getMsgType()); Assert.assertEquals(new CustomerId(EntityId.NULL_UUID), entityViewMsg.getCustomerId()); @@ -227,7 +227,7 @@ public class EntityViewEdgeTest extends AbstractEdgeTest { Optional entityViewUpdateMsgOpt = edgeImitator.findMessageByType(EntityViewUpdateMsg.class); Assert.assertTrue(entityViewUpdateMsgOpt.isPresent()); EntityViewUpdateMsg latestEntityViewUpdateMsg = entityViewUpdateMsgOpt.get(); - entityViewMsg = JacksonUtil.fromEdgeString(latestEntityViewUpdateMsg.getEntity(), EntityView.class); + entityViewMsg = JacksonUtil.fromStringIgnoreUnknownProperties(latestEntityViewUpdateMsg.getEntity(), EntityView.class); Assert.assertNotNull(entityViewMsg); Assert.assertNotEquals(entityViewOnCloudName, entityViewMsg.getName()); Assert.assertNotEquals(entityViewOnCloud.getId(), entityViewMsg.getId()); @@ -268,7 +268,7 @@ public class EntityViewEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof EntityViewUpdateMsg); EntityViewUpdateMsg entityViewUpdateMsg = (EntityViewUpdateMsg) latestMessage; - EntityView entityViewMsg = JacksonUtil.fromEdgeString(entityViewUpdateMsg.getEntity(), EntityView.class); + EntityView entityViewMsg = JacksonUtil.fromStringIgnoreUnknownProperties(entityViewUpdateMsg.getEntity(), EntityView.class); Assert.assertNotNull(entityViewMsg); Assert.assertEquals(entityView, entityViewMsg); Assert.assertEquals(device.getId(), entityViewMsg.getEntityId()); diff --git a/application/src/test/java/org/thingsboard/server/edge/OtaPackageEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/OtaPackageEdgeTest.java index 50e0be2cc1..0ff72b94ec 100644 --- a/application/src/test/java/org/thingsboard/server/edge/OtaPackageEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/OtaPackageEdgeTest.java @@ -58,7 +58,7 @@ public class OtaPackageEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof OtaPackageUpdateMsg); OtaPackageUpdateMsg otaPackageUpdateMsg = (OtaPackageUpdateMsg) latestMessage; - OtaPackage otaPackage = JacksonUtil.fromEdgeString(otaPackageUpdateMsg.getEntity(), OtaPackage.class); + OtaPackage otaPackage = JacksonUtil.fromStringIgnoreUnknownProperties(otaPackageUpdateMsg.getEntity(), OtaPackage.class); Assert.assertNotNull(otaPackage); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, otaPackageUpdateMsg.getMsgType()); Assert.assertEquals(savedFirmwareInfo.getId(), otaPackage.getId()); @@ -112,7 +112,7 @@ public class OtaPackageEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof OtaPackageUpdateMsg); OtaPackageUpdateMsg otaPackageUpdateMsg = (OtaPackageUpdateMsg) latestMessage; - OtaPackage otaPackage = JacksonUtil.fromEdgeString(otaPackageUpdateMsg.getEntity(), OtaPackage.class); + OtaPackage otaPackage = JacksonUtil.fromStringIgnoreUnknownProperties(otaPackageUpdateMsg.getEntity(), OtaPackage.class); Assert.assertNotNull(otaPackage); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, otaPackageUpdateMsg.getMsgType()); Assert.assertEquals(savedFirmwareInfo.getId(), otaPackage.getId()); diff --git a/application/src/test/java/org/thingsboard/server/edge/QueueEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/QueueEdgeTest.java index faf8f6826b..b80a7de112 100644 --- a/application/src/test/java/org/thingsboard/server/edge/QueueEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/QueueEdgeTest.java @@ -64,7 +64,7 @@ public class QueueEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof QueueUpdateMsg); QueueUpdateMsg queueUpdateMsg = (QueueUpdateMsg) latestMessage; - Queue queueMsg = JacksonUtil.fromEdgeString(queueUpdateMsg.getEntity(), Queue.class); + Queue queueMsg = JacksonUtil.fromStringIgnoreUnknownProperties(queueUpdateMsg.getEntity(), Queue.class); Assert.assertNotNull(queueMsg); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, queueUpdateMsg.getMsgType()); Assert.assertEquals(savedQueue, queueMsg); @@ -89,7 +89,7 @@ public class QueueEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof QueueUpdateMsg); queueUpdateMsg = (QueueUpdateMsg) latestMessage; - queueMsg = JacksonUtil.fromEdgeString(queueUpdateMsg.getEntity(), Queue.class); + queueMsg = JacksonUtil.fromStringIgnoreUnknownProperties(queueUpdateMsg.getEntity(), Queue.class); Assert.assertNotNull(queueMsg); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, queueUpdateMsg.getMsgType()); Assert.assertEquals(50, queueMsg.getPollInterval()); diff --git a/application/src/test/java/org/thingsboard/server/edge/RelationEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/RelationEdgeTest.java index a26dd9512a..f9cb351aa0 100644 --- a/application/src/test/java/org/thingsboard/server/edge/RelationEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/RelationEdgeTest.java @@ -53,7 +53,7 @@ public class RelationEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof RelationUpdateMsg); RelationUpdateMsg relationUpdateMsg = (RelationUpdateMsg) latestMessage; - EntityRelation entityRelation = JacksonUtil.fromEdgeString(relationUpdateMsg.getEntity(), EntityRelation.class); + EntityRelation entityRelation = JacksonUtil.fromStringIgnoreUnknownProperties(relationUpdateMsg.getEntity(), EntityRelation.class); Assert.assertNotNull(entityRelation); Assert.assertEquals(relation, entityRelation); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, relationUpdateMsg.getMsgType()); @@ -72,7 +72,7 @@ public class RelationEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof RelationUpdateMsg); relationUpdateMsg = (RelationUpdateMsg) latestMessage; - entityRelation = JacksonUtil.fromEdgeString(relationUpdateMsg.getEntity(), EntityRelation.class); + entityRelation = JacksonUtil.fromStringIgnoreUnknownProperties(relationUpdateMsg.getEntity(), EntityRelation.class); Assert.assertNotNull(entityRelation); Assert.assertEquals(relation, entityRelation); Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, relationUpdateMsg.getMsgType()); @@ -141,7 +141,7 @@ public class RelationEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof RelationUpdateMsg); RelationUpdateMsg relationUpdateMsg = (RelationUpdateMsg) latestMessage; - EntityRelation entityRelation = JacksonUtil.fromEdgeString(relationUpdateMsg.getEntity(), EntityRelation.class); + EntityRelation entityRelation = JacksonUtil.fromStringIgnoreUnknownProperties(relationUpdateMsg.getEntity(), EntityRelation.class); Assert.assertNotNull(entityRelation); Assert.assertEquals(relation, entityRelation); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, relationUpdateMsg.getMsgType()); diff --git a/application/src/test/java/org/thingsboard/server/edge/RuleChainEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/RuleChainEdgeTest.java index 0322bd849e..ff87a64bfe 100644 --- a/application/src/test/java/org/thingsboard/server/edge/RuleChainEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/RuleChainEdgeTest.java @@ -61,7 +61,7 @@ public class RuleChainEdgeTest extends AbstractEdgeTest { Optional ruleChainUpdateMsgOpt = edgeImitator.findMessageByType(RuleChainUpdateMsg.class); Assert.assertTrue(ruleChainUpdateMsgOpt.isPresent()); RuleChainUpdateMsg ruleChainUpdateMsg = ruleChainUpdateMsgOpt.get(); - RuleChain ruleChainMsg = JacksonUtil.fromEdgeString(ruleChainUpdateMsg.getEntity(), RuleChain.class); + RuleChain ruleChainMsg = JacksonUtil.fromStringIgnoreUnknownProperties(ruleChainUpdateMsg.getEntity(), RuleChain.class); Assert.assertNotNull(ruleChainMsg); Assert.assertTrue(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE.equals(ruleChainUpdateMsg.getMsgType()) || UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE.equals(ruleChainUpdateMsg.getMsgType())); @@ -111,7 +111,7 @@ public class RuleChainEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof RuleChainMetadataUpdateMsg); RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = (RuleChainMetadataUpdateMsg) latestMessage; - RuleChainMetaData ruleChainMetadataMsg = JacksonUtil.fromEdgeString(ruleChainMetadataUpdateMsg.getEntity(), RuleChainMetaData.class); + RuleChainMetaData ruleChainMetadataMsg = JacksonUtil.fromStringIgnoreUnknownProperties(ruleChainMetadataUpdateMsg.getEntity(), RuleChainMetaData.class); Assert.assertNotNull(ruleChainMetadataMsg); Assert.assertEquals(edgeRootRuleChainId, ruleChainMetadataMsg.getRuleChainId()); @@ -137,7 +137,7 @@ public class RuleChainEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof RuleChainMetadataUpdateMsg); RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = (RuleChainMetadataUpdateMsg) latestMessage; - RuleChainMetaData ruleChainMetadataMsg = JacksonUtil.fromEdgeString(ruleChainMetadataUpdateMsg.getEntity(), RuleChainMetaData.class); + RuleChainMetaData ruleChainMetadataMsg = JacksonUtil.fromStringIgnoreUnknownProperties(ruleChainMetadataUpdateMsg.getEntity(), RuleChainMetaData.class); Assert.assertNotNull(ruleChainMetadataMsg); Assert.assertEquals(ruleChainId, ruleChainMetadataMsg.getRuleChainId()); @@ -207,7 +207,7 @@ public class RuleChainEdgeTest extends AbstractEdgeTest { Optional ruleChainUpdateMsgOpt = edgeImitator.findMessageByType(RuleChainUpdateMsg.class); Assert.assertTrue(ruleChainUpdateMsgOpt.isPresent()); RuleChainUpdateMsg ruleChainUpdateMsg = ruleChainUpdateMsgOpt.get(); - RuleChain ruleChainMsg = JacksonUtil.fromEdgeString(ruleChainUpdateMsg.getEntity(), RuleChain.class); + RuleChain ruleChainMsg = JacksonUtil.fromStringIgnoreUnknownProperties(ruleChainUpdateMsg.getEntity(), RuleChain.class); Assert.assertNotNull(ruleChainMsg); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, ruleChainUpdateMsg.getMsgType()); Assert.assertTrue(ruleChainMsg.isRoot()); diff --git a/application/src/test/java/org/thingsboard/server/edge/TenantEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/TenantEdgeTest.java index 97ac2eacc5..4c61b0d91d 100644 --- a/application/src/test/java/org/thingsboard/server/edge/TenantEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/TenantEdgeTest.java @@ -48,10 +48,10 @@ public class TenantEdgeTest extends AbstractEdgeTest { Optional tenantProfileUpdateMsgOpt = edgeImitator.findMessageByType(TenantProfileUpdateMsg.class); Assert.assertTrue(tenantProfileUpdateMsgOpt.isPresent()); TenantProfileUpdateMsg tenantProfileUpdateMsg = tenantProfileUpdateMsgOpt.get(); - Tenant tenantMsg = JacksonUtil.fromEdgeString(tenantUpdateMsg.getEntity(), Tenant.class); + Tenant tenantMsg = JacksonUtil.fromStringIgnoreUnknownProperties(tenantUpdateMsg.getEntity(), Tenant.class); Assert.assertNotNull(tenantMsg); Assert.assertEquals(savedTenant, tenantMsg); - TenantProfile tenantProfileMsg = JacksonUtil.fromEdgeString(tenantProfileUpdateMsg.getEntity(), TenantProfile.class); + TenantProfile tenantProfileMsg = JacksonUtil.fromStringIgnoreUnknownProperties(tenantProfileUpdateMsg.getEntity(), TenantProfile.class); Assert.assertNotNull(tenantProfileMsg); Assert.assertEquals(tenantMsg.getTenantProfileId(), tenantProfileMsg.getId()); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, tenantUpdateMsg.getMsgType()); @@ -66,12 +66,12 @@ public class TenantEdgeTest extends AbstractEdgeTest { tenantUpdateMsgOpt = edgeImitator.findMessageByType(TenantUpdateMsg.class); Assert.assertTrue(tenantUpdateMsgOpt.isPresent()); tenantUpdateMsg = tenantUpdateMsgOpt.get(); - tenantMsg = JacksonUtil.fromEdgeString(tenantUpdateMsg.getEntity(), Tenant.class); + tenantMsg = JacksonUtil.fromStringIgnoreUnknownProperties(tenantUpdateMsg.getEntity(), Tenant.class); Assert.assertNotNull(tenantMsg); tenantProfileUpdateMsgOpt = edgeImitator.findMessageByType(TenantProfileUpdateMsg.class); Assert.assertTrue(tenantProfileUpdateMsgOpt.isPresent()); tenantProfileUpdateMsg = tenantProfileUpdateMsgOpt.get(); - tenantProfileMsg = JacksonUtil.fromEdgeString(tenantProfileUpdateMsg.getEntity(), TenantProfile.class); + tenantProfileMsg = JacksonUtil.fromStringIgnoreUnknownProperties(tenantProfileUpdateMsg.getEntity(), TenantProfile.class); Assert.assertNotNull(tenantProfileMsg); // tenant update Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, tenantUpdateMsg.getMsgType()); diff --git a/application/src/test/java/org/thingsboard/server/edge/TenantProfileEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/TenantProfileEdgeTest.java index 695c3a1dea..7320a73a08 100644 --- a/application/src/test/java/org/thingsboard/server/edge/TenantProfileEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/TenantProfileEdgeTest.java @@ -56,7 +56,7 @@ public class TenantProfileEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof TenantProfileUpdateMsg); TenantProfileUpdateMsg tenantProfileUpdateMsg = (TenantProfileUpdateMsg) latestMessage; - TenantProfile tenantProfileMsg = JacksonUtil.fromEdgeString(tenantProfileUpdateMsg.getEntity(), TenantProfile.class); + TenantProfile tenantProfileMsg = JacksonUtil.fromStringIgnoreUnknownProperties(tenantProfileUpdateMsg.getEntity(), TenantProfile.class); Assert.assertNotNull(tenantProfileMsg); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, tenantProfileUpdateMsg.getMsgType()); Assert.assertEquals(edgeTenantProfile, tenantProfileMsg); diff --git a/application/src/test/java/org/thingsboard/server/edge/UserEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/UserEdgeTest.java index 2254f8d051..8dd5b27ad5 100644 --- a/application/src/test/java/org/thingsboard/server/edge/UserEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/UserEdgeTest.java @@ -59,7 +59,7 @@ public class UserEdgeTest extends AbstractEdgeTest { Optional userUpdateMsgOpt = edgeImitator.findMessageByType(UserUpdateMsg.class); Assert.assertTrue(userUpdateMsgOpt.isPresent()); UserUpdateMsg userUpdateMsg = userUpdateMsgOpt.get(); - User userMsg = JacksonUtil.fromEdgeString(userUpdateMsg.getEntity(), User.class); + User userMsg = JacksonUtil.fromStringIgnoreUnknownProperties(userUpdateMsg.getEntity(), User.class); Assert.assertNotNull(userMsg); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, userUpdateMsg.getMsgType()); Assert.assertEquals(savedTenantAdmin.getId(), userMsg.getId()); @@ -78,7 +78,7 @@ public class UserEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof UserUpdateMsg); userUpdateMsg = (UserUpdateMsg) latestMessage; - userMsg = JacksonUtil.fromEdgeString(userUpdateMsg.getEntity(), User.class); + userMsg = JacksonUtil.fromStringIgnoreUnknownProperties(userUpdateMsg.getEntity(), User.class); Assert.assertNotNull(userMsg); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, userUpdateMsg.getMsgType()); Assert.assertEquals(savedTenantAdmin.getLastName(), userMsg.getLastName()); @@ -95,7 +95,7 @@ public class UserEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof UserCredentialsUpdateMsg); UserCredentialsUpdateMsg userCredentialsUpdateMsg = (UserCredentialsUpdateMsg) latestMessage; - UserCredentials userCredentialsMsg = JacksonUtil.fromEdgeString(userCredentialsUpdateMsg.getEntity(), UserCredentials.class); + UserCredentials userCredentialsMsg = JacksonUtil.fromStringIgnoreUnknownProperties(userCredentialsUpdateMsg.getEntity(), UserCredentials.class); Assert.assertNotNull(userCredentialsMsg); Assert.assertEquals(savedTenantAdmin.getId(), userCredentialsMsg.getUserId()); Assert.assertTrue(passwordEncoder.matches(changePasswordRequest.getNewPassword(), userCredentialsMsg.getPassword())); @@ -144,7 +144,7 @@ public class UserEdgeTest extends AbstractEdgeTest { Optional userUpdateMsgOpt = edgeImitator.findMessageByType(UserUpdateMsg.class); Assert.assertTrue(userUpdateMsgOpt.isPresent()); UserUpdateMsg userUpdateMsg = userUpdateMsgOpt.get(); - User userMsg = JacksonUtil.fromEdgeString(userUpdateMsg.getEntity(), User.class); + User userMsg = JacksonUtil.fromStringIgnoreUnknownProperties(userUpdateMsg.getEntity(), User.class); Assert.assertNotNull(userMsg); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, userUpdateMsg.getMsgType()); Assert.assertEquals(savedCustomerUser.getId(), userMsg.getId()); @@ -162,7 +162,7 @@ public class UserEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof UserUpdateMsg); userUpdateMsg = (UserUpdateMsg) latestMessage; - userMsg = JacksonUtil.fromEdgeString(userUpdateMsg.getEntity(), User.class); + userMsg = JacksonUtil.fromStringIgnoreUnknownProperties(userUpdateMsg.getEntity(), User.class); Assert.assertNotNull(userMsg); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, userUpdateMsg.getMsgType()); Assert.assertEquals(savedCustomerUser.getLastName(), userMsg.getLastName()); @@ -179,7 +179,7 @@ public class UserEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof UserCredentialsUpdateMsg); UserCredentialsUpdateMsg userCredentialsUpdateMsg = (UserCredentialsUpdateMsg) latestMessage; - UserCredentials userCredentialsMsg = JacksonUtil.fromEdgeString(userCredentialsUpdateMsg.getEntity(), UserCredentials.class); + UserCredentials userCredentialsMsg = JacksonUtil.fromStringIgnoreUnknownProperties(userCredentialsUpdateMsg.getEntity(), UserCredentials.class); Assert.assertNotNull(userCredentialsMsg); Assert.assertEquals(savedCustomerUser.getId(), userCredentialsMsg.getUserId()); Assert.assertTrue(passwordEncoder.matches(changePasswordRequest.getNewPassword(), userCredentialsMsg.getPassword())); @@ -220,7 +220,7 @@ public class UserEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof UserCredentialsUpdateMsg); UserCredentialsUpdateMsg userCredentialsUpdateMsg = (UserCredentialsUpdateMsg) latestMessage; - UserCredentials userCredentialsMsg = JacksonUtil.fromEdgeString(userCredentialsUpdateMsg.getEntity(), UserCredentials.class); + UserCredentials userCredentialsMsg = JacksonUtil.fromStringIgnoreUnknownProperties(userCredentialsUpdateMsg.getEntity(), UserCredentials.class); Assert.assertNotNull(userCredentialsMsg); Assert.assertEquals(tenantAdminUserId, userCredentialsMsg.getUserId()); } @@ -245,7 +245,7 @@ public class UserEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof UserCredentialsUpdateMsg); UserCredentialsUpdateMsg userCredentialsUpdateMsg = (UserCredentialsUpdateMsg) latestMessage; - UserCredentials userCredentialsMsg = JacksonUtil.fromEdgeString(userCredentialsUpdateMsg.getEntity(), UserCredentials.class); + UserCredentials userCredentialsMsg = JacksonUtil.fromStringIgnoreUnknownProperties(userCredentialsUpdateMsg.getEntity(), UserCredentials.class); Assert.assertNotNull(userCredentialsMsg); Assert.assertEquals(tenantAdminUserId, userCredentialsMsg.getUserId()); diff --git a/application/src/test/java/org/thingsboard/server/edge/WidgetEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/WidgetEdgeTest.java index bb6d09f7bc..a325de3402 100644 --- a/application/src/test/java/org/thingsboard/server/edge/WidgetEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/WidgetEdgeTest.java @@ -43,7 +43,7 @@ public class WidgetEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof WidgetsBundleUpdateMsg); WidgetsBundleUpdateMsg widgetsBundleUpdateMsg = (WidgetsBundleUpdateMsg) latestMessage; - WidgetsBundle widgetsBundleMsg = JacksonUtil.fromEdgeString(widgetsBundleUpdateMsg.getEntity(), WidgetsBundle.class); + WidgetsBundle widgetsBundleMsg = JacksonUtil.fromStringIgnoreUnknownProperties(widgetsBundleUpdateMsg.getEntity(), WidgetsBundle.class); Assert.assertNotNull(widgetsBundleMsg); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, widgetsBundleUpdateMsg.getMsgType()); Assert.assertEquals(savedWidgetsBundle.getId(), widgetsBundleMsg.getId()); @@ -65,7 +65,7 @@ public class WidgetEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof WidgetTypeUpdateMsg); WidgetTypeUpdateMsg widgetTypeUpdateMsg = (WidgetTypeUpdateMsg) latestMessage; - WidgetType widgetsType = JacksonUtil.fromEdgeString(widgetTypeUpdateMsg.getEntity(), WidgetType.class); + WidgetType widgetsType = JacksonUtil.fromStringIgnoreUnknownProperties(widgetTypeUpdateMsg.getEntity(), WidgetType.class); Assert.assertNotNull(widgetsType); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, widgetTypeUpdateMsg.getMsgType()); Assert.assertEquals(savedWidgetType, widgetsType); @@ -78,7 +78,7 @@ public class WidgetEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof WidgetsBundleUpdateMsg); widgetsBundleUpdateMsg = (WidgetsBundleUpdateMsg) latestMessage; - widgetsBundleMsg = JacksonUtil.fromEdgeString(widgetsBundleUpdateMsg.getEntity(), WidgetsBundle.class); + widgetsBundleMsg = JacksonUtil.fromStringIgnoreUnknownProperties(widgetsBundleUpdateMsg.getEntity(), WidgetsBundle.class); Assert.assertNotNull(widgetsBundleMsg); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, widgetsBundleUpdateMsg.getMsgType()); Assert.assertEquals(savedWidgetsBundle.getTitle(), widgetsBundleMsg.getTitle()); @@ -91,7 +91,7 @@ public class WidgetEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof WidgetTypeUpdateMsg); widgetTypeUpdateMsg = (WidgetTypeUpdateMsg) latestMessage; - widgetsType = JacksonUtil.fromEdgeString(widgetTypeUpdateMsg.getEntity(), WidgetType.class); + widgetsType = JacksonUtil.fromStringIgnoreUnknownProperties(widgetTypeUpdateMsg.getEntity(), WidgetType.class); Assert.assertNotNull(widgetsType); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, widgetTypeUpdateMsg.getMsgType()); Assert.assertEquals(savedWidgetType.getName(), widgetsType.getName()); From 5de58644e550618a36d1ebec80995c1d32f36a4d Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Mon, 13 Nov 2023 11:32:04 +0100 Subject: [PATCH 110/252] refactoring --- .../server/service/entitiy/device/DefaultTbDeviceService.java | 2 -- .../org/thingsboard/server/controller/DeviceControllerTest.java | 2 -- 2 files changed, 4 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java index a3d04f6344..ac2d656ba6 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java @@ -232,8 +232,6 @@ public class DefaultTbDeviceService extends AbstractTbEntityService implements T Tenant tenant = tenantService.findTenantById(tenantId); Device assignedDevice = deviceService.assignDeviceToTenant(newTenantId, device); - - notificationEntityService.notifyAssignDeviceToTenant(tenantId, newTenantId, deviceId, assignedDevice.getCustomerId(), assignedDevice, tenant, user, newTenantId.toString(), newTenant.getName()); diff --git a/application/src/test/java/org/thingsboard/server/controller/DeviceControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/DeviceControllerTest.java index b6afaa34e1..cefe24e935 100644 --- a/application/src/test/java/org/thingsboard/server/controller/DeviceControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/DeviceControllerTest.java @@ -72,7 +72,6 @@ import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.DeviceCredentialsValidationException; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.service.DaoSqlTest; -import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.service.gateway_device.GatewayNotificationsService; import org.thingsboard.server.service.state.DeviceStateService; @@ -85,7 +84,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.containsString; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; From caf2f29693503fca98ee7bb97dd41acf2689d605 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Thu, 28 Sep 2023 09:33:47 +0300 Subject: [PATCH 111/252] Refactor MsgConstructors to be in sync with PE logic --- .../rpc/constructor/AdminSettingsMsgConstructor.java | 7 ++++--- .../edge/rpc/constructor/AlarmMsgConstructor.java | 8 +++++--- .../edge/rpc/constructor/AssetMsgConstructor.java | 7 ++++--- .../edge/rpc/constructor/AssetProfileMsgConstructor.java | 7 ++++--- .../edge/rpc/constructor/CustomerMsgConstructor.java | 7 ++++--- .../edge/rpc/constructor/DashboardMsgConstructor.java | 7 ++++--- .../edge/rpc/constructor/DeviceMsgConstructor.java | 7 ++++--- .../rpc/constructor/DeviceProfileMsgConstructor.java | 7 ++++--- .../edge/rpc/constructor/EntityViewMsgConstructor.java | 9 +++++---- .../edge/rpc/constructor/OtaPackageMsgConstructor.java | 7 ++++--- .../edge/rpc/constructor/QueueMsgConstructor.java | 7 ++++--- .../edge/rpc/constructor/RelationMsgConstructor.java | 7 ++++--- .../edge/rpc/constructor/RuleChainMsgConstructor.java | 7 ++++--- .../edge/rpc/constructor/TenantMsgConstructor.java | 7 ++++--- .../rpc/constructor/TenantProfileMsgConstructor.java | 7 ++++--- .../service/edge/rpc/constructor/UserMsgConstructor.java | 7 ++++--- .../edge/rpc/constructor/WidgetTypeMsgConstructor.java | 7 ++++--- .../rpc/constructor/WidgetsBundleMsgConstructor.java | 7 ++++--- .../rpc/processor/dashboard/BaseDashboardProcessor.java | 6 ++---- .../processor/entityview/BaseEntityViewProcessor.java | 3 +++ .../processor/entityview/EntityViewEdgeProcessor.java | 9 +++++++++ 21 files changed, 88 insertions(+), 59 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AdminSettingsMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AdminSettingsMsgConstructor.java index 888f203fb6..26037e221e 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AdminSettingsMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AdminSettingsMsgConstructor.java @@ -28,9 +28,10 @@ import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; public class AdminSettingsMsgConstructor { public AdminSettingsUpdateMsg constructAdminSettingsUpdateMsg(AdminSettings adminSettings, EdgeVersion edgeVersion) { - return EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion) - ? constructDeprecatedWidgetTypeUpdateMsg(adminSettings) - : AdminSettingsUpdateMsg.newBuilder().setEntity(JacksonUtil.toString(adminSettings)).build(); + if (EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)) { + return constructDeprecatedWidgetTypeUpdateMsg(adminSettings); + } + return AdminSettingsUpdateMsg.newBuilder().setEntity(JacksonUtil.toString(adminSettings)).build(); } private AdminSettingsUpdateMsg constructDeprecatedWidgetTypeUpdateMsg(AdminSettings adminSettings) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AlarmMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AlarmMsgConstructor.java index 2fbb4e6cd6..f9b145ec99 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AlarmMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AlarmMsgConstructor.java @@ -29,9 +29,11 @@ import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; public class AlarmMsgConstructor { public AlarmUpdateMsg constructAlarmUpdatedMsg(UpdateMsgType msgType, Alarm alarm, String entityName, EdgeVersion edgeVersion) { - return EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion) - ? constructDeprecatedAlarmUpdatedMsg(msgType, alarm, entityName) - : AlarmUpdateMsg.newBuilder().setMsgType(msgType).setOriginatorName(entityName).setEntity(JacksonUtil.toString(alarm)) + if (EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion)) { + return constructDeprecatedAlarmUpdatedMsg(msgType, alarm, entityName); + } + return AlarmUpdateMsg.newBuilder().setMsgType(msgType) + .setEntity(JacksonUtil.toString(alarm)).setOriginatorName(entityName) .setIdMSB(alarm.getId().getId().getMostSignificantBits()) .setIdLSB(alarm.getId().getId().getLeastSignificantBits()).build(); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetMsgConstructor.java index e3ffb71e47..72235be45d 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetMsgConstructor.java @@ -30,9 +30,10 @@ import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; public class AssetMsgConstructor { public AssetUpdateMsg constructAssetUpdatedMsg(UpdateMsgType msgType, Asset asset, EdgeVersion edgeVersion) { - return EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion) - ? constructDeprecatedAssetUpdateMsg(msgType, asset) - : AssetUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(asset)) + if (EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion)) { + return constructDeprecatedAssetUpdateMsg(msgType, asset); + } + return AssetUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(asset)) .setIdMSB(asset.getUuidId().getMostSignificantBits()) .setIdLSB(asset.getUuidId().getLeastSignificantBits()).build(); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetProfileMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetProfileMsgConstructor.java index c344adc15c..b140e4f529 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetProfileMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetProfileMsgConstructor.java @@ -33,9 +33,10 @@ import java.nio.charset.StandardCharsets; public class AssetProfileMsgConstructor { public AssetProfileUpdateMsg constructAssetProfileUpdatedMsg(UpdateMsgType msgType, AssetProfile assetProfile, EdgeVersion edgeVersion) { - return EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion) - ? constructDeprecatedAssetProfileUpdatedMsg(msgType, assetProfile) - : AssetProfileUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(assetProfile)) + if (EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion)) { + return constructDeprecatedAssetProfileUpdatedMsg(msgType, assetProfile); + } + return AssetProfileUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(assetProfile)) .setIdMSB(assetProfile.getId().getId().getMostSignificantBits()) .setIdLSB(assetProfile.getId().getId().getLeastSignificantBits()).build(); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/CustomerMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/CustomerMsgConstructor.java index 714cb60672..e84978383e 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/CustomerMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/CustomerMsgConstructor.java @@ -30,9 +30,10 @@ import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; public class CustomerMsgConstructor { public CustomerUpdateMsg constructCustomerUpdatedMsg(UpdateMsgType msgType, Customer customer, EdgeVersion edgeVersion) { - return EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion) - ? constructDeprecatedCustomerUpdatedMsg(msgType, customer) - : CustomerUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(customer)) + if (EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion)) { + return constructDeprecatedCustomerUpdatedMsg(msgType, customer); + } + return CustomerUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(customer)) .setIdMSB(customer.getId().getId().getMostSignificantBits()) .setIdLSB(customer.getId().getId().getLeastSignificantBits()).build(); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DashboardMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DashboardMsgConstructor.java index 76a969e30a..351b3d8e57 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DashboardMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DashboardMsgConstructor.java @@ -30,9 +30,10 @@ import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; public class DashboardMsgConstructor { public DashboardUpdateMsg constructDashboardUpdatedMsg(UpdateMsgType msgType, Dashboard dashboard, EdgeVersion edgeVersion) { - return EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion) - ? constructDeprecatedDashboardUpdatedMsg(msgType, dashboard) - : DashboardUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(dashboard)) + if (EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion)) { + return constructDeprecatedDashboardUpdatedMsg(msgType, dashboard); + } + return DashboardUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(dashboard)) .setIdMSB(dashboard.getId().getId().getMostSignificantBits()) .setIdLSB(dashboard.getId().getId().getLeastSignificantBits()).build(); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceMsgConstructor.java index 9325fbc424..15c221e608 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceMsgConstructor.java @@ -44,9 +44,10 @@ public class DeviceMsgConstructor { private DataDecodingEncodingService dataDecodingEncodingService; public DeviceUpdateMsg constructDeviceUpdatedMsg(UpdateMsgType msgType, Device device, EdgeVersion edgeVersion) { - return EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion) - ? constructDeprecatedDeviceUpdateMsg(msgType, device) - : DeviceUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(device)) + if (EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion)) { + return constructDeprecatedDeviceUpdateMsg(msgType, device); + } + return DeviceUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(device)) .setIdMSB(device.getId().getId().getMostSignificantBits()) .setIdLSB(device.getId().getId().getLeastSignificantBits()).build(); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceProfileMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceProfileMsgConstructor.java index 802cadbe87..de6920ee7f 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceProfileMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceProfileMsgConstructor.java @@ -38,9 +38,10 @@ public class DeviceProfileMsgConstructor { private DataDecodingEncodingService dataDecodingEncodingService; public DeviceProfileUpdateMsg constructDeviceProfileUpdatedMsg(UpdateMsgType msgType, DeviceProfile deviceProfile, EdgeVersion edgeVersion) { - return EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion) - ? constructDeprecatedDeviceProfileUpdatedMsg(msgType, deviceProfile) - : DeviceProfileUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(deviceProfile)) + if (EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion)) { + return constructDeprecatedDeviceProfileUpdatedMsg(msgType, deviceProfile); + } + return DeviceProfileUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(deviceProfile)) .setIdMSB(deviceProfile.getId().getId().getMostSignificantBits()) .setIdLSB(deviceProfile.getId().getId().getLeastSignificantBits()).build(); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityViewMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityViewMsgConstructor.java index 2f71103ba5..96db358811 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityViewMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityViewMsgConstructor.java @@ -32,14 +32,15 @@ import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; public class EntityViewMsgConstructor { public EntityViewUpdateMsg constructEntityViewUpdatedMsg(UpdateMsgType msgType, EntityView entityView, EdgeVersion edgeVersion) { - return EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion) - ? constructEntityViewUpdatedMsgDeprecated(msgType, entityView) - : EntityViewUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(entityView)) + if (EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion)) { + return constructDeprecatedEntityViewUpdatedMsg(msgType, entityView); + } + return EntityViewUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(entityView)) .setIdMSB(entityView.getId().getId().getMostSignificantBits()) .setIdLSB(entityView.getId().getId().getLeastSignificantBits()).build(); } - private EntityViewUpdateMsg constructEntityViewUpdatedMsgDeprecated(UpdateMsgType msgType, EntityView entityView) { + private EntityViewUpdateMsg constructDeprecatedEntityViewUpdatedMsg(UpdateMsgType msgType, EntityView entityView) { EdgeEntityType edgeEntityType = checkEntityType(entityView.getEntityId().getEntityType()); EntityViewUpdateMsg.Builder builder = EntityViewUpdateMsg.newBuilder() .setMsgType(msgType) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/OtaPackageMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/OtaPackageMsgConstructor.java index a946767088..cf90f25563 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/OtaPackageMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/OtaPackageMsgConstructor.java @@ -31,9 +31,10 @@ import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; public class OtaPackageMsgConstructor { public OtaPackageUpdateMsg constructOtaPackageUpdatedMsg(UpdateMsgType msgType, OtaPackage otaPackage, EdgeVersion edgeVersion) { - return EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion) - ? constructDeprecatedOtaPackageUpdatedMsg(msgType, otaPackage) - : OtaPackageUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(otaPackage)) + if (EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion)) { + return constructDeprecatedOtaPackageUpdatedMsg(msgType, otaPackage); + } + return OtaPackageUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(otaPackage)) .setIdMSB(otaPackage.getId().getId().getMostSignificantBits()) .setIdLSB(otaPackage.getId().getId().getLeastSignificantBits()).build(); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/QueueMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/QueueMsgConstructor.java index 3dbb8d4d05..6c9f9f441a 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/QueueMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/QueueMsgConstructor.java @@ -34,9 +34,10 @@ import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; public class QueueMsgConstructor { public QueueUpdateMsg constructQueueUpdatedMsg(UpdateMsgType msgType, Queue queue, EdgeVersion edgeVersion) { - return EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion) - ? constructDeprecatedQueueUpdatedMsg(msgType, queue) - : QueueUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(queue)) + if (EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion)) { + return constructDeprecatedQueueUpdatedMsg(msgType, queue); + } + return QueueUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(queue)) .setIdMSB(queue.getId().getId().getMostSignificantBits()) .setIdLSB(queue.getId().getId().getLeastSignificantBits()).build(); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RelationMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RelationMsgConstructor.java index 31b4ade4b1..01abb37236 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RelationMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RelationMsgConstructor.java @@ -29,9 +29,10 @@ import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; public class RelationMsgConstructor { public RelationUpdateMsg constructRelationUpdatedMsg(UpdateMsgType msgType, EntityRelation entityRelation, EdgeVersion edgeVersion) { - return EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion) - ? constructDeprecatedRelationUpdatedMsg(msgType, entityRelation) - : RelationUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(entityRelation)).build(); + if (EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)) { + return constructDeprecatedRelationUpdatedMsg(msgType, entityRelation); + } + return RelationUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(entityRelation)).build(); } private RelationUpdateMsg constructDeprecatedRelationUpdatedMsg(UpdateMsgType msgType, EntityRelation entityRelation) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java index f51ee17c13..15fe10618a 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java @@ -38,9 +38,10 @@ public class RuleChainMsgConstructor { public RuleChainUpdateMsg constructRuleChainUpdatedMsg(UpdateMsgType msgType, RuleChain ruleChain, boolean isRoot, EdgeVersion edgeVersion) { ruleChain.setRoot(isRoot); - return EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion) - ? constructDeprecatedRuleChainUpdatedMsg(msgType, ruleChain) - : RuleChainUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(ruleChain)) + if (EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion)) { + return constructDeprecatedRuleChainUpdatedMsg(msgType, ruleChain); + } + return RuleChainUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(ruleChain)) .setIdMSB(ruleChain.getId().getId().getMostSignificantBits()) .setIdLSB(ruleChain.getId().getId().getLeastSignificantBits()).build(); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/TenantMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/TenantMsgConstructor.java index 6f9aee29f5..6f6c4149ed 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/TenantMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/TenantMsgConstructor.java @@ -29,9 +29,10 @@ import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; public class TenantMsgConstructor { public TenantUpdateMsg constructTenantUpdateMsg(UpdateMsgType msgType, Tenant tenant, EdgeVersion edgeVersion) { - return EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion) - ? constructDeprecatedTenantUpdateMsg(msgType, tenant) - : TenantUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(tenant)).build(); + if (EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion)) { + return constructDeprecatedTenantUpdateMsg(msgType, tenant); + } + return TenantUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(tenant)).build(); } private TenantUpdateMsg constructDeprecatedTenantUpdateMsg(UpdateMsgType msgType, Tenant tenant) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/TenantProfileMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/TenantProfileMsgConstructor.java index f56a781437..a93c730ee7 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/TenantProfileMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/TenantProfileMsgConstructor.java @@ -37,9 +37,10 @@ public class TenantProfileMsgConstructor { public TenantProfileUpdateMsg constructTenantProfileUpdateMsg(UpdateMsgType msgType, TenantProfile tenantProfile, EdgeVersion edgeVersion) { ByteString profileData = EdgeVersionUtils.isEdgeVersionOlderThan(edgeVersion, EdgeVersion.V_3_6_1) ? ByteString.empty() : ByteString.copyFrom(dataDecodingEncodingService.encode(tenantProfile.getProfileData())); - return EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion) - ? constructDeprecatedTenantProfileUpdateMsg(msgType, tenantProfile, profileData) - : TenantProfileUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(tenantProfile)).build(); + if (EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)) { + return constructDeprecatedTenantProfileUpdateMsg(msgType, tenantProfile, profileData); + } + return TenantProfileUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(tenantProfile)).build(); } private TenantProfileUpdateMsg constructDeprecatedTenantProfileUpdateMsg(UpdateMsgType msgType, TenantProfile tenantProfile, ByteString profileData) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/UserMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/UserMsgConstructor.java index e0829ead4f..1f39f74150 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/UserMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/UserMsgConstructor.java @@ -32,9 +32,10 @@ import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; public class UserMsgConstructor { public UserUpdateMsg constructUserUpdatedMsg(UpdateMsgType msgType, User user, EdgeVersion edgeVersion) { - return EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion) - ? constructDeprecatedUserUpdatedMsg(msgType, user) - : UserUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(user)) + if (EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion)) { + return constructDeprecatedUserUpdatedMsg(msgType, user); + } + return UserUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(user)) .setIdMSB(user.getId().getId().getMostSignificantBits()) .setIdLSB(user.getId().getId().getLeastSignificantBits()).build(); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetTypeMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetTypeMsgConstructor.java index 8d928e7125..d4e5810c04 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetTypeMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetTypeMsgConstructor.java @@ -33,9 +33,10 @@ import java.util.Arrays; public class WidgetTypeMsgConstructor { public WidgetTypeUpdateMsg constructWidgetTypeUpdateMsg(UpdateMsgType msgType, WidgetTypeDetails widgetTypeDetails, EdgeVersion edgeVersion) { - return EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion) - ? constructDeprecatedWidgetTypeUpdateMsg(msgType, widgetTypeDetails, edgeVersion) - : WidgetTypeUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(widgetTypeDetails)) + if (EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)) { + return constructDeprecatedWidgetTypeUpdateMsg(msgType, widgetTypeDetails, edgeVersion); + } + return WidgetTypeUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(widgetTypeDetails)) .setIdMSB(widgetTypeDetails.getId().getId().getMostSignificantBits()) .setIdLSB(widgetTypeDetails.getId().getId().getLeastSignificantBits()).build(); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetsBundleMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetsBundleMsgConstructor.java index e52e44aa1e..16e9d58ad6 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetsBundleMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetsBundleMsgConstructor.java @@ -35,9 +35,10 @@ import java.util.List; public class WidgetsBundleMsgConstructor { public WidgetsBundleUpdateMsg constructWidgetsBundleUpdateMsg(UpdateMsgType msgType, WidgetsBundle widgetsBundle, List widgets, EdgeVersion edgeVersion) { - return EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion) - ? constructDeprecatedWidgetsBundleUpdateMsg(msgType, widgetsBundle, widgets) - : WidgetsBundleUpdateMsg.newBuilder().setWidgets(JacksonUtil.toString(widgets)).setEntity(JacksonUtil.toString(widgetsBundle)) + if (EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion)) { + return constructDeprecatedWidgetsBundleUpdateMsg(msgType, widgetsBundle, widgets); + } + return WidgetsBundleUpdateMsg.newBuilder().setWidgets(JacksonUtil.toString(widgets)).setEntity(JacksonUtil.toString(widgetsBundle)) .setMsgType(msgType).setIdMSB(widgetsBundle.getId().getId().getMostSignificantBits()) .setIdLSB(widgetsBundle.getId().getId().getLeastSignificantBits()).build(); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/BaseDashboardProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/BaseDashboardProcessor.java index 53db17a3ba..15c446af67 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/BaseDashboardProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/BaseDashboardProcessor.java @@ -31,15 +31,14 @@ import java.util.Set; @Slf4j public abstract class BaseDashboardProcessor extends BaseEdgeProcessor { - protected boolean saveOrUpdateDashboard(TenantId tenantId, DashboardId deprecatedDashboardId, DashboardUpdateMsg dashboardUpdateMsg, boolean isEdgeProtoDeprecated) { + protected boolean saveOrUpdateDashboard(TenantId tenantId, DashboardId dashboardId, DashboardUpdateMsg dashboardUpdateMsg, boolean isEdgeProtoDeprecated) { boolean created = false; Dashboard dashboard = isEdgeProtoDeprecated - ? createDashboard(tenantId, deprecatedDashboardId, dashboardUpdateMsg) + ? createDashboard(tenantId, dashboardId, dashboardUpdateMsg) : JacksonUtil.fromStringIgnoreUnknownProperties(dashboardUpdateMsg.getEntity(), Dashboard.class); if (dashboard == null) { throw new RuntimeException("[{" + tenantId + "}] dashboardUpdateMsg {" + dashboardUpdateMsg + "} cannot be converted to dashboard"); } - DashboardId dashboardId = dashboard.getId(); Dashboard dashboardById = dashboardService.findDashboardById(tenantId, dashboardId); if (dashboardById == null) { created = true; @@ -64,7 +63,6 @@ public abstract class BaseDashboardProcessor extends BaseEdgeProcessor { private Dashboard createDashboard(TenantId tenantId, DashboardId dashboardId, DashboardUpdateMsg dashboardUpdateMsg) { Dashboard dashboard = new Dashboard(); dashboard.setTenantId(tenantId); - dashboard.setId(dashboardId); dashboard.setCreatedTime(Uuids.unixTimestamp(dashboardId.getId())); dashboard.setTitle(dashboardUpdateMsg.getTitle()); dashboard.setImage(dashboardUpdateMsg.hasImage() ? dashboardUpdateMsg.getImage() : null); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/BaseEntityViewProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/BaseEntityViewProcessor.java index 236c00a861..b53470b0cb 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/BaseEntityViewProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/BaseEntityViewProcessor.java @@ -60,6 +60,7 @@ public abstract class BaseEntityViewProcessor extends BaseEdgeProcessor { entityViewNameUpdated = true; } entityView.setName(entityViewName); + setCustomerId(tenantId, created ? null : entityViewById.getCustomerId(), entityView, entityViewUpdateMsg, isEdgeProtoDeprecated); entityViewValidator.validate(entityView, EntityView::getTenantId); if (created) { @@ -90,4 +91,6 @@ public abstract class BaseEntityViewProcessor extends BaseEdgeProcessor { } return entityView; } + + protected abstract void setCustomerId(TenantId tenantId, CustomerId customerId, EntityView entityView, EntityViewUpdateMsg entityViewUpdateMsg, boolean isEdgeVersionDeprecated); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewEdgeProcessor.java index 41996d8afb..7290f731ff 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewEdgeProcessor.java @@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.edge.EdgeEventType; +import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.msg.TbMsgType; @@ -137,4 +138,12 @@ public class EntityViewEdgeProcessor extends BaseEntityViewProcessor { } return downlinkMsg; } + + @Override + protected void setCustomerId(TenantId tenantId, CustomerId customerId, EntityView entityView, EntityViewUpdateMsg entityViewUpdateMsg, boolean isEdgeVersionDeprecated) { + CustomerId customerUUID = isEdgeVersionDeprecated + ? safeGetCustomerId(entityViewUpdateMsg.getCustomerIdMSB(), entityViewUpdateMsg.getCustomerIdLSB()) + : entityView.getCustomerId() != null ? entityView.getCustomerId() : customerId; + entityView.setCustomerId(customerUUID); + } } From ec4200d735ed19286c6ac922ce9dfc4a2fa8c7ce Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Fri, 13 Oct 2023 11:51:02 +0300 Subject: [PATCH 112/252] Refactoring after final review --- .../processor/alarm/BaseAlarmProcessor.java | 6 ++-- .../asset/AssetProfileEdgeProcessor.java | 7 ++-- .../asset/BaseAssetProfileProcessor.java | 7 ++-- .../dashboard/BaseDashboardProcessor.java | 33 ++++++++++++++++--- .../dashboard/DashboardEdgeProcessor.java | 2 +- .../device/BaseDeviceProfileProcessor.java | 7 ++-- .../device/DeviceProfileEdgeProcessor.java | 7 ++-- .../server/edge/AbstractEdgeTest.java | 17 ++++++---- .../server/edge/AssetProfileEdgeTest.java | 2 -- common/edge-api/src/main/proto/edge.proto | 2 +- .../common/util/JacksonUtilTest.java | 8 ++--- 11 files changed, 63 insertions(+), 35 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java index b89c2d4dc4..e81b7916f6 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java @@ -56,9 +56,9 @@ public abstract class BaseAlarmProcessor extends BaseEdgeProcessor { if (alarm == null) { throw new RuntimeException("[{" + tenantId + "}] alarmUpdateMsg {" + alarmUpdateMsg + "} cannot be converted to alarm"); } - EntityType entityType = isEdgeProtoDeprecated ? EntityType.valueOf(alarmUpdateMsg.getOriginatorType()) - : alarm.getOriginator().getEntityType(); - EntityId originatorId = getAlarmOriginator(tenantId, alarmUpdateMsg.getOriginatorName(), entityType); + EntityId originatorId = isEdgeProtoDeprecated + ? getAlarmOriginator(tenantId, alarmUpdateMsg.getOriginatorName(), EntityType.valueOf(alarmUpdateMsg.getOriginatorType())) + : alarm.getOriginator(); if (originatorId == null) { log.warn("[{}] Originator not found for the alarm msg {}", tenantId, alarmUpdateMsg); return Futures.immediateFuture(null); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetProfileEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetProfileEdgeProcessor.java index b7b6690a87..544cf46690 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetProfileEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetProfileEdgeProcessor.java @@ -129,17 +129,16 @@ public class AssetProfileEdgeProcessor extends BaseAssetProfileProcessor { } @Override - protected void setDefaultRuleChainId(TenantId tenantId, AssetProfile assetProfile) { - // do nothing on cloud + protected void setDefaultRuleChainId(TenantId tenantId, AssetProfile assetProfile, RuleChainId ruleChainId) { + assetProfile.setDefaultRuleChainId(ruleChainId); } @Override protected void setDefaultEdgeRuleChainId(AssetProfile assetProfile, RuleChainId ruleChainId, AssetProfileUpdateMsg assetProfileUpdateMsg, boolean isEdgeVersionDeprecated) { UUID defaultEdgeRuleChainUUID = isEdgeVersionDeprecated ? safeGetUUID(assetProfileUpdateMsg.getDefaultRuleChainIdMSB(), assetProfileUpdateMsg.getDefaultRuleChainIdLSB()) - : assetProfile.getDefaultRuleChainId() != null ? assetProfile.getDefaultRuleChainId().getId() : null; + : ruleChainId != null ? ruleChainId.getId() : null; assetProfile.setDefaultEdgeRuleChainId(defaultEdgeRuleChainUUID != null ? new RuleChainId(defaultEdgeRuleChainUUID) : null); - assetProfile.setDefaultRuleChainId(ruleChainId); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProfileProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProfileProcessor.java index d829df4a32..2712881a30 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProfileProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProfileProcessor.java @@ -61,8 +61,9 @@ public abstract class BaseAssetProfileProcessor extends BaseEdgeProcessor { } assetProfile.setName(assetProfileName); - setDefaultRuleChainId(tenantId, assetProfile); - setDefaultEdgeRuleChainId(assetProfile, created ? null : assetProfileById.getDefaultRuleChainId(), assetProfileUpdateMsg, isEdgeProtoDeprecated); + RuleChainId ruleChainId = assetProfile.getDefaultRuleChainId(); + setDefaultRuleChainId(tenantId, assetProfile, created ? null : assetProfileById.getDefaultRuleChainId()); + setDefaultEdgeRuleChainId(assetProfile, ruleChainId, assetProfileUpdateMsg, isEdgeProtoDeprecated); setDefaultDashboardId(tenantId, created ? null : assetProfileById.getDefaultDashboardId(), assetProfile, assetProfileUpdateMsg, isEdgeProtoDeprecated); assetProfileValidator.validate(assetProfile, AssetProfile::getTenantId); @@ -92,7 +93,7 @@ public abstract class BaseAssetProfileProcessor extends BaseEdgeProcessor { return assetProfile; } - protected abstract void setDefaultRuleChainId(TenantId tenantId, AssetProfile assetProfile); + protected abstract void setDefaultRuleChainId(TenantId tenantId, AssetProfile assetProfile, RuleChainId ruleChainId); protected abstract void setDefaultEdgeRuleChainId(AssetProfile assetProfile, RuleChainId ruleChainId, AssetProfileUpdateMsg assetProfileUpdateMsg, boolean isEdgeVersionDeprecated); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/BaseDashboardProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/BaseDashboardProcessor.java index 15c446af67..823de9a58b 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/BaseDashboardProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/BaseDashboardProcessor.java @@ -21,6 +21,7 @@ import lombok.extern.slf4j.Slf4j; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.ShortCustomerInfo; +import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg; @@ -31,7 +32,7 @@ import java.util.Set; @Slf4j public abstract class BaseDashboardProcessor extends BaseEdgeProcessor { - protected boolean saveOrUpdateDashboard(TenantId tenantId, DashboardId dashboardId, DashboardUpdateMsg dashboardUpdateMsg, boolean isEdgeProtoDeprecated) { + protected boolean saveOrUpdateDashboard(TenantId tenantId, DashboardId dashboardId, DashboardUpdateMsg dashboardUpdateMsg, boolean isEdgeProtoDeprecated, CustomerId customerId) { boolean created = false; Dashboard dashboard = isEdgeProtoDeprecated ? createDashboard(tenantId, dashboardId, dashboardUpdateMsg) @@ -39,23 +40,38 @@ public abstract class BaseDashboardProcessor extends BaseEdgeProcessor { if (dashboard == null) { throw new RuntimeException("[{" + tenantId + "}] dashboardUpdateMsg {" + dashboardUpdateMsg + "} cannot be converted to dashboard"); } + Set assignedCustomers = null; Dashboard dashboardById = dashboardService.findDashboardById(tenantId, dashboardId); if (dashboardById == null) { created = true; dashboard.setId(null); } else { dashboard.setId(dashboardId); + assignedCustomers = filterNonExistingCustomers(tenantId, dashboardById.getAssignedCustomers()); } dashboardValidator.validate(dashboard, Dashboard::getTenantId); if (created) { dashboard.setId(dashboardId); } + Set msgAssignedCustomers = filterNonExistingCustomers(tenantId, dashboard.getAssignedCustomers()); + if (msgAssignedCustomers != null) { + if (assignedCustomers == null) { + assignedCustomers = msgAssignedCustomers; + } else { + assignedCustomers.addAll(msgAssignedCustomers); + } + } + dashboard.setAssignedCustomers(assignedCustomers); Dashboard savedDashboard = dashboardService.saveDashboard(dashboard, false); - if (savedDashboard.getAssignedCustomers() != null && savedDashboard.getAssignedCustomers().isEmpty()) { - for (ShortCustomerInfo assignedCustomer : savedDashboard.getAssignedCustomers()) { - dashboardService.unassignDashboardFromCustomer(tenantId, savedDashboard.getId(), assignedCustomer.getCustomerId()); + if (msgAssignedCustomers != null && !msgAssignedCustomers.isEmpty()) { + for (ShortCustomerInfo assignedCustomer : msgAssignedCustomers) { + if (assignedCustomer.getCustomerId().equals(customerId)) { + dashboardService.assignDashboardToCustomer(tenantId, savedDashboard.getId(), assignedCustomer.getCustomerId()); + } } + } else { + unassignCustomersFromDashboard(tenantId, savedDashboard, customerId); } return created; } @@ -80,6 +96,15 @@ public abstract class BaseDashboardProcessor extends BaseEdgeProcessor { return dashboard; } + private void unassignCustomersFromDashboard(TenantId tenantId, Dashboard dashboard, CustomerId customerId) { + if (dashboard.getAssignedCustomers() != null && !dashboard.getAssignedCustomers().isEmpty()) { + for (ShortCustomerInfo assignedCustomer : dashboard.getAssignedCustomers()) { + if (assignedCustomer.getCustomerId().equals(customerId)) { + dashboardService.unassignDashboardFromCustomer(tenantId, dashboard.getId(), assignedCustomer.getCustomerId()); + } + } + } + } protected abstract Set filterNonExistingCustomers(TenantId tenantId, Set assignedCustomers); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardEdgeProcessor.java index 630416f775..a7e7cdbc12 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardEdgeProcessor.java @@ -80,7 +80,7 @@ public class DashboardEdgeProcessor extends BaseDashboardProcessor { private void saveOrUpdateDashboard(TenantId tenantId, DashboardId dashboardId, DashboardUpdateMsg dashboardUpdateMsg, Edge edge, EdgeVersion edgeVersion) { boolean created = super.saveOrUpdateDashboard(tenantId, dashboardId, dashboardUpdateMsg, - EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)); + EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion), edge.getCustomerId()); if (created) { createRelationFromEdge(tenantId, edge.getId(), dashboardId); pushDashboardCreatedEventToRuleEngine(tenantId, edge, dashboardId); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProfileProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProfileProcessor.java index 38d781fb13..27ed9eb3c9 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProfileProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProfileProcessor.java @@ -73,8 +73,9 @@ public abstract class BaseDeviceProfileProcessor extends BaseEdgeProcessor { } deviceProfile.setName(deviceProfileName); - setDefaultRuleChainId(tenantId, deviceProfile); - setDefaultEdgeRuleChainId(deviceProfile, created ? null : deviceProfileById.getDefaultRuleChainId(), deviceProfileUpdateMsg, isEdgeVersionProtoDeprecated); + RuleChainId ruleChainId = deviceProfile.getDefaultRuleChainId(); + setDefaultRuleChainId(tenantId, deviceProfile, created ? null : deviceProfileById.getDefaultRuleChainId()); + setDefaultEdgeRuleChainId(deviceProfile, ruleChainId, deviceProfileUpdateMsg, isEdgeVersionProtoDeprecated); setDefaultDashboardId(tenantId, created ? null : deviceProfileById.getDefaultDashboardId(), deviceProfile, deviceProfileUpdateMsg, isEdgeVersionProtoDeprecated); deviceProfileValidator.validate(deviceProfile, DeviceProfile::getTenantId); @@ -124,7 +125,7 @@ public abstract class BaseDeviceProfileProcessor extends BaseEdgeProcessor { return deviceProfile; } - protected abstract void setDefaultRuleChainId(TenantId tenantId, DeviceProfile deviceProfile); + protected abstract void setDefaultRuleChainId(TenantId tenantId, DeviceProfile deviceProfile, RuleChainId ruleChainId); protected abstract void setDefaultEdgeRuleChainId(DeviceProfile deviceProfile, RuleChainId ruleChainId, DeviceProfileUpdateMsg deviceProfileUpdateMsg, boolean isEdgeVersionDeprecated); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceProfileEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceProfileEdgeProcessor.java index 419b7f90c8..3814992491 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceProfileEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceProfileEdgeProcessor.java @@ -129,17 +129,16 @@ public class DeviceProfileEdgeProcessor extends BaseDeviceProfileProcessor { } @Override - protected void setDefaultRuleChainId(TenantId tenantId, DeviceProfile deviceProfile) { - // do nothing on cloud + protected void setDefaultRuleChainId(TenantId tenantId, DeviceProfile deviceProfile, RuleChainId ruleChainId) { + deviceProfile.setDefaultRuleChainId(ruleChainId); } @Override protected void setDefaultEdgeRuleChainId(DeviceProfile deviceProfile, RuleChainId ruleChainId, DeviceProfileUpdateMsg deviceProfileUpdateMsg, boolean isEdgeVersionDeprecated) { UUID defaultEdgeRuleChainUUID = isEdgeVersionDeprecated ? safeGetUUID(deviceProfileUpdateMsg.getDefaultRuleChainIdMSB(), deviceProfileUpdateMsg.getDefaultRuleChainIdLSB()) - : deviceProfile.getDefaultRuleChainId() != null ? deviceProfile.getDefaultRuleChainId().getId() : null; + : ruleChainId != null ? ruleChainId.getId() : null; deviceProfile.setDefaultEdgeRuleChainId(defaultEdgeRuleChainUUID != null ? new RuleChainId(defaultEdgeRuleChainUUID) : null); - deviceProfile.setDefaultRuleChainId(ruleChainId); } @Override diff --git a/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java index b2bce41bf6..88aead4c7d 100644 --- a/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java @@ -449,6 +449,9 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { if (adminSettings.getKey().equals("connectivity")) { validateConnectivityAdminSettings(adminSettings); } + if (adminSettings.getKey().equals("jwt")) { + validateJwtAdminSettings(adminSettings); + } } } @@ -467,12 +470,14 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { private void validateConnectivityAdminSettings(AdminSettings adminSettings) { JsonNode jsonNode = adminSettings.getJsonValue(); - Assert.assertNotNull(jsonNode.get("http")); - Assert.assertNotNull(jsonNode.get("https")); - Assert.assertNotNull(jsonNode.get("mqtt")); - Assert.assertNotNull(jsonNode.get("mqtts")); - Assert.assertNotNull(jsonNode.get("coap")); - Assert.assertNotNull(jsonNode.get("coaps")); + Assert.assertNotNull(jsonNode.get("tokenExpirationTime")); + Assert.assertNotNull(jsonNode.get("refreshTokenExpTime")); + Assert.assertNotNull(jsonNode.get("tokenIssuer")); + Assert.assertNotNull(jsonNode.get("tokenSigningKey")); + } + + private void validateJwtAdminSettings(AdminSettings adminSettings) { + Assert.assertNotNull(adminSettings.getJsonValue().get("baseUrl")); } private void validateAssetProfiles(int expectedMsgCnt) throws Exception { diff --git a/application/src/test/java/org/thingsboard/server/edge/AssetProfileEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/AssetProfileEdgeTest.java index 7c93a4440d..7ffff29ed7 100644 --- a/application/src/test/java/org/thingsboard/server/edge/AssetProfileEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/AssetProfileEdgeTest.java @@ -16,7 +16,6 @@ package org.thingsboard.server.edge; import com.google.protobuf.AbstractMessage; -import com.google.protobuf.ByteString; import org.junit.Assert; import org.junit.Test; import org.thingsboard.common.util.JacksonUtil; @@ -31,7 +30,6 @@ import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.UplinkMsg; import org.thingsboard.server.gen.edge.v1.UplinkResponseMsg; -import java.nio.charset.StandardCharsets; import java.util.Optional; import java.util.UUID; diff --git a/common/edge-api/src/main/proto/edge.proto b/common/edge-api/src/main/proto/edge.proto index 7d5c57928f..b42b3f08fa 100644 --- a/common/edge-api/src/main/proto/edge.proto +++ b/common/edge-api/src/main/proto/edge.proto @@ -311,7 +311,7 @@ message AlarmUpdateMsg { string name = 4 [deprecated = true]; string type = 5 [deprecated = true]; string originatorType = 6 [deprecated = true]; - string originatorName = 7; + string originatorName = 7 [deprecated = true]; string severity = 8 [deprecated = true]; string status = 9 [deprecated = true]; int64 startTs = 10 [deprecated = true]; diff --git a/common/util/src/test/java/org/thingsboard/common/util/JacksonUtilTest.java b/common/util/src/test/java/org/thingsboard/common/util/JacksonUtilTest.java index 808fb22834..229ba4187d 100644 --- a/common/util/src/test/java/org/thingsboard/common/util/JacksonUtilTest.java +++ b/common/util/src/test/java/org/thingsboard/common/util/JacksonUtilTest.java @@ -15,7 +15,6 @@ */ package org.thingsboard.common.util; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.Assert; @@ -28,7 +27,7 @@ import java.util.UUID; public class JacksonUtilTest { @Test - public void allow_unquoted_field_mapper_test() { + public void allowUnquotedFieldMapperTest() { String data = "{data: 123}"; JsonNode actualResult = JacksonUtil.toJsonNode(data, JacksonUtil.ALLOW_UNQUOTED_FIELD_NAMES_MAPPER); // should be: {"data": 123} ObjectNode expectedResult = JacksonUtil.newObjectNode(); @@ -38,14 +37,15 @@ public class JacksonUtilTest { } @Test - public void fail_on_unknown_properties_edge_mapper_test() throws JsonProcessingException { + public void failOnUnknownPropertiesMapperTest() { Asset asset = new Asset(); asset.setId(new AssetId(UUID.randomUUID())); asset.setName("Test"); asset.setType("type"); String serializedAsset = JacksonUtil.toString(asset); - JsonNode jsonNode = JacksonUtil.IGNORE_UNKNOWN_PROPERTIES_JSON_MAPPER.readTree(serializedAsset); + JsonNode jsonNode = JacksonUtil.toJsonNode(serializedAsset); // case: add new field to serialized Asset string and check for backward compatibility with original Asset object + Assert.assertNotNull(jsonNode); ((ObjectNode) jsonNode).put("test", (String) null); serializedAsset = JacksonUtil.toString(jsonNode); // deserialize with FAIL_ON_UNKNOWN_PROPERTIES = false From ed285338d246dfe2297df835cd3952092d0374af Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Tue, 17 Oct 2023 16:07:45 +0300 Subject: [PATCH 113/252] Minor refactoring of RuleChainMetadataConstructor --- .../rpc/constructor/AlarmMsgConstructor.java | 5 ++--- .../rpc/constructor/AssetMsgConstructor.java | 2 +- .../AssetProfileMsgConstructor.java | 2 +- .../constructor/CustomerMsgConstructor.java | 2 +- .../constructor/DashboardMsgConstructor.java | 3 +-- .../rpc/constructor/DeviceMsgConstructor.java | 4 ++-- .../DeviceProfileMsgConstructor.java | 3 +-- .../constructor/EntityViewMsgConstructor.java | 2 +- .../constructor/OtaPackageMsgConstructor.java | 3 +-- .../rpc/constructor/QueueMsgConstructor.java | 3 +-- .../constructor/RuleChainMsgConstructor.java | 2 +- .../rpc/constructor/TenantMsgConstructor.java | 2 +- .../rpc/constructor/UserMsgConstructor.java | 4 ++-- .../WidgetsBundleMsgConstructor.java | 2 +- .../AbstractRuleChainMetadataConstructor.java | 19 +++++++------------ .../processor/alarm/AlarmEdgeProcessor.java | 1 - .../processor/alarm/BaseAlarmProcessor.java | 6 +++--- .../processor/asset/BaseAssetProcessor.java | 6 +++--- .../asset/BaseAssetProfileProcessor.java | 8 ++++---- .../dashboard/BaseDashboardProcessor.java | 4 ++-- .../processor/device/BaseDeviceProcessor.java | 6 +++--- .../entityview/BaseEntityViewProcessor.java | 6 +++--- .../server/edge/AlarmEdgeTest.java | 4 ---- .../server/edge/TenantProfileEdgeTest.java | 13 ++++++++----- common/edge-api/src/main/proto/edge.proto | 2 +- 25 files changed, 51 insertions(+), 63 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AlarmMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AlarmMsgConstructor.java index f9b145ec99..ef2a353e8e 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AlarmMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AlarmMsgConstructor.java @@ -29,11 +29,11 @@ import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; public class AlarmMsgConstructor { public AlarmUpdateMsg constructAlarmUpdatedMsg(UpdateMsgType msgType, Alarm alarm, String entityName, EdgeVersion edgeVersion) { - if (EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion)) { + if (EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)) { return constructDeprecatedAlarmUpdatedMsg(msgType, alarm, entityName); } return AlarmUpdateMsg.newBuilder().setMsgType(msgType) - .setEntity(JacksonUtil.toString(alarm)).setOriginatorName(entityName) + .setEntity(JacksonUtil.toString(alarm)) .setIdMSB(alarm.getId().getId().getMostSignificantBits()) .setIdLSB(alarm.getId().getId().getLeastSignificantBits()).build(); } @@ -59,5 +59,4 @@ public class AlarmMsgConstructor { .setPropagateToTenant(alarm.isPropagateToTenant()); return builder.build(); } - } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetMsgConstructor.java index 72235be45d..f46ec7ab2d 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetMsgConstructor.java @@ -30,7 +30,7 @@ import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; public class AssetMsgConstructor { public AssetUpdateMsg constructAssetUpdatedMsg(UpdateMsgType msgType, Asset asset, EdgeVersion edgeVersion) { - if (EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion)) { + if (EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)) { return constructDeprecatedAssetUpdateMsg(msgType, asset); } return AssetUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(asset)) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetProfileMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetProfileMsgConstructor.java index b140e4f529..f58271917f 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetProfileMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetProfileMsgConstructor.java @@ -33,7 +33,7 @@ import java.nio.charset.StandardCharsets; public class AssetProfileMsgConstructor { public AssetProfileUpdateMsg constructAssetProfileUpdatedMsg(UpdateMsgType msgType, AssetProfile assetProfile, EdgeVersion edgeVersion) { - if (EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion)) { + if (EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)) { return constructDeprecatedAssetProfileUpdatedMsg(msgType, assetProfile); } return AssetProfileUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(assetProfile)) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/CustomerMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/CustomerMsgConstructor.java index e84978383e..cbfb9004eb 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/CustomerMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/CustomerMsgConstructor.java @@ -30,7 +30,7 @@ import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; public class CustomerMsgConstructor { public CustomerUpdateMsg constructCustomerUpdatedMsg(UpdateMsgType msgType, Customer customer, EdgeVersion edgeVersion) { - if (EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion)) { + if (EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)) { return constructDeprecatedCustomerUpdatedMsg(msgType, customer); } return CustomerUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(customer)) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DashboardMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DashboardMsgConstructor.java index 351b3d8e57..82f83a32ff 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DashboardMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DashboardMsgConstructor.java @@ -30,7 +30,7 @@ import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; public class DashboardMsgConstructor { public DashboardUpdateMsg constructDashboardUpdatedMsg(UpdateMsgType msgType, Dashboard dashboard, EdgeVersion edgeVersion) { - if (EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion)) { + if (EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)) { return constructDeprecatedDashboardUpdatedMsg(msgType, dashboard); } return DashboardUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(dashboard)) @@ -64,5 +64,4 @@ public class DashboardMsgConstructor { .setIdMSB(dashboardId.getId().getMostSignificantBits()) .setIdLSB(dashboardId.getId().getLeastSignificantBits()).build(); } - } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceMsgConstructor.java index 15c221e608..bbdfc098c5 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceMsgConstructor.java @@ -44,7 +44,7 @@ public class DeviceMsgConstructor { private DataDecodingEncodingService dataDecodingEncodingService; public DeviceUpdateMsg constructDeviceUpdatedMsg(UpdateMsgType msgType, Device device, EdgeVersion edgeVersion) { - if (EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion)) { + if (EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)) { return constructDeprecatedDeviceUpdateMsg(msgType, device); } return DeviceUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(device)) @@ -88,7 +88,7 @@ public class DeviceMsgConstructor { } public DeviceCredentialsUpdateMsg constructDeviceCredentialsUpdatedMsg(DeviceCredentials deviceCredentials, EdgeVersion edgeVersion) { - return EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion) + return EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion) ? constructDeprecatedDeviceCredentialsUpdatedMsg(deviceCredentials) : DeviceCredentialsUpdateMsg.newBuilder().setEntity(JacksonUtil.toString(deviceCredentials)).build(); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceProfileMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceProfileMsgConstructor.java index de6920ee7f..6d3e9da378 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceProfileMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceProfileMsgConstructor.java @@ -38,7 +38,7 @@ public class DeviceProfileMsgConstructor { private DataDecodingEncodingService dataDecodingEncodingService; public DeviceProfileUpdateMsg constructDeviceProfileUpdatedMsg(UpdateMsgType msgType, DeviceProfile deviceProfile, EdgeVersion edgeVersion) { - if (EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion)) { + if (EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)) { return constructDeprecatedDeviceProfileUpdatedMsg(msgType, deviceProfile); } return DeviceProfileUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(deviceProfile)) @@ -98,5 +98,4 @@ public class DeviceProfileMsgConstructor { .setIdMSB(deviceProfileId.getId().getMostSignificantBits()) .setIdLSB(deviceProfileId.getId().getLeastSignificantBits()).build(); } - } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityViewMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityViewMsgConstructor.java index 96db358811..bfa69459cc 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityViewMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityViewMsgConstructor.java @@ -32,7 +32,7 @@ import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; public class EntityViewMsgConstructor { public EntityViewUpdateMsg constructEntityViewUpdatedMsg(UpdateMsgType msgType, EntityView entityView, EdgeVersion edgeVersion) { - if (EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion)) { + if (EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)) { return constructDeprecatedEntityViewUpdatedMsg(msgType, entityView); } return EntityViewUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(entityView)) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/OtaPackageMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/OtaPackageMsgConstructor.java index cf90f25563..a626574d92 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/OtaPackageMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/OtaPackageMsgConstructor.java @@ -31,7 +31,7 @@ import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; public class OtaPackageMsgConstructor { public OtaPackageUpdateMsg constructOtaPackageUpdatedMsg(UpdateMsgType msgType, OtaPackage otaPackage, EdgeVersion edgeVersion) { - if (EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion)) { + if (EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)) { return constructDeprecatedOtaPackageUpdatedMsg(msgType, otaPackage); } return OtaPackageUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(otaPackage)) @@ -87,5 +87,4 @@ public class OtaPackageMsgConstructor { .setIdMSB(otaPackageId.getId().getMostSignificantBits()) .setIdLSB(otaPackageId.getId().getLeastSignificantBits()).build(); } - } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/QueueMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/QueueMsgConstructor.java index 6c9f9f441a..d1901cdf83 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/QueueMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/QueueMsgConstructor.java @@ -34,7 +34,7 @@ import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; public class QueueMsgConstructor { public QueueUpdateMsg constructQueueUpdatedMsg(UpdateMsgType msgType, Queue queue, EdgeVersion edgeVersion) { - if (EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion)) { + if (EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)) { return constructDeprecatedQueueUpdatedMsg(msgType, queue); } return QueueUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(queue)) @@ -83,5 +83,4 @@ public class QueueMsgConstructor { .setIdMSB(queueId.getId().getMostSignificantBits()) .setIdLSB(queueId.getId().getLeastSignificantBits()).build(); } - } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java index 15fe10618a..202f726ede 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java @@ -38,7 +38,7 @@ public class RuleChainMsgConstructor { public RuleChainUpdateMsg constructRuleChainUpdatedMsg(UpdateMsgType msgType, RuleChain ruleChain, boolean isRoot, EdgeVersion edgeVersion) { ruleChain.setRoot(isRoot); - if (EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion)) { + if (EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)) { return constructDeprecatedRuleChainUpdatedMsg(msgType, ruleChain); } return RuleChainUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(ruleChain)) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/TenantMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/TenantMsgConstructor.java index 6f6c4149ed..93bf46899e 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/TenantMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/TenantMsgConstructor.java @@ -29,7 +29,7 @@ import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; public class TenantMsgConstructor { public TenantUpdateMsg constructTenantUpdateMsg(UpdateMsgType msgType, Tenant tenant, EdgeVersion edgeVersion) { - if (EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion)) { + if (EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)) { return constructDeprecatedTenantUpdateMsg(msgType, tenant); } return TenantUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(tenant)).build(); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/UserMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/UserMsgConstructor.java index 1f39f74150..43771a77c0 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/UserMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/UserMsgConstructor.java @@ -32,7 +32,7 @@ import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; public class UserMsgConstructor { public UserUpdateMsg constructUserUpdatedMsg(UpdateMsgType msgType, User user, EdgeVersion edgeVersion) { - if (EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion)) { + if (EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)) { return constructDeprecatedUserUpdatedMsg(msgType, user); } return UserUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(user)) @@ -71,7 +71,7 @@ public class UserMsgConstructor { } public UserCredentialsUpdateMsg constructUserCredentialsUpdatedMsg(UserCredentials userCredentials, EdgeVersion edgeVersion) { - return EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion) + return EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion) ? constructDeprecatedUserCredentialsUpdatedMsg(userCredentials) : UserCredentialsUpdateMsg.newBuilder().setEntity(JacksonUtil.toString(userCredentials)).build(); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetsBundleMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetsBundleMsgConstructor.java index 16e9d58ad6..4d7e7be2f9 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetsBundleMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetsBundleMsgConstructor.java @@ -35,7 +35,7 @@ import java.util.List; public class WidgetsBundleMsgConstructor { public WidgetsBundleUpdateMsg constructWidgetsBundleUpdateMsg(UpdateMsgType msgType, WidgetsBundle widgetsBundle, List widgets, EdgeVersion edgeVersion) { - if (EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion)) { + if (EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)) { return constructDeprecatedWidgetsBundleUpdateMsg(msgType, widgetsBundle, widgets); } return WidgetsBundleUpdateMsg.newBuilder().setWidgets(JacksonUtil.toString(widgets)).setEntity(JacksonUtil.toString(widgetsBundle)) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/AbstractRuleChainMetadataConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/AbstractRuleChainMetadataConstructor.java index 997613a42a..eaab137301 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/AbstractRuleChainMetadataConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/AbstractRuleChainMetadataConstructor.java @@ -45,7 +45,7 @@ public abstract class AbstractRuleChainMetadataConstructor implements RuleChainM UpdateMsgType msgType, RuleChainMetaData ruleChainMetaData, EdgeVersion edgeVersion) { - return EdgeVersionUtils.isEdgeProtoDeprecated(edgeVersion) + return EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion) ? constructDeprecatedRuleChainMetadataUpdatedMsg(tenantId, msgType, ruleChainMetaData) : RuleChainMetadataUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(ruleChainMetaData)).build(); } @@ -53,17 +53,12 @@ public abstract class AbstractRuleChainMetadataConstructor implements RuleChainM private RuleChainMetadataUpdateMsg constructDeprecatedRuleChainMetadataUpdatedMsg(TenantId tenantId, UpdateMsgType msgType, RuleChainMetaData ruleChainMetaData) { - try { - RuleChainMetadataUpdateMsg.Builder builder = RuleChainMetadataUpdateMsg.newBuilder(); - builder.setRuleChainIdMSB(ruleChainMetaData.getRuleChainId().getId().getMostSignificantBits()) - .setRuleChainIdLSB(ruleChainMetaData.getRuleChainId().getId().getLeastSignificantBits()); - constructRuleChainMetadataUpdatedMsg(tenantId, builder, ruleChainMetaData); - builder.setMsgType(msgType); - return builder.build(); - } catch (Exception ex) { - log.error("[{}] Can't construct RuleChainMetadataUpdateMsg", tenantId, ex); - } - return null; + RuleChainMetadataUpdateMsg.Builder builder = RuleChainMetadataUpdateMsg.newBuilder(); + builder.setRuleChainIdMSB(ruleChainMetaData.getRuleChainId().getId().getMostSignificantBits()) + .setRuleChainIdLSB(ruleChainMetaData.getRuleChainId().getId().getLeastSignificantBits()); + constructRuleChainMetadataUpdatedMsg(tenantId, builder, ruleChainMetaData); + builder.setMsgType(msgType); + return builder.build(); } protected abstract void constructRuleChainMetadataUpdatedMsg(TenantId tenantId, diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/AlarmEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/AlarmEdgeProcessor.java index 1a06f91149..b934a33d54 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/AlarmEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/AlarmEdgeProcessor.java @@ -123,5 +123,4 @@ public class AlarmEdgeProcessor extends BaseAlarmProcessor { } while (pageData != null && pageData.hasNext()); return futures; } - } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java index e81b7916f6..8f085f7323 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java @@ -50,13 +50,13 @@ public abstract class BaseAlarmProcessor extends BaseEdgeProcessor { public ListenableFuture processAlarmMsg(TenantId tenantId, AlarmUpdateMsg alarmUpdateMsg, EdgeVersion edgeVersion) { log.trace("[{}] processAlarmMsg [{}]", tenantId, alarmUpdateMsg); AlarmId alarmId = new AlarmId(new UUID(alarmUpdateMsg.getIdMSB(), alarmUpdateMsg.getIdLSB())); - boolean isEdgeProtoDeprecated = EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion); - Alarm alarm = isEdgeProtoDeprecated ? createDeprecatedAlarm(tenantId, alarmUpdateMsg) + boolean isEdgeVersionOlderThan_3_6_2 = EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion); + Alarm alarm = isEdgeVersionOlderThan_3_6_2 ? createDeprecatedAlarm(tenantId, alarmUpdateMsg) : JacksonUtil.fromStringIgnoreUnknownProperties(alarmUpdateMsg.getEntity(), Alarm.class); if (alarm == null) { throw new RuntimeException("[{" + tenantId + "}] alarmUpdateMsg {" + alarmUpdateMsg + "} cannot be converted to alarm"); } - EntityId originatorId = isEdgeProtoDeprecated + EntityId originatorId = isEdgeVersionOlderThan_3_6_2 ? getAlarmOriginator(tenantId, alarmUpdateMsg.getOriginatorName(), EntityType.valueOf(alarmUpdateMsg.getOriginatorType())) : alarm.getOriginator(); if (originatorId == null) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProcessor.java index 46a58588d7..916083a362 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProcessor.java @@ -33,12 +33,12 @@ import java.util.UUID; @Slf4j public abstract class BaseAssetProcessor extends BaseEdgeProcessor { - protected Pair saveOrUpdateAsset(TenantId tenantId, AssetId assetId, AssetUpdateMsg assetUpdateMsg, boolean isEdgeProtoDeprecated) { + protected Pair saveOrUpdateAsset(TenantId tenantId, AssetId assetId, AssetUpdateMsg assetUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2) { boolean created = false; boolean assetNameUpdated = false; assetCreationLock.lock(); try { - Asset asset = isEdgeProtoDeprecated + Asset asset = isEdgeVersionOlderThan_3_6_2 ? createAsset(tenantId, assetId, assetUpdateMsg) : JacksonUtil.fromStringIgnoreUnknownProperties(assetUpdateMsg.getEntity(), Asset.class); if (asset == null) { @@ -60,7 +60,7 @@ public abstract class BaseAssetProcessor extends BaseEdgeProcessor { assetNameUpdated = true; } asset.setName(assetName); - setCustomerId(tenantId, created ? null : assetById.getCustomerId(), asset, assetUpdateMsg, isEdgeProtoDeprecated); + setCustomerId(tenantId, created ? null : assetById.getCustomerId(), asset, assetUpdateMsg, isEdgeVersionOlderThan_3_6_2); assetValidator.validate(asset, Asset::getTenantId); if (created) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProfileProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProfileProcessor.java index 2712881a30..4d106eeacb 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProfileProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProfileProcessor.java @@ -33,12 +33,12 @@ import java.nio.charset.StandardCharsets; @Slf4j public abstract class BaseAssetProfileProcessor extends BaseEdgeProcessor { - protected Pair saveOrUpdateAssetProfile(TenantId tenantId, AssetProfileId assetProfileId, AssetProfileUpdateMsg assetProfileUpdateMsg, boolean isEdgeProtoDeprecated) { + protected Pair saveOrUpdateAssetProfile(TenantId tenantId, AssetProfileId assetProfileId, AssetProfileUpdateMsg assetProfileUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2) { boolean created = false; boolean assetProfileNameUpdated = false; assetCreationLock.lock(); try { - AssetProfile assetProfile = isEdgeProtoDeprecated + AssetProfile assetProfile = isEdgeVersionOlderThan_3_6_2 ? createAssetProfile(tenantId, assetProfileId, assetProfileUpdateMsg) : JacksonUtil.fromStringIgnoreUnknownProperties(assetProfileUpdateMsg.getEntity(), AssetProfile.class); if (assetProfile == null) { @@ -63,8 +63,8 @@ public abstract class BaseAssetProfileProcessor extends BaseEdgeProcessor { RuleChainId ruleChainId = assetProfile.getDefaultRuleChainId(); setDefaultRuleChainId(tenantId, assetProfile, created ? null : assetProfileById.getDefaultRuleChainId()); - setDefaultEdgeRuleChainId(assetProfile, ruleChainId, assetProfileUpdateMsg, isEdgeProtoDeprecated); - setDefaultDashboardId(tenantId, created ? null : assetProfileById.getDefaultDashboardId(), assetProfile, assetProfileUpdateMsg, isEdgeProtoDeprecated); + setDefaultEdgeRuleChainId(assetProfile, ruleChainId, assetProfileUpdateMsg, isEdgeVersionOlderThan_3_6_2); + setDefaultDashboardId(tenantId, created ? null : assetProfileById.getDefaultDashboardId(), assetProfile, assetProfileUpdateMsg, isEdgeVersionOlderThan_3_6_2); assetProfileValidator.validate(assetProfile, AssetProfile::getTenantId); if (created) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/BaseDashboardProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/BaseDashboardProcessor.java index 823de9a58b..46f4088f41 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/BaseDashboardProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/BaseDashboardProcessor.java @@ -32,9 +32,9 @@ import java.util.Set; @Slf4j public abstract class BaseDashboardProcessor extends BaseEdgeProcessor { - protected boolean saveOrUpdateDashboard(TenantId tenantId, DashboardId dashboardId, DashboardUpdateMsg dashboardUpdateMsg, boolean isEdgeProtoDeprecated, CustomerId customerId) { + protected boolean saveOrUpdateDashboard(TenantId tenantId, DashboardId dashboardId, DashboardUpdateMsg dashboardUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2, CustomerId customerId) { boolean created = false; - Dashboard dashboard = isEdgeProtoDeprecated + Dashboard dashboard = isEdgeVersionOlderThan_3_6_2 ? createDashboard(tenantId, dashboardId, dashboardUpdateMsg) : JacksonUtil.fromStringIgnoreUnknownProperties(dashboardUpdateMsg.getEntity(), Dashboard.class); if (dashboard == null) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProcessor.java index b43843580b..4b03c4d76b 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProcessor.java @@ -46,12 +46,12 @@ public abstract class BaseDeviceProcessor extends BaseEdgeProcessor { @Autowired protected DataDecodingEncodingService dataDecodingEncodingService; - protected Pair saveOrUpdateDevice(TenantId tenantId, DeviceId deviceId, DeviceUpdateMsg deviceUpdateMsg, boolean isEdgeProtoDeprecated) { + protected Pair saveOrUpdateDevice(TenantId tenantId, DeviceId deviceId, DeviceUpdateMsg deviceUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2) { boolean created = false; boolean deviceNameUpdated = false; deviceCreationLock.lock(); try { - Device device = isEdgeProtoDeprecated + Device device = isEdgeVersionOlderThan_3_6_2 ? createDevice(tenantId, deviceId, deviceUpdateMsg) : JacksonUtil.fromStringIgnoreUnknownProperties(deviceUpdateMsg.getEntity(), Device.class); if (device == null) { @@ -73,7 +73,7 @@ public abstract class BaseDeviceProcessor extends BaseEdgeProcessor { deviceNameUpdated = true; } device.setName(deviceName); - setCustomerId(tenantId, created ? null : deviceById.getCustomerId(), device, deviceUpdateMsg, isEdgeProtoDeprecated); + setCustomerId(tenantId, created ? null : deviceById.getCustomerId(), device, deviceUpdateMsg, isEdgeVersionOlderThan_3_6_2); deviceValidator.validate(device, Device::getTenantId); if (created) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/BaseEntityViewProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/BaseEntityViewProcessor.java index b53470b0cb..02bc2b9529 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/BaseEntityViewProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/BaseEntityViewProcessor.java @@ -35,10 +35,10 @@ import java.util.UUID; @Slf4j public abstract class BaseEntityViewProcessor extends BaseEdgeProcessor { - protected Pair saveOrUpdateEntityView(TenantId tenantId, EntityViewId entityViewId, EntityViewUpdateMsg entityViewUpdateMsg, boolean isEdgeProtoDeprecated) { + protected Pair saveOrUpdateEntityView(TenantId tenantId, EntityViewId entityViewId, EntityViewUpdateMsg entityViewUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2) { boolean created = false; boolean entityViewNameUpdated = false; - EntityView entityView = isEdgeProtoDeprecated + EntityView entityView = isEdgeVersionOlderThan_3_6_2 ? createEntityView(tenantId, entityViewId, entityViewUpdateMsg) : JacksonUtil.fromStringIgnoreUnknownProperties(entityViewUpdateMsg.getEntity(), EntityView.class); if (entityView == null) { @@ -60,7 +60,7 @@ public abstract class BaseEntityViewProcessor extends BaseEdgeProcessor { entityViewNameUpdated = true; } entityView.setName(entityViewName); - setCustomerId(tenantId, created ? null : entityViewById.getCustomerId(), entityView, entityViewUpdateMsg, isEdgeProtoDeprecated); + setCustomerId(tenantId, created ? null : entityViewById.getCustomerId(), entityView, entityViewUpdateMsg, isEdgeVersionOlderThan_3_6_2); entityViewValidator.validate(entityView, EntityView::getTenantId); if (created) { diff --git a/application/src/test/java/org/thingsboard/server/edge/AlarmEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/AlarmEdgeTest.java index 634b16e248..804d2f6807 100644 --- a/application/src/test/java/org/thingsboard/server/edge/AlarmEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/AlarmEdgeTest.java @@ -54,7 +54,6 @@ public class AlarmEdgeTest extends AbstractEdgeTest { alarmUpdateMgBuilder.setIdMSB(edgeAlarm.getUuidId().getMostSignificantBits()); alarmUpdateMgBuilder.setIdLSB(edgeAlarm.getUuidId().getLeastSignificantBits()); alarmUpdateMgBuilder.setEntity(JacksonUtil.toString(edgeAlarm)); - alarmUpdateMgBuilder.setOriginatorName(device.getName()); testAutoGeneratedCodeByProtobuf(alarmUpdateMgBuilder); uplinkMsgBuilder.addAlarmUpdateMsg(alarmUpdateMgBuilder.build()); @@ -108,7 +107,6 @@ public class AlarmEdgeTest extends AbstractEdgeTest { Assert.assertNotNull(alarmMsg); Assert.assertEquals(savedAlarm.getType(), alarmMsg.getType()); Assert.assertEquals(savedAlarm.getName(), alarmMsg.getName()); - Assert.assertEquals(device.getName(), alarmUpdateMsg.getOriginatorName()); Assert.assertEquals(AlarmStatus.ACTIVE_ACK, alarmMsg.getStatus()); // clear alarm @@ -123,7 +121,6 @@ public class AlarmEdgeTest extends AbstractEdgeTest { Assert.assertNotNull(alarmMsg); Assert.assertEquals(savedAlarm.getType(), alarmMsg.getType()); Assert.assertEquals(savedAlarm.getName(), alarmMsg.getName()); - Assert.assertEquals(device.getName(), alarmUpdateMsg.getOriginatorName()); Assert.assertEquals(AlarmStatus.CLEARED_ACK, alarmMsg.getStatus()); // delete alarm @@ -139,7 +136,6 @@ public class AlarmEdgeTest extends AbstractEdgeTest { Assert.assertNotNull(alarmMsg); Assert.assertEquals(savedAlarm.getType(), alarmMsg.getType()); Assert.assertEquals(savedAlarm.getName(), alarmMsg.getName()); - Assert.assertEquals(device.getName(), alarmUpdateMsg.getOriginatorName()); Assert.assertEquals(AlarmStatus.CLEARED_ACK, alarmMsg.getStatus()); } diff --git a/application/src/test/java/org/thingsboard/server/edge/TenantProfileEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/TenantProfileEdgeTest.java index 7320a73a08..e958325410 100644 --- a/application/src/test/java/org/thingsboard/server/edge/TenantProfileEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/TenantProfileEdgeTest.java @@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.queue.ProcessingStrategy; import org.thingsboard.server.common.data.queue.ProcessingStrategyType; +import org.thingsboard.server.common.data.queue.Queue; import org.thingsboard.server.common.data.queue.SubmitStrategy; import org.thingsboard.server.common.data.queue.SubmitStrategyType; import org.thingsboard.server.common.data.tenant.profile.TenantProfileQueueConfiguration; @@ -84,10 +85,11 @@ public class TenantProfileEdgeTest extends AbstractEdgeTest { Optional tenantProfileUpdateMsgOpt = edgeImitator.findMessageByType(TenantProfileUpdateMsg.class); Assert.assertTrue(tenantProfileUpdateMsgOpt.isPresent()); TenantProfileUpdateMsg tenantProfileUpdateMsg = tenantProfileUpdateMsgOpt.get(); + TenantProfile tenantProfile = JacksonUtil.fromStringIgnoreUnknownProperties(tenantProfileUpdateMsg.getEntity(), TenantProfile.class); + Assert.assertNotNull(tenantProfile); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, tenantProfileUpdateMsg.getMsgType()); - Assert.assertEquals(edgeTenantProfile.getUuidId().getMostSignificantBits(), tenantProfileUpdateMsg.getIdMSB()); - Assert.assertEquals(edgeTenantProfile.getUuidId().getLeastSignificantBits(), tenantProfileUpdateMsg.getIdLSB()); - Assert.assertEquals(edgeTenantProfile.getDescription(), tenantProfileUpdateMsg.getDescription()); + Assert.assertEquals(edgeTenantProfile.getId(), tenantProfile.getId()); + Assert.assertEquals(edgeTenantProfile.getDescription(), tenantProfile.getDescription()); List queueUpdateMsgs = edgeImitator.findAllMessagesByType(QueueUpdateMsg.class); Assert.assertEquals(2, queueUpdateMsgs.size()); @@ -104,8 +106,9 @@ public class TenantProfileEdgeTest extends AbstractEdgeTest { queueUpdateMsgs = edgeImitator.findAllMessagesByType(QueueUpdateMsg.class); Assert.assertEquals(2, queueUpdateMsgs.size()); for (QueueUpdateMsg queueUpdateMsg : queueUpdateMsgs) { - Assert.assertEquals(tenantId.getId().getMostSignificantBits(), queueUpdateMsg.getTenantIdMSB()); - Assert.assertEquals(tenantId.getId().getLeastSignificantBits(), queueUpdateMsg.getTenantIdLSB()); + Queue queue = JacksonUtil.fromStringIgnoreUnknownProperties(queueUpdateMsg.getEntity(), Queue.class); + Assert.assertNotNull(queue); + Assert.assertEquals(tenantId, queue.getTenantId()); } } diff --git a/common/edge-api/src/main/proto/edge.proto b/common/edge-api/src/main/proto/edge.proto index b42b3f08fa..a7df1e41fb 100644 --- a/common/edge-api/src/main/proto/edge.proto +++ b/common/edge-api/src/main/proto/edge.proto @@ -397,7 +397,7 @@ message WidgetTypeUpdateMsg { optional string description = 10 [deprecated = true]; optional string fqn = 11 [deprecated = true]; bool deprecated = 12 [deprecated = true]; - repeated string tags = 13; + repeated string tags = 13 [deprecated = true]; string entity = 14; } From 442fe9029e41a6be4a5168b46e56942c93342c93 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Mon, 13 Nov 2023 13:14:37 +0200 Subject: [PATCH 114/252] added env variable to regulate pubsud queue executor threads pool size --- application/src/main/resources/thingsboard.yml | 2 ++ .../server/queue/pubsub/TbPubSubProducerTemplate.java | 10 +++++++++- .../server/queue/pubsub/TbPubSubSettings.java | 3 +++ msa/vc-executor/src/main/resources/tb-vc-executor.yml | 2 ++ .../coap/src/main/resources/tb-coap-transport.yml | 2 ++ .../http/src/main/resources/tb-http-transport.yml | 2 ++ .../lwm2m/src/main/resources/tb-lwm2m-transport.yml | 2 ++ .../mqtt/src/main/resources/tb-mqtt-transport.yml | 2 ++ .../snmp/src/main/resources/tb-snmp-transport.yml | 2 ++ 9 files changed, 26 insertions(+), 1 deletion(-) diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index bdf0ad8252..e292237da5 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -1411,6 +1411,8 @@ queue: max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" # Number of messages per consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" + # Number of threads of pubsub executor provider + executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_SIZE:50}" queue-properties: # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consumed again rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubProducerTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubProducerTemplate.java index 0d84a8f839..56e21da3db 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubProducerTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubProducerTemplate.java @@ -18,6 +18,7 @@ package org.thingsboard.server.queue.pubsub; import com.google.api.core.ApiFuture; import com.google.api.core.ApiFutureCallback; import com.google.api.core.ApiFutures; +import com.google.api.gax.core.InstantiatingExecutorProvider; import com.google.cloud.pubsub.v1.Publisher; import com.google.gson.Gson; import com.google.protobuf.ByteString; @@ -120,7 +121,14 @@ public class TbPubSubProducerTemplate implements TbQueuePr try { admin.createTopicIfNotExists(topic); ProjectTopicName topicName = ProjectTopicName.of(pubSubSettings.getProjectId(), topic); - Publisher publisher = Publisher.newBuilder(topicName).setCredentialsProvider(pubSubSettings.getCredentialsProvider()).build(); + InstantiatingExecutorProvider executorProvider = + InstantiatingExecutorProvider.newBuilder() + .setExecutorThreadCount(pubSubSettings.getExecutorThreadPoolSize()) + .build(); + Publisher publisher = Publisher.newBuilder(topicName) + .setCredentialsProvider(pubSubSettings.getCredentialsProvider()) + .setExecutorProvider(executorProvider) + .build(); publisherMap.put(topic, publisher); return publisher; } catch (IOException e) { diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubSettings.java b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubSettings.java index fe9d85c306..b1b095f23c 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubSettings.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubSettings.java @@ -46,6 +46,9 @@ public class TbPubSubSettings { @Value("${queue.pubsub.max_messages}") private int maxMessages; + @Value("${queue.pubsub.executor_thread_pool_size}") + private int executorThreadPoolSize; + private CredentialsProvider credentialsProvider; @PostConstruct diff --git a/msa/vc-executor/src/main/resources/tb-vc-executor.yml b/msa/vc-executor/src/main/resources/tb-vc-executor.yml index 4ffa1ab702..cfbd61eda5 100644 --- a/msa/vc-executor/src/main/resources/tb-vc-executor.yml +++ b/msa/vc-executor/src/main/resources/tb-vc-executor.yml @@ -172,6 +172,8 @@ queue: max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" #in bytes # Number of messages per consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" + # Number of threads of pubsub executor provider + executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_SIZE:50}" queue-properties: # Pub/Sub properties for Core subscribers, messages which will commit after ackDeadlineInSec period can be consumed again core: "${TB_QUEUE_PUBSUB_CORE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" diff --git a/transport/coap/src/main/resources/tb-coap-transport.yml b/transport/coap/src/main/resources/tb-coap-transport.yml index 437f551690..be54ed34cf 100644 --- a/transport/coap/src/main/resources/tb-coap-transport.yml +++ b/transport/coap/src/main/resources/tb-coap-transport.yml @@ -295,6 +295,8 @@ queue: max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" # Number of messages per consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" + # Number of threads of pubsub executor provider + executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_SIZE:50}" queue-properties: # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consumed again rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" diff --git a/transport/http/src/main/resources/tb-http-transport.yml b/transport/http/src/main/resources/tb-http-transport.yml index 8d4d6fadf0..cf2c49b846 100644 --- a/transport/http/src/main/resources/tb-http-transport.yml +++ b/transport/http/src/main/resources/tb-http-transport.yml @@ -278,6 +278,8 @@ queue: max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" # Number of messages per a consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" + # Number of threads of pubsub executor provider + executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_SIZE:50}" queue-properties: # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consume again rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" diff --git a/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml b/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml index 8fd1c11e6e..9884b33390 100644 --- a/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml +++ b/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml @@ -374,6 +374,8 @@ queue: max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" # Number of messages per consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" + # Number of threads of pubsub executor provider + executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_SIZE:50}" queue-properties: # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consumed again rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" diff --git a/transport/mqtt/src/main/resources/tb-mqtt-transport.yml b/transport/mqtt/src/main/resources/tb-mqtt-transport.yml index 6c51d18ab7..0c26caa97e 100644 --- a/transport/mqtt/src/main/resources/tb-mqtt-transport.yml +++ b/transport/mqtt/src/main/resources/tb-mqtt-transport.yml @@ -311,6 +311,8 @@ queue: max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" # Number of messages per consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" + # Number of threads of pubsub executor provider + executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_SIZE:50}" queue-properties: # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consumed again rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" diff --git a/transport/snmp/src/main/resources/tb-snmp-transport.yml b/transport/snmp/src/main/resources/tb-snmp-transport.yml index eeb6807714..5e4dc06275 100644 --- a/transport/snmp/src/main/resources/tb-snmp-transport.yml +++ b/transport/snmp/src/main/resources/tb-snmp-transport.yml @@ -264,6 +264,8 @@ queue: max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" # Number of messages per consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" + # Number of threads of pubsub executor provider + executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_SIZE:50}" queue-properties: # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consumed again rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" From ccb7b947009b0da2076b7faa8bf1c212cff066c7 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Mon, 13 Nov 2023 13:17:19 +0200 Subject: [PATCH 115/252] refactoring --- application/src/main/resources/thingsboard.yml | 2 +- msa/vc-executor/src/main/resources/tb-vc-executor.yml | 2 +- transport/coap/src/main/resources/tb-coap-transport.yml | 2 +- transport/http/src/main/resources/tb-http-transport.yml | 2 +- transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml | 2 +- transport/mqtt/src/main/resources/tb-mqtt-transport.yml | 2 +- transport/snmp/src/main/resources/tb-snmp-transport.yml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index e292237da5..5c2a62a79a 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -1412,7 +1412,7 @@ queue: # Number of messages per consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" # Number of threads of pubsub executor provider - executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_SIZE:50}" + executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:50}" queue-properties: # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consumed again rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" diff --git a/msa/vc-executor/src/main/resources/tb-vc-executor.yml b/msa/vc-executor/src/main/resources/tb-vc-executor.yml index cfbd61eda5..63ed58da12 100644 --- a/msa/vc-executor/src/main/resources/tb-vc-executor.yml +++ b/msa/vc-executor/src/main/resources/tb-vc-executor.yml @@ -173,7 +173,7 @@ queue: # Number of messages per consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" # Number of threads of pubsub executor provider - executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_SIZE:50}" + executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:50}" queue-properties: # Pub/Sub properties for Core subscribers, messages which will commit after ackDeadlineInSec period can be consumed again core: "${TB_QUEUE_PUBSUB_CORE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" diff --git a/transport/coap/src/main/resources/tb-coap-transport.yml b/transport/coap/src/main/resources/tb-coap-transport.yml index be54ed34cf..e2c515de58 100644 --- a/transport/coap/src/main/resources/tb-coap-transport.yml +++ b/transport/coap/src/main/resources/tb-coap-transport.yml @@ -296,7 +296,7 @@ queue: # Number of messages per consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" # Number of threads of pubsub executor provider - executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_SIZE:50}" + executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:50}" queue-properties: # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consumed again rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" diff --git a/transport/http/src/main/resources/tb-http-transport.yml b/transport/http/src/main/resources/tb-http-transport.yml index cf2c49b846..820c901603 100644 --- a/transport/http/src/main/resources/tb-http-transport.yml +++ b/transport/http/src/main/resources/tb-http-transport.yml @@ -279,7 +279,7 @@ queue: # Number of messages per a consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" # Number of threads of pubsub executor provider - executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_SIZE:50}" + executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:50}" queue-properties: # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consume again rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" diff --git a/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml b/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml index 9884b33390..f7e7d55545 100644 --- a/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml +++ b/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml @@ -375,7 +375,7 @@ queue: # Number of messages per consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" # Number of threads of pubsub executor provider - executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_SIZE:50}" + executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:50}" queue-properties: # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consumed again rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" diff --git a/transport/mqtt/src/main/resources/tb-mqtt-transport.yml b/transport/mqtt/src/main/resources/tb-mqtt-transport.yml index 0c26caa97e..d67f5f91ba 100644 --- a/transport/mqtt/src/main/resources/tb-mqtt-transport.yml +++ b/transport/mqtt/src/main/resources/tb-mqtt-transport.yml @@ -312,7 +312,7 @@ queue: # Number of messages per consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" # Number of threads of pubsub executor provider - executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_SIZE:50}" + executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:50}" queue-properties: # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consumed again rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" diff --git a/transport/snmp/src/main/resources/tb-snmp-transport.yml b/transport/snmp/src/main/resources/tb-snmp-transport.yml index 5e4dc06275..bdd9aef7aa 100644 --- a/transport/snmp/src/main/resources/tb-snmp-transport.yml +++ b/transport/snmp/src/main/resources/tb-snmp-transport.yml @@ -265,7 +265,7 @@ queue: # Number of messages per consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" # Number of threads of pubsub executor provider - executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_SIZE:50}" + executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:50}" queue-properties: # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consumed again rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" From 6fa3a125c2b9fbf4a92aba5bdaabcfc678c76dcd Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Mon, 13 Nov 2023 13:28:54 +0200 Subject: [PATCH 116/252] Mino refactoring: add json support for resources, fix test and edgeVersion for test --- .../server/service/edge/rpc/EdgeGrpcSession.java | 2 +- .../rpc/constructor/ResourceMsgConstructor.java | 14 +++++++++++++- .../rpc/processor/alarm/BaseAlarmProcessor.java | 10 ---------- .../processor/resource/ResourceEdgeProcessor.java | 5 +++-- .../telemetry/BaseTelemetryProcessor.java | 2 +- .../org/thingsboard/edge/rpc/EdgeGrpcClient.java | 2 +- common/edge-api/src/main/proto/edge.proto | 15 ++++++++------- .../thingsboard/common/util/JacksonUtilTest.java | 2 +- 8 files changed, 28 insertions(+), 24 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java index 277b8a3f34..de5817a7af 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java @@ -647,7 +647,7 @@ public final class EdgeGrpcSession implements Closeable { case OTA_PACKAGE: return ctx.getOtaPackageEdgeProcessor().convertOtaPackageEventToDownlink(edgeEvent, this.edgeVersion); case TB_RESOURCE: - return ctx.getResourceEdgeProcessor().convertResourceEventToDownlink(edgeEvent); + return ctx.getResourceEdgeProcessor().convertResourceEventToDownlink(edgeEvent, this.edgeVersion); case QUEUE: return ctx.getQueueEdgeProcessor().convertQueueEventToDownlink(edgeEvent, this.edgeVersion); case TENANT: diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ResourceMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ResourceMsgConstructor.java index 27b7dc3978..bf9a0e4521 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ResourceMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ResourceMsgConstructor.java @@ -16,18 +16,30 @@ package org.thingsboard.server.service.edge.rpc.constructor; import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.ResourceUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; @Component @TbCoreComponent public class ResourceMsgConstructor { - public ResourceUpdateMsg constructResourceUpdatedMsg(UpdateMsgType msgType, TbResource tbResource) { + public ResourceUpdateMsg constructResourceUpdatedMsg(UpdateMsgType msgType, TbResource tbResource, EdgeVersion edgeVersion) { + if (EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)) { + return constructDeprecatedResourceUpdateMsg(msgType, tbResource); + } + return ResourceUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(tbResource)) + .setIdMSB(tbResource.getId().getId().getMostSignificantBits()) + .setIdLSB(tbResource.getId().getId().getLeastSignificantBits()).build(); + } + + private ResourceUpdateMsg constructDeprecatedResourceUpdateMsg(UpdateMsgType msgType, TbResource tbResource) { ResourceUpdateMsg.Builder builder = ResourceUpdateMsg.newBuilder() .setMsgType(msgType) .setIdMSB(tbResource.getId().getId().getMostSignificantBits()) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java index 8f085f7323..fd53e38f4a 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java @@ -70,16 +70,6 @@ public abstract class BaseAlarmProcessor extends BaseEdgeProcessor { case ENTITY_UPDATED_RPC_MESSAGE: alarm.setId(alarmId); alarm.setOriginator(originatorId); - alarm.setSeverity(AlarmSeverity.valueOf(alarmUpdateMsg.getSeverity())); - alarm.setStartTs(alarmUpdateMsg.getStartTs()); - var alarmStatus = AlarmStatus.valueOf(alarmUpdateMsg.getStatus()); - alarm.setClearTs(alarmUpdateMsg.getClearTs()); - alarm.setPropagate(alarmUpdateMsg.getPropagate()); - alarm.setCleared(alarmStatus.isCleared()); - alarm.setAcknowledged(alarmStatus.isAck()); - alarm.setAckTs(alarmUpdateMsg.getAckTs()); - alarm.setEndTs(alarmUpdateMsg.getEndTs()); - alarm.setDetails(JacksonUtil.toJsonNode(alarmUpdateMsg.getDetails())); if (UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE.equals(alarmUpdateMsg.getMsgType())) { alarmService.createAlarm(AlarmCreateOrUpdateActiveRequest.fromAlarm(alarm, null, alarmId)); } else { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessor.java index ad283826f8..7242d8c01b 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessor.java @@ -29,6 +29,7 @@ import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.ResourceUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; @@ -70,7 +71,7 @@ public class ResourceEdgeProcessor extends BaseResourceProcessor { return Futures.immediateFuture(null); } - public DownlinkMsg convertResourceEventToDownlink(EdgeEvent edgeEvent) { + public DownlinkMsg convertResourceEventToDownlink(EdgeEvent edgeEvent, EdgeVersion edgeVersion) { TbResourceId tbResourceId = new TbResourceId(edgeEvent.getEntityId()); DownlinkMsg downlinkMsg = null; switch (edgeEvent.getAction()) { @@ -80,7 +81,7 @@ public class ResourceEdgeProcessor extends BaseResourceProcessor { if (tbResource != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); ResourceUpdateMsg resourceUpdateMsg = - resourceMsgConstructor.constructResourceUpdatedMsg(msgType, tbResource); + resourceMsgConstructor.constructResourceUpdatedMsg(msgType, tbResource, edgeVersion); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addResourceUpdateMsg(resourceUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/BaseTelemetryProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/BaseTelemetryProcessor.java index 974e9267f6..2eaa69af82 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/BaseTelemetryProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/BaseTelemetryProcessor.java @@ -314,7 +314,7 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor { EntityType entityType, UUID entityUUID, EdgeEventActionType actionType, - JsonNode body) throws JsonProcessingException { + JsonNode body) { EntityId entityId; switch (entityType) { case DEVICE: diff --git a/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeGrpcClient.java b/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeGrpcClient.java index a9b12865a2..973c50c1d2 100644 --- a/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeGrpcClient.java +++ b/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeGrpcClient.java @@ -111,7 +111,7 @@ public class EdgeGrpcClient implements EdgeRpcClient { .setConnectRequestMsg(ConnectRequestMsg.newBuilder() .setEdgeRoutingKey(edgeKey) .setEdgeSecret(edgeSecret) - .setEdgeVersion(EdgeVersion.V_3_6_1) + .setEdgeVersion(EdgeVersion.V_3_6_2) .setMaxInboundMessageSize(maxInboundMessageSize) .build()) .build()); diff --git a/common/edge-api/src/main/proto/edge.proto b/common/edge-api/src/main/proto/edge.proto index a7df1e41fb..18b30e439f 100644 --- a/common/edge-api/src/main/proto/edge.proto +++ b/common/edge-api/src/main/proto/edge.proto @@ -452,13 +452,14 @@ message ResourceUpdateMsg { UpdateMsgType msgType = 1; int64 idMSB = 2; int64 idLSB = 3; - string title = 4; - string resourceType = 5; - string resourceKey = 6; - string fileName = 7; - optional string data = 8; - optional string etag = 9; - bool isSystem = 10; + string title = 4 [deprecated = true]; + string resourceType = 5 [deprecated = true]; + string resourceKey = 6 [deprecated = true]; + string fileName = 7 [deprecated = true]; + optional string data = 8 [deprecated = true]; + optional string etag = 9 [deprecated = true]; + bool isSystem = 10 [deprecated = true]; + string entity = 11; } message RuleChainMetadataRequestMsg { diff --git a/common/util/src/test/java/org/thingsboard/common/util/JacksonUtilTest.java b/common/util/src/test/java/org/thingsboard/common/util/JacksonUtilTest.java index 229ba4187d..57fd457f14 100644 --- a/common/util/src/test/java/org/thingsboard/common/util/JacksonUtilTest.java +++ b/common/util/src/test/java/org/thingsboard/common/util/JacksonUtilTest.java @@ -55,4 +55,4 @@ public class JacksonUtilTest { Assert.assertEquals(asset.getName(), result.getName()); Assert.assertEquals(asset.getType(), result.getType()); } -} \ No newline at end of file +} From 64349f538e0ab742d16c7bcbb300ef5558ab2a04 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Mon, 13 Nov 2023 15:00:23 +0200 Subject: [PATCH 117/252] Fix resourceEdgeTest --- .../service/edge/rpc/EdgeGrpcSession.java | 2 +- .../resource/BaseResourceProcessor.java | 51 +++++++++++------ .../resource/ResourceEdgeProcessor.java | 5 +- .../server/edge/AbstractEdgeTest.java | 14 +++-- .../server/edge/ResourceEdgeTest.java | 57 ++++++++++--------- 5 files changed, 77 insertions(+), 52 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java index de5817a7af..99f126c4a7 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java @@ -715,7 +715,7 @@ public final class EdgeGrpcSession implements Closeable { } if (uplinkMsg.getResourceUpdateMsgCount() > 0) { for (ResourceUpdateMsg resourceUpdateMsg : uplinkMsg.getResourceUpdateMsgList()) { - result.add(ctx.getResourceEdgeProcessor().processResourceMsgFromEdge(edge.getTenantId(), edge, resourceUpdateMsg)); + result.add(ctx.getResourceEdgeProcessor().processResourceMsgFromEdge(edge.getTenantId(), edge, resourceUpdateMsg, this.edgeVersion)); } } if (uplinkMsg.getRuleChainMetadataRequestMsgCount() > 0) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/BaseResourceProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/BaseResourceProcessor.java index a00745d8b7..20a8aa372b 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/BaseResourceProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/BaseResourceProcessor.java @@ -17,6 +17,7 @@ package org.thingsboard.server.service.edge.rpc.processor.resource; import com.datastax.oss.driver.api.core.uuid.Uuids; import lombok.extern.slf4j.Slf4j; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.TbResource; @@ -30,39 +31,37 @@ import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; @Slf4j public abstract class BaseResourceProcessor extends BaseEdgeProcessor { - protected boolean saveOrUpdateTbResource(TenantId tenantId, TbResourceId tbResourceId, ResourceUpdateMsg resourceUpdateMsg) { + protected boolean saveOrUpdateTbResource(TenantId tenantId, TbResourceId tbResourceId, ResourceUpdateMsg resourceUpdateMsg, boolean isEdgeDeprecated) { boolean resourceKeyUpdated = false; try { - boolean created = false; - TbResource resource = resourceService.findResourceById(tenantId, tbResourceId); + TbResource resource = isEdgeDeprecated + ? createTbResource(tenantId, resourceUpdateMsg) + : JacksonUtil.fromStringIgnoreUnknownProperties(resourceUpdateMsg.getEntity(), TbResource.class); if (resource == null) { - resource = new TbResource(); - if (resourceUpdateMsg.getIsSystem()) { - resource.setTenantId(TenantId.SYS_TENANT_ID); - } else { - resource.setTenantId(tenantId); - } + throw new RuntimeException("[{" + tenantId + "}] resourceUpdateMsg {" + resourceUpdateMsg + " } cannot be converted to resource"); + } + boolean created = false; + TbResource resourceById = resourceService.findResourceById(tenantId, tbResourceId); + if (resourceById == null) { resource.setCreatedTime(Uuids.unixTimestamp(tbResourceId.getId())); created = true; + resource.setId(null); + } else { + resource.setId(tbResourceId); } - String resourceKey = resourceUpdateMsg.getResourceKey(); - ResourceType resourceType = ResourceType.valueOf(resourceUpdateMsg.getResourceType()); + String resourceKey = resource.getResourceKey(); + ResourceType resourceType = resource.getResourceType(); PageDataIterable resourcesIterable = new PageDataIterable<>( link -> resourceService.findTenantResourcesByResourceTypeAndPageLink(tenantId, resourceType, link), 1024); for (TbResource tbResource : resourcesIterable) { - if (tbResource.getResourceKey().equals(resourceUpdateMsg.getResourceKey()) && !tbResourceId.equals(tbResource.getId())) { + if (tbResource.getResourceKey().equals(resourceKey) && !tbResourceId.equals(tbResource.getId())) { resourceKey = StringUtils.randomAlphabetic(15) + "_" + resourceKey; log.warn("[{}] Resource with resource type {} and key {} already exists. Renaming resource key to {}", - tenantId, resourceType, resourceUpdateMsg.getResourceKey(), resourceKey); + tenantId, resourceType, resource.getResourceKey(), resourceKey); resourceKeyUpdated = true; } } - resource.setTitle(resourceUpdateMsg.getTitle()); resource.setResourceKey(resourceKey); - resource.setResourceType(resourceType); - resource.setFileName(resourceUpdateMsg.getFileName()); - resource.setData(resourceUpdateMsg.hasData() ? resourceUpdateMsg.getData() : null); - resource.setEtag(resourceUpdateMsg.hasEtag() ? resourceUpdateMsg.getEtag() : null); resourceValidator.validate(resource, TbResourceInfo::getTenantId); if (created) { resource.setId(tbResourceId); @@ -74,4 +73,20 @@ public abstract class BaseResourceProcessor extends BaseEdgeProcessor { } return resourceKeyUpdated; } + + private TbResource createTbResource(TenantId tenantId, ResourceUpdateMsg resourceUpdateMsg) { + TbResource resource = new TbResource(); + if (resourceUpdateMsg.getIsSystem()) { + resource.setTenantId(TenantId.SYS_TENANT_ID); + } else { + resource.setTenantId(tenantId); + } + resource.setTitle(resourceUpdateMsg.getTitle()); + resource.setResourceKey(resourceUpdateMsg.getResourceKey()); + resource.setResourceType(ResourceType.valueOf(resourceUpdateMsg.getResourceType())); + resource.setFileName(resourceUpdateMsg.getFileName()); + resource.setData(resourceUpdateMsg.hasData() ? resourceUpdateMsg.getData() : null); + resource.setEtag(resourceUpdateMsg.hasEtag() ? resourceUpdateMsg.getEtag() : null); + return resource; + } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessor.java index 7242d8c01b..bd99e48622 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessor.java @@ -33,6 +33,7 @@ import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.ResourceUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; import java.util.UUID; @@ -41,7 +42,7 @@ import java.util.UUID; @TbCoreComponent public class ResourceEdgeProcessor extends BaseResourceProcessor { - public ListenableFuture processResourceMsgFromEdge(TenantId tenantId, Edge edge, ResourceUpdateMsg resourceUpdateMsg) { + public ListenableFuture processResourceMsgFromEdge(TenantId tenantId, Edge edge, ResourceUpdateMsg resourceUpdateMsg, EdgeVersion edgeVersion) { TbResourceId tbResourceId = new TbResourceId(new UUID(resourceUpdateMsg.getIdMSB(), resourceUpdateMsg.getIdLSB())); try { edgeSynchronizationManager.getEdgeId().set(edge.getId()); @@ -49,7 +50,7 @@ public class ResourceEdgeProcessor extends BaseResourceProcessor { switch (resourceUpdateMsg.getMsgType()) { case ENTITY_CREATED_RPC_MESSAGE: case ENTITY_UPDATED_RPC_MESSAGE: - boolean resourceKeyUpdated = super.saveOrUpdateTbResource(tenantId, tbResourceId, resourceUpdateMsg); + boolean resourceKeyUpdated = super.saveOrUpdateTbResource(tenantId, tbResourceId, resourceUpdateMsg, EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)); if (resourceKeyUpdated) { saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.TB_RESOURCE, EdgeEventActionType.UPDATED, tbResourceId, null); } diff --git a/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java index 88aead4c7d..bf08967f16 100644 --- a/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java @@ -469,6 +469,16 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { } private void validateConnectivityAdminSettings(AdminSettings adminSettings) { + JsonNode jsonNode = adminSettings.getJsonValue(); + Assert.assertNotNull(jsonNode.get("http")); + Assert.assertNotNull(jsonNode.get("https")); + Assert.assertNotNull(jsonNode.get("mqtt")); + Assert.assertNotNull(jsonNode.get("mqtts")); + Assert.assertNotNull(jsonNode.get("coap")); + Assert.assertNotNull(jsonNode.get("coaps")); + } + + private void validateJwtAdminSettings(AdminSettings adminSettings) { JsonNode jsonNode = adminSettings.getJsonValue(); Assert.assertNotNull(jsonNode.get("tokenExpirationTime")); Assert.assertNotNull(jsonNode.get("refreshTokenExpTime")); @@ -476,10 +486,6 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { Assert.assertNotNull(jsonNode.get("tokenSigningKey")); } - private void validateJwtAdminSettings(AdminSettings adminSettings) { - Assert.assertNotNull(adminSettings.getJsonValue().get("baseUrl")); - } - private void validateAssetProfiles(int expectedMsgCnt) throws Exception { List assetProfileUpdateMsgs = edgeImitator.findAllMessagesByType(AssetProfileUpdateMsg.class); Assert.assertEquals(expectedMsgCnt, assetProfileUpdateMsgs.size()); diff --git a/application/src/test/java/org/thingsboard/server/edge/ResourceEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/ResourceEdgeTest.java index 2ae57f7f19..74773208da 100644 --- a/application/src/test/java/org/thingsboard/server/edge/ResourceEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/ResourceEdgeTest.java @@ -19,6 +19,7 @@ import com.datastax.oss.driver.api.core.uuid.Uuids; import com.google.protobuf.AbstractMessage; import org.junit.Assert; import org.junit.Test; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.TbResource; @@ -58,12 +59,14 @@ public class ResourceEdgeTest extends AbstractEdgeTest { Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, resourceUpdateMsg.getMsgType()); Assert.assertEquals(savedResource.getUuidId().getMostSignificantBits(), resourceUpdateMsg.getIdMSB()); Assert.assertEquals(savedResource.getUuidId().getLeastSignificantBits(), resourceUpdateMsg.getIdLSB()); - Assert.assertEquals("Edge Test Resource", resourceUpdateMsg.getTitle()); - Assert.assertEquals(ResourceType.JKS.name(), resourceUpdateMsg.getResourceType()); - Assert.assertEquals(FILE_NAME, resourceUpdateMsg.getResourceKey()); - Assert.assertEquals(FILE_NAME, resourceUpdateMsg.getFileName()); - Assert.assertEquals(TEST_DATA, resourceUpdateMsg.getData()); - Assert.assertTrue(StringUtils.isNotBlank(resourceUpdateMsg.getEtag())); + TbResource tbResource = JacksonUtil.fromStringIgnoreUnknownProperties(resourceUpdateMsg.getEntity(), TbResource.class); + Assert.assertNotNull(tbResource); + Assert.assertEquals("Edge Test Resource", tbResource.getTitle()); + Assert.assertEquals(ResourceType.JKS, tbResource.getResourceType()); + Assert.assertEquals(FILE_NAME, tbResource.getResourceKey()); + Assert.assertEquals(FILE_NAME, tbResource.getFileName()); + Assert.assertEquals(TEST_DATA, tbResource.getData()); + Assert.assertTrue(StringUtils.isNotBlank(tbResource.getEtag())); // update resource edgeImitator.expectMessageAmount(1); @@ -73,8 +76,10 @@ public class ResourceEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof ResourceUpdateMsg); resourceUpdateMsg = (ResourceUpdateMsg) latestMessage; + tbResource = JacksonUtil.fromStringIgnoreUnknownProperties(resourceUpdateMsg.getEntity(), TbResource.class); + Assert.assertNotNull(tbResource); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, resourceUpdateMsg.getMsgType()); - Assert.assertEquals("Updated Edge Test Resource", resourceUpdateMsg.getTitle()); + Assert.assertEquals("Updated Edge Test Resource", tbResource.getTitle()); // delete resource edgeImitator.expectMessageAmount(1); @@ -91,18 +96,14 @@ public class ResourceEdgeTest extends AbstractEdgeTest { @Test public void testSendResourceToCloud() throws Exception { + TbResource tbResource = createTbResource(); UUID uuid = Uuids.timeBased(); UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); ResourceUpdateMsg.Builder resourceUpdateMsgBuilder = ResourceUpdateMsg.newBuilder(); resourceUpdateMsgBuilder.setIdMSB(uuid.getMostSignificantBits()); resourceUpdateMsgBuilder.setIdLSB(uuid.getLeastSignificantBits()); - resourceUpdateMsgBuilder.setTitle("Edge Test Resource"); - resourceUpdateMsgBuilder.setResourceType(ResourceType.JKS.name()); - resourceUpdateMsgBuilder.setResourceKey(FILE_NAME); - resourceUpdateMsgBuilder.setFileName(FILE_NAME); - resourceUpdateMsgBuilder.setData(TEST_DATA); - resourceUpdateMsgBuilder.setIsSystem(false); + resourceUpdateMsgBuilder.setEntity(JacksonUtil.toString(tbResource)); resourceUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); testAutoGeneratedCodeByProtobuf(resourceUpdateMsgBuilder); uplinkMsgBuilder.addResourceUpdateMsg(resourceUpdateMsgBuilder.build()); @@ -117,18 +118,14 @@ public class ResourceEdgeTest extends AbstractEdgeTest { UplinkResponseMsg latestResponseMsg = edgeImitator.getLatestResponseMsg(); Assert.assertTrue(latestResponseMsg.getSuccess()); - TbResource tbResource = doGet("/api/resource/" + uuid, TbResource.class); - Assert.assertNotNull(tbResource); - Assert.assertEquals("Edge Test Resource", tbResource.getName()); + TbResource tb = doGet("/api/resource/" + uuid, TbResource.class); + Assert.assertNotNull(tb); + Assert.assertEquals("Edge Test Resource", tb.getName()); } @Test public void testResourceToCloudWithNameThatAlreadyExistsOnCloud() throws Exception { - TbResource resource = new TbResource(); - resource.setResourceType(ResourceType.JKS); - resource.setTitle("Edge Test Resource"); - resource.setFileName(FILE_NAME); - resource.setData(TEST_DATA); + TbResource resource = createTbResource(); edgeImitator.expectMessageAmount(1); TbResource savedResource = doPost("/api/resource", resource, TbResource.class); @@ -140,12 +137,7 @@ public class ResourceEdgeTest extends AbstractEdgeTest { ResourceUpdateMsg.Builder resourceUpdateMsgBuilder = ResourceUpdateMsg.newBuilder(); resourceUpdateMsgBuilder.setIdMSB(uuid.getMostSignificantBits()); resourceUpdateMsgBuilder.setIdLSB(uuid.getLeastSignificantBits()); - resourceUpdateMsgBuilder.setTitle("Edge Test Resource"); - resourceUpdateMsgBuilder.setResourceType(ResourceType.JKS.name()); - resourceUpdateMsgBuilder.setResourceKey(FILE_NAME); - resourceUpdateMsgBuilder.setFileName(FILE_NAME); - resourceUpdateMsgBuilder.setData(TEST_DATA); - resourceUpdateMsgBuilder.setIsSystem(false); + resourceUpdateMsgBuilder.setEntity(JacksonUtil.toString(resource)); resourceUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); testAutoGeneratedCodeByProtobuf(resourceUpdateMsgBuilder); uplinkMsgBuilder.addResourceUpdateMsg(resourceUpdateMsgBuilder.build()); @@ -171,4 +163,15 @@ public class ResourceEdgeTest extends AbstractEdgeTest { Assert.assertNotNull(tbResource); Assert.assertNotEquals(FILE_NAME, tbResource.getName()); } + + private TbResource createTbResource() { + TbResource tbResource = new TbResource(); + tbResource.setTenantId(tenantId); + tbResource.setTitle("Edge Test Resource"); + tbResource.setResourceType(ResourceType.JKS); + tbResource.setResourceKey(FILE_NAME); + tbResource.setFileName(FILE_NAME); + tbResource.setData(TEST_DATA); + return tbResource; + } } From ccb0fc8cad395fe176a01977469494c5a2ba476f Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Mon, 13 Nov 2023 15:29:22 +0200 Subject: [PATCH 118/252] updated TbPubSubNode publisher to use one thread for publishing --- .../thingsboard/rule/engine/gcp/pubsub/TbPubSubNode.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNode.java index c55113783c..060683434f 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNode.java @@ -20,6 +20,7 @@ import com.google.api.core.ApiFutureCallback; import com.google.api.core.ApiFutures; import com.google.api.gax.core.CredentialsProvider; import com.google.api.gax.core.FixedCredentialsProvider; +import com.google.api.gax.core.InstantiatingExecutorProvider; import com.google.api.gax.retrying.RetrySettings; import com.google.auth.oauth2.ServiceAccountCredentials; import com.google.cloud.pubsub.v1.Publisher; @@ -135,6 +136,11 @@ public class TbPubSubNode extends TbAbstractExternalNode { new ByteArrayInputStream(config.getServiceAccountKey().getBytes())); CredentialsProvider credProvider = FixedCredentialsProvider.create(credentials); + InstantiatingExecutorProvider executorProvider = + InstantiatingExecutorProvider.newBuilder() + .setExecutorThreadCount(1) + .build(); + var retrySettings = RetrySettings.newBuilder() .setTotalTimeout(Duration.ofSeconds(10)) .setInitialRetryDelay(Duration.ofMillis(50)) @@ -148,6 +154,7 @@ public class TbPubSubNode extends TbAbstractExternalNode { return Publisher.newBuilder(topicName) .setCredentialsProvider(credProvider) .setRetrySettings(retrySettings) + .setExecutorProvider(executorProvider) .build(); } } From df24c363ced1641b5ec3b4ca33cc61cfa719d5d4 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Mon, 13 Nov 2023 17:07:32 +0200 Subject: [PATCH 119/252] Version set to 3.6.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/edge-api/pom.xml | 2 +- common/message/pom.xml | 2 +- common/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 +- monitoring/pom.xml | 2 +- msa/black-box-tests/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 | 4 ++-- 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 +- 58 files changed, 60 insertions(+), 60 deletions(-) diff --git a/application/pom.xml b/application/pom.xml index ee01bfbaf3..9c30c4cb77 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT thingsboard application diff --git a/common/actor/pom.xml b/common/actor/pom.xml index 7fa770c811..149d88ba2c 100644 --- a/common/actor/pom.xml +++ b/common/actor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT common org.thingsboard.common diff --git a/common/cache/pom.xml b/common/cache/pom.xml index 8ec12ca0e7..22987bad50 100644 --- a/common/cache/pom.xml +++ b/common/cache/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT common org.thingsboard.common diff --git a/common/cluster-api/pom.xml b/common/cluster-api/pom.xml index 1546121b77..6a0831de47 100644 --- a/common/cluster-api/pom.xml +++ b/common/cluster-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT common org.thingsboard.common diff --git a/common/coap-server/pom.xml b/common/coap-server/pom.xml index d4b54af0b5..8429c4452b 100644 --- a/common/coap-server/pom.xml +++ b/common/coap-server/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT common org.thingsboard.common diff --git a/common/dao-api/pom.xml b/common/dao-api/pom.xml index 3c735dd073..fa2dca9dca 100644 --- a/common/dao-api/pom.xml +++ b/common/dao-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT common org.thingsboard.common diff --git a/common/data/pom.xml b/common/data/pom.xml index ab1831a6e7..3eb0da67c2 100644 --- a/common/data/pom.xml +++ b/common/data/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT common org.thingsboard.common diff --git a/common/edge-api/pom.xml b/common/edge-api/pom.xml index 71d6a42819..8e24615a88 100644 --- a/common/edge-api/pom.xml +++ b/common/edge-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT common org.thingsboard.common diff --git a/common/message/pom.xml b/common/message/pom.xml index 2a8171ec72..64c9140569 100644 --- a/common/message/pom.xml +++ b/common/message/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT common org.thingsboard.common diff --git a/common/pom.xml b/common/pom.xml index 9e293cdd69..b00b13db57 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT thingsboard common diff --git a/common/queue/pom.xml b/common/queue/pom.xml index 6a03c6bff3..32d740812d 100644 --- a/common/queue/pom.xml +++ b/common/queue/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT common org.thingsboard.common diff --git a/common/script/pom.xml b/common/script/pom.xml index dcd5c6bc55..58630b78db 100644 --- a/common/script/pom.xml +++ b/common/script/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.6.1-SNAPSHOT + 3.6.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 e0ad5ec3b1..874c0d4d0c 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 - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT script org.thingsboard.common.script diff --git a/common/script/script-api/pom.xml b/common/script/script-api/pom.xml index 6680199616..d12eccd184 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 - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT script org.thingsboard.common.script diff --git a/common/stats/pom.xml b/common/stats/pom.xml index 6514b65b2a..2dce8dc465 100644 --- a/common/stats/pom.xml +++ b/common/stats/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT common org.thingsboard.common diff --git a/common/transport/coap/pom.xml b/common/transport/coap/pom.xml index 52758820cf..ee8c5dba86 100644 --- a/common/transport/coap/pom.xml +++ b/common/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/http/pom.xml b/common/transport/http/pom.xml index bcd05edba4..fdbb45fd6c 100644 --- a/common/transport/http/pom.xml +++ b/common/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/lwm2m/pom.xml b/common/transport/lwm2m/pom.xml index c2a5d8bd4b..5c37bbcf89 100644 --- a/common/transport/lwm2m/pom.xml +++ b/common/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/mqtt/pom.xml b/common/transport/mqtt/pom.xml index 0baf8be935..5c0aed7121 100644 --- a/common/transport/mqtt/pom.xml +++ b/common/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/pom.xml b/common/transport/pom.xml index be175ae817..14509a8648 100644 --- a/common/transport/pom.xml +++ b/common/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT common org.thingsboard.common diff --git a/common/transport/snmp/pom.xml b/common/transport/snmp/pom.xml index be95664251..2a3874510d 100644 --- a/common/transport/snmp/pom.xml +++ b/common/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard.common - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT transport diff --git a/common/transport/transport-api/pom.xml b/common/transport/transport-api/pom.xml index ce4594be7d..42dc93c2e9 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 - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/util/pom.xml b/common/util/pom.xml index f8dba98bb8..eab8ae982b 100644 --- a/common/util/pom.xml +++ b/common/util/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT common org.thingsboard.common diff --git a/common/version-control/pom.xml b/common/version-control/pom.xml index a0811b1e1a..c6595ca824 100644 --- a/common/version-control/pom.xml +++ b/common/version-control/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT common org.thingsboard.common diff --git a/dao/pom.xml b/dao/pom.xml index 673a08e0c1..3c0a036212 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT thingsboard dao diff --git a/monitoring/pom.xml b/monitoring/pom.xml index 989cd113af..b52212b064 100644 --- a/monitoring/pom.xml +++ b/monitoring/pom.xml @@ -21,7 +21,7 @@ 4.0.0 org.thingsboard - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT thingsboard diff --git a/msa/black-box-tests/pom.xml b/msa/black-box-tests/pom.xml index 341bdbb560..f710ea0414 100644 --- a/msa/black-box-tests/pom.xml +++ b/msa/black-box-tests/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/js-executor/package.json b/msa/js-executor/package.json index 558eddd3e4..a38ee36fba 100644 --- a/msa/js-executor/package.json +++ b/msa/js-executor/package.json @@ -1,7 +1,7 @@ { "name": "thingsboard-js-executor", "private": true, - "version": "3.6.1", + "version": "3.6.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 1cf55d9795..b3d76ad0ec 100644 --- a/msa/js-executor/pom.xml +++ b/msa/js-executor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/monitoring/pom.xml b/msa/monitoring/pom.xml index 092bcfccc8..2721a84382 100644 --- a/msa/monitoring/pom.xml +++ b/msa/monitoring/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT msa diff --git a/msa/pom.xml b/msa/pom.xml index fde5244ddf..2371540dd7 100644 --- a/msa/pom.xml +++ b/msa/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT thingsboard msa diff --git a/msa/tb-node/pom.xml b/msa/tb-node/pom.xml index 61185f4eda..4e6a268cac 100644 --- a/msa/tb-node/pom.xml +++ b/msa/tb-node/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/tb/pom.xml b/msa/tb/pom.xml index c67de4b7dc..de9e55a12b 100644 --- a/msa/tb/pom.xml +++ b/msa/tb/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT msa org.thingsboard.msa @@ -38,7 +38,7 @@ tb-postgres tb-cassandra /usr/share/${pkg.name} - 3.6.1 + 3.6.2 diff --git a/msa/transport/coap/pom.xml b/msa/transport/coap/pom.xml index 404006d2fa..06f22351e9 100644 --- a/msa/transport/coap/pom.xml +++ b/msa/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/http/pom.xml b/msa/transport/http/pom.xml index 2ba1a5cb73..edd2a3f570 100644 --- a/msa/transport/http/pom.xml +++ b/msa/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/lwm2m/pom.xml b/msa/transport/lwm2m/pom.xml index 3db99773c8..a089dbf97b 100644 --- a/msa/transport/lwm2m/pom.xml +++ b/msa/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/mqtt/pom.xml b/msa/transport/mqtt/pom.xml index 3b67d74fee..ea52fb5c8d 100644 --- a/msa/transport/mqtt/pom.xml +++ b/msa/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/pom.xml b/msa/transport/pom.xml index 445ee31bf7..d693264164 100644 --- a/msa/transport/pom.xml +++ b/msa/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/transport/snmp/pom.xml b/msa/transport/snmp/pom.xml index af3dc31629..ebfc0db272 100644 --- a/msa/transport/snmp/pom.xml +++ b/msa/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard.msa transport - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT org.thingsboard.msa.transport diff --git a/msa/vc-executor-docker/pom.xml b/msa/vc-executor-docker/pom.xml index 3bca1a5c8d..4d09d0fa92 100644 --- a/msa/vc-executor-docker/pom.xml +++ b/msa/vc-executor-docker/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/vc-executor/pom.xml b/msa/vc-executor/pom.xml index 4e6b6c4e11..0ca1553eed 100644 --- a/msa/vc-executor/pom.xml +++ b/msa/vc-executor/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/web-ui/package.json b/msa/web-ui/package.json index a1ac96937b..96b7169f98 100644 --- a/msa/web-ui/package.json +++ b/msa/web-ui/package.json @@ -1,7 +1,7 @@ { "name": "thingsboard-web-ui", "private": true, - "version": "3.6.1", + "version": "3.6.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 02772a68e3..1b22066e2a 100644 --- a/msa/web-ui/pom.xml +++ b/msa/web-ui/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT msa org.thingsboard.msa diff --git a/netty-mqtt/pom.xml b/netty-mqtt/pom.xml index 492d120d05..15576be6af 100644 --- a/netty-mqtt/pom.xml +++ b/netty-mqtt/pom.xml @@ -19,11 +19,11 @@ 4.0.0 org.thingsboard - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT thingsboard netty-mqtt - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT jar Netty MQTT Client diff --git a/pom.xml b/pom.xml index 818bef02f0..7fd3769704 100755 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard thingsboard - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT pom Thingsboard diff --git a/rest-client/pom.xml b/rest-client/pom.xml index 8651656744..ea223ae64c 100644 --- a/rest-client/pom.xml +++ b/rest-client/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT thingsboard rest-client diff --git a/rule-engine/pom.xml b/rule-engine/pom.xml index 39e395b756..e477188b12 100644 --- a/rule-engine/pom.xml +++ b/rule-engine/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT thingsboard rule-engine diff --git a/rule-engine/rule-engine-api/pom.xml b/rule-engine/rule-engine-api/pom.xml index 9658f51e05..d9c42e9e34 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 - 3.6.1-SNAPSHOT + 3.6.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 636f59ca5f..07331d951e 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 - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT rule-engine org.thingsboard.rule-engine diff --git a/tools/pom.xml b/tools/pom.xml index 5417f12119..925946e8c5 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT thingsboard tools diff --git a/transport/coap/pom.xml b/transport/coap/pom.xml index c4b52361b7..47dea23ba8 100644 --- a/transport/coap/pom.xml +++ b/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/http/pom.xml b/transport/http/pom.xml index 4e3abea0d1..4220cf97ef 100644 --- a/transport/http/pom.xml +++ b/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/lwm2m/pom.xml b/transport/lwm2m/pom.xml index 5bac4f5973..f9b9368db1 100644 --- a/transport/lwm2m/pom.xml +++ b/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/mqtt/pom.xml b/transport/mqtt/pom.xml index 67f27f8ab4..8df709ed35 100644 --- a/transport/mqtt/pom.xml +++ b/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/pom.xml b/transport/pom.xml index 4e0182928c..0b0e90c710 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT thingsboard transport diff --git a/transport/snmp/pom.xml b/transport/snmp/pom.xml index da4c8c23d3..671de0e33c 100644 --- a/transport/snmp/pom.xml +++ b/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT transport diff --git a/ui-ngx/package.json b/ui-ngx/package.json index df14ad0c7f..ea64501e6b 100644 --- a/ui-ngx/package.json +++ b/ui-ngx/package.json @@ -1,6 +1,6 @@ { "name": "thingsboard", - "version": "3.6.1", + "version": "3.6.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 2c09d29af2..d2751b0220 100644 --- a/ui-ngx/pom.xml +++ b/ui-ngx/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.6.1-SNAPSHOT + 3.6.2-SNAPSHOT thingsboard org.thingsboard From 7489b87fa55a0946f98e70f74adbabe55cce30c3 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Mon, 13 Nov 2023 18:46:24 +0200 Subject: [PATCH 120/252] Remove tenantId from image link; remove link from resource --- .../main/data/upgrade/3.6.1/schema_update.sql | 1 - .../controller/TbResourceController.java | 28 +----- .../service/install/update/ImagesUpdater.java | 16 ++-- .../controller/TbResourceControllerTest.java | 13 ++- .../server/dao/resource/ResourceService.java | 2 + .../server/common/data/TbResourceInfo.java | 6 +- .../server/common/data/util/ImageUtils.java | 96 +++++++++++++++++++ .../common/data/util/MediaTypeUtils.java | 45 --------- .../server/dao/model/ModelConstants.java | 1 - .../dao/model/sql/TbResourceEntity.java | 6 -- .../dao/model/sql/TbResourceInfoEntity.java | 6 -- .../dao/resource/BaseResourceService.java | 19 +++- .../main/resources/sql/schema-entities.sql | 1 - 13 files changed, 137 insertions(+), 103 deletions(-) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/util/ImageUtils.java delete mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/util/MediaTypeUtils.java diff --git a/application/src/main/data/upgrade/3.6.1/schema_update.sql b/application/src/main/data/upgrade/3.6.1/schema_update.sql index 7f5838f8bf..a594b5f3c8 100644 --- a/application/src/main/data/upgrade/3.6.1/schema_update.sql +++ b/application/src/main/data/upgrade/3.6.1/schema_update.sql @@ -28,6 +28,5 @@ $$ END; $$; ALTER TABLE resource ADD COLUMN IF NOT EXISTS media_type varchar(255); -ALTER TABLE resource ADD COLUMN IF NOT EXISTS link varchar(255); -- RESOURCES UPDATE END diff --git a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java index 9874db3002..7968b2cda8 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java @@ -35,7 +35,6 @@ import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.StringUtils; @@ -93,7 +92,6 @@ public class TbResourceController extends BaseController { @ApiOperation(value = "Download Resource (downloadResource)", notes = "Download Resource based on the provided Resource Id." + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @GetMapping(value = "/resource/{resourceId}/download") - @ResponseBody public ResponseEntity downloadResource(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION) @PathVariable(RESOURCE_ID) String strResourceId) throws ThingsboardException { checkParameter(RESOURCE_ID, strResourceId); @@ -111,17 +109,10 @@ public class TbResourceController extends BaseController { @ApiOperation(value = "Download Image (downloadImageIfChanged)", notes = DOWNLOAD_RESOURCE_IF_NOT_CHANGED + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @GetMapping(value = "/images/{tenantId}/{resourceKey}", produces = "image/*") - @ResponseBody - public ResponseEntity downloadImageIfChanged(@PathVariable("tenantId") String tenantIdStr, - @PathVariable("resourceKey") String resourceKey, + @GetMapping(value = "/images/{resourceKey}", produces = "image/*") + public ResponseEntity downloadImageIfChanged(@PathVariable("resourceKey") String resourceKey, @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws ThingsboardException { - TenantId tenantId; - if (tenantIdStr.equals("system")) { - tenantId = TenantId.SYS_TENANT_ID; - } else { - tenantId = TenantId.fromUUID(toUUID(tenantIdStr)); - } + TenantId tenantId = getTenantId(); return downloadResourceIfChanged(ResourceType.IMAGE, etag, () -> resourceService.findResourceInfoByTenantIdAndKey(tenantId, ResourceType.IMAGE, resourceKey), () -> resourceService.findResourceByTenantIdAndKey(tenantId, ResourceType.IMAGE, resourceKey)); @@ -130,7 +121,6 @@ public class TbResourceController extends BaseController { @ApiOperation(value = "Download LWM2M Resource (downloadLwm2mResourceIfChanged)", notes = DOWNLOAD_RESOURCE_IF_NOT_CHANGED + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @GetMapping(value = "/resource/lwm2m/{resourceId}/download", produces = "application/xml") - @ResponseBody public ResponseEntity downloadLwm2mResourceIfChanged(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION) @PathVariable(RESOURCE_ID) String strResourceId, @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws ThingsboardException { @@ -140,7 +130,6 @@ public class TbResourceController extends BaseController { @ApiOperation(value = "Download PKCS_12 Resource (downloadPkcs12ResourceIfChanged)", notes = DOWNLOAD_RESOURCE_IF_NOT_CHANGED + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @RequestMapping(value = "/resource/pkcs12/{resourceId}/download", method = RequestMethod.GET, produces = "application/x-pkcs12") - @ResponseBody public ResponseEntity downloadPkcs12ResourceIfChanged(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION) @PathVariable(RESOURCE_ID) String strResourceId, @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws ThingsboardException { @@ -151,7 +140,6 @@ public class TbResourceController extends BaseController { notes = DOWNLOAD_RESOURCE_IF_NOT_CHANGED + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @GetMapping(value = "/resource/jks/{resourceId}/download", produces = "application/x-java-keystore") - @ResponseBody public ResponseEntity downloadJksResourceIfChanged(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION) @PathVariable(RESOURCE_ID) String strResourceId, @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws ThingsboardException { @@ -161,7 +149,6 @@ public class TbResourceController extends BaseController { @ApiOperation(value = "Download JS Resource (downloadJsResourceIfChanged)", notes = DOWNLOAD_RESOURCE_IF_NOT_CHANGED + AVAILABLE_FOR_ANY_AUTHORIZED_USER) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @GetMapping(value = "/resource/js/{resourceId}/download", produces = "application/javascript") - @ResponseBody public ResponseEntity downloadJsResourceIfChanged(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION) @PathVariable(RESOURCE_ID) String strResourceId, @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws ThingsboardException { @@ -174,7 +161,6 @@ public class TbResourceController extends BaseController { produces = "application/json") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @GetMapping(value = "/resource/info/{resourceId}") - @ResponseBody public TbResourceInfo getResourceInfoById(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION) @PathVariable(RESOURCE_ID) String strResourceId) throws ThingsboardException { checkParameter(RESOURCE_ID, strResourceId); @@ -189,7 +175,6 @@ public class TbResourceController extends BaseController { @Deprecated // resource's data should be fetched with a download request @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @GetMapping(value = "/resource/{resourceId}") - @ResponseBody public TbResource getResourceById(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION) @PathVariable(RESOURCE_ID) String strResourceId) throws ThingsboardException { checkParameter(RESOURCE_ID, strResourceId); @@ -211,7 +196,6 @@ public class TbResourceController extends BaseController { consumes = "application/json") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @PostMapping(value = "/resource") - @ResponseBody public TbResourceInfo saveResource(@ApiParam(value = "A JSON value representing the Resource.") @RequestBody TbResource resource) throws Exception { resource.setTenantId(getTenantId()); @@ -225,7 +209,6 @@ public class TbResourceController extends BaseController { produces = "application/json") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @GetMapping(value = "/resource") - @ResponseBody public PageData getResources(@ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true) @RequestParam int pageSize, @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true) @@ -257,7 +240,6 @@ public class TbResourceController extends BaseController { produces = "application/json") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @GetMapping(value = "/resource/lwm2m/page") - @ResponseBody public List getLwm2mListObjectsPage(@ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true) @RequestParam int pageSize, @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true) @@ -278,7 +260,6 @@ public class TbResourceController extends BaseController { produces = "application/json") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @GetMapping(value = "/resource/lwm2m") - @ResponseBody public List getLwm2mListObjects(@ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES, required = true) @RequestParam String sortOrder, @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = LWM2M_OBJECT_SORT_PROPERTY_ALLOWABLE_VALUES, required = true) @@ -292,7 +273,6 @@ public class TbResourceController extends BaseController { notes = "Deletes the Resource. Referencing non-existing Resource Id will cause an error." + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @DeleteMapping(value = "/resource/{resourceId}") - @ResponseBody public void deleteResource(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION) @PathVariable("resourceId") String strResourceId) throws ThingsboardException { checkParameter(RESOURCE_ID, strResourceId); @@ -323,6 +303,7 @@ public class TbResourceController extends BaseController { } } + // TODO: rate limits TbResource tbResource = resourceSupplier.get(); checkEntity(getCurrentUser(), tbResource, Operation.READ); @@ -336,4 +317,5 @@ public class TbResourceController extends BaseController { .eTag(tbResource.getEtag()) .body(resource); } + } \ No newline at end of file diff --git a/application/src/main/java/org/thingsboard/server/service/install/update/ImagesUpdater.java b/application/src/main/java/org/thingsboard/server/service/install/update/ImagesUpdater.java index e4b8defc56..74ee36ce34 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/update/ImagesUpdater.java +++ b/application/src/main/java/org/thingsboard/server/service/install/update/ImagesUpdater.java @@ -30,7 +30,7 @@ import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.TbResourceInfo; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.util.MediaTypeUtils; +import org.thingsboard.server.common.data.util.ImageUtils; import org.thingsboard.server.common.data.widget.WidgetTypeDetails; import org.thingsboard.server.common.data.widget.WidgetsBundle; import org.thingsboard.server.dao.resource.ResourceService; @@ -65,7 +65,7 @@ public class ImagesUpdater { throw new IllegalArgumentException("Image name is missing for " + imageKey + ". Please add it to names.json file"); } byte[] imageData = Files.readAllBytes(imageFile); - String mediaType = MediaTypeUtils.fileExtensionToMediaType("image", StringUtils.substringAfterLast(imageKey, ".")); + String mediaType = ImageUtils.fileExtensionToMediaType("image", StringUtils.substringAfterLast(imageKey, ".")); try { saveImage(TenantId.SYS_TENANT_ID, imageName, imageKey, imageData, mediaType, null); } catch (Exception e) { @@ -191,7 +191,7 @@ public class ImagesUpdater { } String imageMediaType = StringUtils.substringBetween(data, "data:", ";base64"); - String extension = MediaTypeUtils.mediaTypeToFileExtension(imageMediaType); + String extension = ImageUtils.mediaTypeToFileExtension(imageMediaType); key += "." + extension; byte[] imageData = Base64.getDecoder().decode(base64Data); @@ -203,14 +203,16 @@ public class ImagesUpdater { String existingImageQuery) { TbResourceInfo resourceInfo = resourceService.findResourceInfoByTenantIdAndKey(tenantId, ResourceType.IMAGE, key); if (resourceInfo == null && !tenantId.isSysTenantId() && existingImageQuery != null) { + // TODO: need to search among tenant images too (custom widgets) List existingSystemImages = resourceService.findByTenantIdAndDataAndKeyStartingWith(TenantId.SYS_TENANT_ID, imageData, existingImageQuery); if (!existingSystemImages.isEmpty()) { resourceInfo = existingSystemImages.get(0); if (existingSystemImages.size() > 1) { log.warn("Found more than one system image resources for key {}", existingImageQuery); } - log.info("Using system image {} for {}", resourceInfo.getLink(), key); - return resourceInfo.getLink(); + String link = resourceService.getResourceLink(resourceInfo); + log.info("Using system image {} for {}", link, key); + return link; } } TbResource resource; @@ -222,7 +224,7 @@ public class ImagesUpdater { } else if (tenantId.isSysTenantId()) { resource = new TbResource(resourceInfo); } else { - return resourceInfo.getLink(); + return resourceService.getResourceLink(resourceInfo); } resource.setTitle(name); resource.setFileName(key); @@ -231,7 +233,7 @@ public class ImagesUpdater { resource = resourceService.saveResource(resource); log.info("[{}] {} image '{}' ({})", tenantId, resourceInfo == null ? "Created" : "Updated", resource.getTitle(), resource.getResourceKey()); - return resource.getLink(); + return resourceService.getResourceLink(resourceInfo); } private String getText(JsonNode jsonNode, String field) { diff --git a/application/src/test/java/org/thingsboard/server/controller/TbResourceControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/TbResourceControllerTest.java index f84e786edd..638d09bfdd 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TbResourceControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/TbResourceControllerTest.java @@ -676,10 +676,9 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setBase64Data(TEST_DATA); TbResource savedResource = save(resource); - assertThat(savedResource.getLink()).isEqualTo("/api/images/system/image.png"); loginTenantAdmin(); - MockHttpServletResponse imageResponse = doGet(savedResource.getLink()).andExpect(status().isOk()) + MockHttpServletResponse imageResponse = doGet(getImageLink(savedResource)).andExpect(status().isOk()) .andReturn().getResponse(); assertThat(imageResponse.getContentAsByteArray()) .isEqualTo(download(savedResource.getId())) @@ -701,9 +700,9 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setBase64Data(TEST_DATA); TbResource savedResource = save(resource); - assertThat(savedResource.getLink()).isEqualTo("/api/images/" + tenantId + "/image.jpg"); + String imageLink = getImageLink(savedResource); - MockHttpServletResponse imageResponse = doGet(savedResource.getLink()).andExpect(status().isOk()) + MockHttpServletResponse imageResponse = doGet(imageLink).andExpect(status().isOk()) .andReturn().getResponse(); assertThat(imageResponse.getContentAsByteArray()) .isEqualTo(download(savedResource.getId())) @@ -711,7 +710,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { assertThat(imageResponse.getContentType()).isEqualTo("image/jpeg"); loginDifferentTenant(); - doGet(savedResource.getLink()).andExpect(status().isForbidden()); + doGet(imageLink).andExpect(status().isNotFound()); } private TbResource save(TbResource tbResource) throws Exception { @@ -724,4 +723,8 @@ public class TbResourceControllerTest extends AbstractControllerTest { .andExpect(status().isOk()) .andReturn().getResponse().getContentAsByteArray(); } + + private String getImageLink(TbResourceInfo resourceInfo) { + return "/api/images/" + (resourceInfo.getTenantId().isSysTenantId() ? "system/" : "") + resourceInfo.getResourceKey(); + } } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java index 7240b1b742..db8c5b64a5 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java @@ -62,4 +62,6 @@ public interface ResourceService extends EntityDaoService { List findByTenantIdAndDataAndKeyStartingWith(TenantId tenantId, byte[] data, String query); + String getResourceLink(TbResourceInfo resourceInfo); + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java index 102029814f..5b0fc9433d 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java @@ -16,6 +16,7 @@ package org.thingsboard.server.common.data; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.databind.node.ObjectNode; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; @@ -56,8 +57,7 @@ public class TbResourceInfo extends BaseData implements HasName, H private String fileName; @ApiModelProperty(position = 10, value = "Resource media type.", example = "image/png", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private String mediaType; - @ApiModelProperty(position = 11, value = "Resource link (for IMAGE resource type).", example = "/api/images/system/my-image.png", accessMode = ApiModelProperty.AccessMode.READ_ONLY) - private String link; + private ObjectNode descriptor; public TbResourceInfo() { super(); @@ -77,7 +77,7 @@ public class TbResourceInfo extends BaseData implements HasName, H this.etag = resourceInfo.etag; this.fileName = resourceInfo.fileName; this.mediaType = resourceInfo.mediaType; - this.link = resourceInfo.link; + this.descriptor = resourceInfo.descriptor; } @ApiModelProperty(position = 1, value = "JSON object with the Resource Id. " + diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/util/ImageUtils.java b/common/data/src/main/java/org/thingsboard/server/common/data/util/ImageUtils.java new file mode 100644 index 0000000000..f67051a3da --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/util/ImageUtils.java @@ -0,0 +1,96 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.common.data.util; + +import lombok.AccessLevel; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.util.MimeType; +import org.springframework.util.MimeTypeUtils; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Map; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ImageUtils { + + private static final Map mediaTypeMappings = Map.of( + "jpeg", "jpg", + "svg+xml", "svg" + ); + + public static String mediaTypeToFileExtension(String mimeType) { + String subtype = MimeTypeUtils.parseMimeType(mimeType).getSubtype(); + return mediaTypeMappings.getOrDefault(subtype, subtype); + } + + public static String fileExtensionToMediaType(String type, String extension) { + String subtype = mediaTypeMappings.entrySet().stream() + .filter(mapping -> mapping.getValue().equals(extension)) + .map(Map.Entry::getKey).findFirst().orElse(extension); + return new MimeType(type, subtype).toString(); + } + + public static ImageInfo processImage(byte[] imageData) throws IOException { + BufferedImage image = ImageIO.read(new ByteArrayInputStream(imageData)); + ImageThumbnail thumbnail = getImageThumbnail(image, 250); + return new ImageInfo(image.getWidth(), image.getHeight(), thumbnail); + } + + private static ImageThumbnail getImageThumbnail(BufferedImage originalImage, int maxDimension) throws IOException { + int originalWidth = originalImage.getWidth(); + int originalHeight = originalImage.getHeight(); + int thumbnailWidth; + int thumbnailHeight; + if (originalWidth <= maxDimension && originalHeight <= maxDimension) { + thumbnailWidth = originalWidth; + thumbnailHeight = originalHeight; + } else { + double aspectRatio = (double) originalWidth / originalHeight; + if (originalWidth > originalHeight) { + thumbnailWidth = maxDimension; + thumbnailHeight = (int) (maxDimension / aspectRatio); + } else { + thumbnailWidth = (int) (maxDimension * aspectRatio); + thumbnailHeight = maxDimension; + } + } + BufferedImage thumbnail = new BufferedImage(thumbnailWidth, thumbnailHeight, BufferedImage.TYPE_INT_RGB); + thumbnail.getGraphics().drawImage(originalImage, 0, 0, thumbnailWidth, thumbnailHeight, null); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + ImageIO.write(thumbnail, "ignored", os); + return new ImageThumbnail(thumbnail.getWidth(), thumbnail.getHeight(), os.toByteArray()); + } + + @Data + public static class ImageInfo { + private final int width; + private final int height; + private final ImageThumbnail thumbnail; + } + + @Data + public static class ImageThumbnail { + private final int width; + private final int height; + private final byte[] data; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/util/MediaTypeUtils.java b/common/data/src/main/java/org/thingsboard/server/common/data/util/MediaTypeUtils.java deleted file mode 100644 index 4170cc738d..0000000000 --- a/common/data/src/main/java/org/thingsboard/server/common/data/util/MediaTypeUtils.java +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright © 2016-2023 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. - */ -package org.thingsboard.server.common.data.util; - -import lombok.AccessLevel; -import lombok.NoArgsConstructor; -import org.springframework.util.MimeType; -import org.springframework.util.MimeTypeUtils; - -import java.util.Map; - -@NoArgsConstructor(access = AccessLevel.PRIVATE) -public class MediaTypeUtils { - - private static final Map mappings = Map.of( - "jpeg", "jpg", - "svg+xml", "svg" - ); - - public static String mediaTypeToFileExtension(String mimeType) { - String subtype = MimeTypeUtils.parseMimeType(mimeType).getSubtype(); - return mappings.getOrDefault(subtype, subtype); - } - - public static String fileExtensionToMediaType(String type, String extension) { - String subtype = mappings.entrySet().stream() - .filter(mapping -> mapping.getValue().equals(extension)) - .map(Map.Entry::getKey).findFirst().orElse(extension); - return new MimeType(type, subtype).toString(); - } - -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index 1bbc978438..3a77a92eb7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -500,7 +500,6 @@ public class ModelConstants { public static final String RESOURCE_DATA_COLUMN = "data"; public static final String RESOURCE_ETAG_COLUMN = "etag"; public static final String RESOURCE_MEDIA_TYPE_COLUMN = "media_type"; - public static final String RESOURCE_LINK_COLUMN = "link"; /** * Ota Package constants. diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceEntity.java index 6a366c7621..f26bf100d7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceEntity.java @@ -33,7 +33,6 @@ import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_DATA_COLU import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_ETAG_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_FILE_NAME_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_KEY_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_LINK_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_MEDIA_TYPE_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TABLE_NAME; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TENANT_ID_COLUMN; @@ -74,9 +73,6 @@ public class TbResourceEntity extends BaseSqlEntity implements BaseE @Column(name = RESOURCE_MEDIA_TYPE_COLUMN) private String mediaType; - @Column(name = RESOURCE_LINK_COLUMN) - private String link; - public TbResourceEntity() { } @@ -96,7 +92,6 @@ public class TbResourceEntity extends BaseSqlEntity implements BaseE this.data = resource.getData(); this.etag = resource.getEtag(); this.mediaType = resource.getMediaType(); - this.link = resource.getLink(); } @Override @@ -112,7 +107,6 @@ public class TbResourceEntity extends BaseSqlEntity implements BaseE resource.setData(data); resource.setEtag(etag); resource.setMediaType(mediaType); - resource.setLink(link); return resource; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceInfoEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceInfoEntity.java index c8d962c9af..d74f5a3c8e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceInfoEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceInfoEntity.java @@ -32,7 +32,6 @@ import java.util.UUID; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_ETAG_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_FILE_NAME_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_KEY_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_LINK_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_MEDIA_TYPE_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TABLE_NAME; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TENANT_ID_COLUMN; @@ -70,9 +69,6 @@ public class TbResourceInfoEntity extends BaseSqlEntity implemen @Column(name = RESOURCE_MEDIA_TYPE_COLUMN) private String mediaType; - @Column(name = RESOURCE_LINK_COLUMN) - private String link; - public TbResourceInfoEntity() { } @@ -89,7 +85,6 @@ public class TbResourceInfoEntity extends BaseSqlEntity implemen this.hashCode = resource.getEtag(); this.fileName = resource.getFileName(); this.mediaType = resource.getMediaType(); - this.link = resource.getLink(); } @Override @@ -104,7 +99,6 @@ public class TbResourceInfoEntity extends BaseSqlEntity implemen resource.setEtag(hashCode); resource.setFileName(fileName); resource.setMediaType(mediaType); - resource.setLink(link); return resource; } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java index a045a06290..370c0b5d6d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java @@ -77,11 +77,10 @@ public class BaseResourceService extends AbstractCachedEntityService Date: Tue, 14 Nov 2023 11:00:29 +0200 Subject: [PATCH 121/252] added additional logging for afterTest --- .../org/thingsboard/server/controller/AbstractWebTest.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java index 35f0c52c8d..74e4efbcb3 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java @@ -29,7 +29,6 @@ import org.apache.http.HttpStatus; import org.awaitility.Awaitility; import org.hamcrest.Matcher; import org.hibernate.exception.ConstraintViolationException; -import org.jetbrains.annotations.NotNull; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -359,6 +358,10 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { try { status = doDelete("/api/tenant/" + tenantId.getId().toString()) .andReturn().getResponse().getStatus(); + if (status != HttpStatus.SC_OK) { + log.warn("Tenant deletion failed, tenantId: {}", tenantId.getId().toString()); + Thread.sleep(1000L); + } } catch (Exception e) { throw new RuntimeException(e); } From 7d4729451a5b949e3fa79ec003496acff78a5d08 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Tue, 14 Nov 2023 12:03:26 +0200 Subject: [PATCH 122/252] ImageController draft --- .../server/controller/ImageController.java | 184 ++++++++++++++++++ .../controller/TbResourceController.java | 10 +- .../resource/DefaultTbImageService.java | 30 +++ .../service/resource/TbImageService.java | 14 ++ .../server/dao/resource/ImageService.java | 28 +++ .../server/dao/resource/ResourceService.java | 2 - .../common/data/TbResourceInfoFilter.java | 4 +- .../server/dao/resource/BaseImageService.java | 84 ++++++++ .../dao/resource/BaseResourceService.java | 28 +-- .../server/dao/resource/TbResourceDao.java | 4 + .../dao/sql/resource/JpaTbResourceDao.java | 7 +- .../sql/resource/JpaTbResourceInfoDao.java | 13 +- .../resource/TbResourceInfoRepository.java | 9 +- .../sql/resource/TbResourceRepository.java | 4 + 14 files changed, 382 insertions(+), 39 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/controller/ImageController.java create mode 100644 application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/resource/TbImageService.java create mode 100644 common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ImageService.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java diff --git a/application/src/main/java/org/thingsboard/server/controller/ImageController.java b/application/src/main/java/org/thingsboard/server/controller/ImageController.java new file mode 100644 index 0000000000..93d01698cf --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/controller/ImageController.java @@ -0,0 +1,184 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.controller; + +import io.swagger.annotations.ApiParam; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.http.CacheControl; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; +import org.thingsboard.server.common.data.TbResourceInfo; +import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.dao.resource.ImageService; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.resource.TbImageService; +import org.thingsboard.server.service.security.permission.Operation; + +import java.util.function.Supplier; + +import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION; +import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION; +import static org.thingsboard.server.controller.ControllerConstants.RESOURCE_SORT_PROPERTY_ALLOWABLE_VALUES; +import static org.thingsboard.server.controller.ControllerConstants.RESOURCE_TEXT_SEARCH_DESCRIPTION; +import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_ALLOWABLE_VALUES; +import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_DESCRIPTION; +import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION; + +@Slf4j +@RestController +@TbCoreComponent +@RequiredArgsConstructor +public class ImageController extends BaseController { + + private final ImageService imageService; + private final TbImageService tbImageService; + + private static final String IMAGE_URL = "/api/images/{type}/{key}"; + private static final String SYSTEM_IMAGE = "system"; + private static final String TENANT_IMAGE = "tenant"; + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @PostMapping("/api/image") + public TbResourceInfo uploadImage(MultipartFile file) { +// imageService.saveImage() + return null; + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @PutMapping(IMAGE_URL) + public TbResourceInfo updateImage(MultipartFile file) { + return null; + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @PutMapping(IMAGE_URL + "/info") + public TbResourceInfo updateImageInfo(@RequestBody TbResourceInfo imageInfo) { + return null; + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @GetMapping(value = IMAGE_URL, produces = "image/*") + public ResponseEntity downloadImage(@PathVariable String type, + @PathVariable String key, + @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws ThingsboardException { + TenantId tenantId = getTenantId(type); + TbResourceInfo imageInfo = imageService.getImageInfoByTenantIdAndKey(tenantId, key); + return downloadIfChanged(etag, imageInfo, () -> imageService.getImageData(tenantId, imageInfo.getId()), imageInfo.getMediaType()); + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @GetMapping(value = IMAGE_URL + "/preview", produces = "image/png") + public ResponseEntity downloadImagePreview(@PathVariable String type, + @PathVariable String key, + @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws ThingsboardException { + TenantId tenantId = getTenantId(type); + TbResourceInfo imageInfo = imageService.getImageInfoByTenantIdAndKey(tenantId, key); + return downloadIfChanged(etag, imageInfo, () -> imageService.getImagePreview(tenantId, imageInfo.getId()), imageInfo.getMediaType()); + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @GetMapping(IMAGE_URL + "/info") + public TbResourceInfo getImageInfo(@PathVariable String type, + @PathVariable String key) throws ThingsboardException { + TenantId tenantId = getTenantId(type); + TbResourceInfo imageInfo = imageService.getImageInfoByTenantIdAndKey(tenantId, key); + return checkEntity(getCurrentUser(), imageInfo, Operation.READ); + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @GetMapping("/images") + public PageData getImages(@ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true) + @RequestParam int pageSize, + @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true) + @RequestParam int page, + @ApiParam(value = RESOURCE_TEXT_SEARCH_DESCRIPTION) + @RequestParam(required = false) String textSearch, + @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = RESOURCE_SORT_PROPERTY_ALLOWABLE_VALUES) + @RequestParam(required = false) String sortProperty, + @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) + @RequestParam(required = false) String sortOrder) throws ThingsboardException { + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + TenantId tenantId = getTenantId(); + if (getCurrentUser().getAuthority() == Authority.SYS_ADMIN) { + return checkNotNull(imageService.getImagesByTenantId(tenantId, pageLink)); + } else { + return checkNotNull(imageService.getAllImagesByTenantId(tenantId, pageLink)); + } + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @DeleteMapping(IMAGE_URL) + public void deleteImage(@PathVariable String type, + @PathVariable String key) throws ThingsboardException { + TenantId tenantId = getTenantId(type); + TbResourceInfo imageInfo = imageService.getImageInfoByTenantIdAndKey(tenantId, key); + checkEntity(getCurrentUser(), imageInfo, Operation.DELETE); + tbImageService.delete(imageInfo, getCurrentUser()); + } + + private ResponseEntity downloadIfChanged(String etag, TbResourceInfo resourceInfo, + Supplier dataSupplier, String mediaType) throws ThingsboardException { + checkEntity(getCurrentUser(), resourceInfo, Operation.READ); + if (etag != null) { + if (etag.equals(resourceInfo.getEtag())) { + return ResponseEntity.status(HttpStatus.NOT_MODIFIED) + .eTag(etag) + .build(); + } + } + + byte[] data = dataSupplier.get(); + return ResponseEntity.ok() + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + resourceInfo.getFileName()) + .header("x-filename", resourceInfo.getFileName()) + .contentLength(data.length) + .header("Content-Type", mediaType) + .cacheControl(CacheControl.noCache()) + .eTag(resourceInfo.getEtag()) + .body(new ByteArrayResource(data)); + } + + private TenantId getTenantId(String imageType) throws ThingsboardException { + TenantId tenantId; + if (imageType.equals(TENANT_IMAGE)) { + tenantId = getTenantId(); + } else if (imageType.equals(SYSTEM_IMAGE)) { + tenantId = TenantId.SYS_TENANT_ID; + } else { + throw new IllegalArgumentException("Invalid image URL"); + } + return tenantId; + } + +} \ No newline at end of file diff --git a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java index 7968b2cda8..a87b26c333 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java @@ -54,7 +54,10 @@ import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; import java.util.Base64; + import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.function.Supplier; import static org.thingsboard.server.controller.ControllerConstants.AVAILABLE_FOR_ANY_AUTHORIZED_USER; @@ -224,9 +227,14 @@ public class TbResourceController extends BaseController { PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); TbResourceInfoFilter.TbResourceInfoFilterBuilder filter = TbResourceInfoFilter.builder(); filter.tenantId(getTenantId()); + Set resourceTypes = new HashSet<>(); if (StringUtils.isNotEmpty(resourceType)) { - filter.resourceType(ResourceType.valueOf(resourceType)); + resourceTypes.add(ResourceType.valueOf(resourceType)); + } else { + Collections.addAll(resourceTypes, ResourceType.values()); + resourceTypes.remove(ResourceType.IMAGE); } + filter.resourceTypes(resourceTypes); if (Authority.SYS_ADMIN.equals(getCurrentUser().getAuthority())) { return checkNotNull(resourceService.findTenantResourcesByTenantId(filter.build(), pageLink)); } else { diff --git a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java new file mode 100644 index 0000000000..d367bb2aa2 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java @@ -0,0 +1,30 @@ +package org.thingsboard.server.service.resource; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; +import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.common.data.TbResourceInfo; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.dao.resource.ImageService; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Service +@TbCoreComponent +@RequiredArgsConstructor +public class DefaultTbImageService implements TbImageService { + + private final ImageService imageService; + + @Override + public TbResource save(TbResourceInfo imageInfo, MultipartFile imageFile, User user) { + return null; + } + + @Override + public void delete(TbResourceInfo imageInfo, User user) { + + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/resource/TbImageService.java b/application/src/main/java/org/thingsboard/server/service/resource/TbImageService.java new file mode 100644 index 0000000000..5acb1b4180 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/resource/TbImageService.java @@ -0,0 +1,14 @@ +package org.thingsboard.server.service.resource; + +import org.springframework.web.multipart.MultipartFile; +import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.common.data.TbResourceInfo; +import org.thingsboard.server.common.data.User; + +public interface TbImageService { + + TbResource save(TbResourceInfo imageInfo, MultipartFile imageFile, User user); + + void delete(TbResourceInfo imageInfo, User user); + +} diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ImageService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ImageService.java new file mode 100644 index 0000000000..b31befa36b --- /dev/null +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ImageService.java @@ -0,0 +1,28 @@ +package org.thingsboard.server.dao.resource; + +import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.common.data.TbResourceInfo; +import org.thingsboard.server.common.data.id.TbResourceId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; + +public interface ImageService { + + TbResourceInfo saveImage(TbResource image); + + TbResourceInfo saveImageInfo(TbResourceInfo imageInfo); + + TbResourceInfo getImageInfoByTenantIdAndKey(TenantId tenantId, String key); + + PageData getImagesByTenantId(TenantId tenantId, PageLink pageLink); + + PageData getAllImagesByTenantId(TenantId tenantId, PageLink pageLink); + + byte[] getImageData(TenantId tenantId, TbResourceId imageId); + + byte[] getImagePreview(TenantId tenantId, TbResourceId imageId); + + String getImageLink(TbResourceInfo imageInfo); + +} diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java index db8c5b64a5..7240b1b742 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java @@ -62,6 +62,4 @@ public interface ResourceService extends EntityDaoService { List findByTenantIdAndDataAndKeyStartingWith(TenantId tenantId, byte[] data, String query); - String getResourceLink(TbResourceInfo resourceInfo); - } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfoFilter.java b/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfoFilter.java index 9057fc144b..5c0747f100 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfoFilter.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfoFilter.java @@ -19,11 +19,13 @@ import lombok.Builder; import lombok.Data; import org.thingsboard.server.common.data.id.TenantId; +import java.util.Set; + @Data @Builder public class TbResourceInfoFilter { private TenantId tenantId; - private ResourceType resourceType; + private Set resourceTypes; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java new file mode 100644 index 0000000000..19959baf54 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java @@ -0,0 +1,84 @@ +package org.thingsboard.server.dao.resource; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.ResourceType; +import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.common.data.TbResourceInfo; +import org.thingsboard.server.common.data.TbResourceInfoFilter; +import org.thingsboard.server.common.data.id.TbResourceId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.dao.service.validator.ResourceDataValidator; + +import java.util.Set; + +@Service +public class BaseImageService extends BaseResourceService implements ImageService { + + public BaseImageService(TbResourceDao resourceDao, TbResourceInfoDao resourceInfoDao, ResourceDataValidator resourceValidator) { + super(resourceDao, resourceInfoDao, resourceValidator); + } + + @Override + public TbResourceInfo saveImage(TbResource image) { + resourceValidator.validate(image, TbResourceInfo::getTenantId); + if (image.getData() != null) { + + } + // generate preview, etc. + return saveResource(image, false); + } + + @Override + public TbResourceInfo saveImageInfo(TbResourceInfo imageInfo) { + return null; + } + + @Override + public TbResourceInfo getImageInfoByTenantIdAndKey(TenantId tenantId, String key) { + return findResourceInfoByTenantIdAndKey(tenantId, ResourceType.IMAGE, key); + } + + @Override + public PageData getImagesByTenantId(TenantId tenantId, PageLink pageLink) { + TbResourceInfoFilter filter = TbResourceInfoFilter.builder() + .tenantId(tenantId) + .resourceTypes(Set.of(ResourceType.IMAGE)) + .build(); + return findTenantResourcesByTenantId(filter, pageLink); + } + + @Override + public PageData getAllImagesByTenantId(TenantId tenantId, PageLink pageLink) { + TbResourceInfoFilter filter = TbResourceInfoFilter.builder() + .tenantId(tenantId) + .resourceTypes(Set.of(ResourceType.IMAGE)) + .build(); + return findAllTenantResourcesByTenantId(filter, pageLink); + } + + @Override + public byte[] getImageData(TenantId tenantId, TbResourceId imageId) { + return resourceDao.getResourceData(tenantId, imageId); + } + + @Override + public byte[] getImagePreview(TenantId tenantId, TbResourceId imageId) { + return new byte[0]; + } + + @Override + public String getImageLink(TbResourceInfo imageInfo) { + String link = "/api/images/"; + if (imageInfo.getTenantId().isSysTenantId()) { + link += "system/"; + } else { + link += "tenant/"; + } + link += imageInfo.getResourceKey(); + return link; + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java index 370c0b5d6d..30748e5ab3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java @@ -57,9 +57,9 @@ import static org.thingsboard.server.dao.service.Validator.validateId; public class BaseResourceService extends AbstractCachedEntityService implements ResourceService { public static final String INCORRECT_RESOURCE_ID = "Incorrect resourceId "; - private final TbResourceDao resourceDao; - private final TbResourceInfoDao resourceInfoDao; - private final ResourceDataValidator resourceValidator; + protected final TbResourceDao resourceDao; + protected final TbResourceInfoDao resourceInfoDao; + protected final ResourceDataValidator resourceValidator; @Override public TbResource saveResource(TbResource resource, boolean doValidate) { @@ -67,20 +67,10 @@ public class BaseResourceService extends AbstractCachedEntityService, TenantEntityWithDataDao ResourceType resourceType, String[] objectIds, String searchText); + + byte[] getResourceData(TenantId tenantId, TbResourceId resourceId); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceDao.java index 36fa05e00e..3d163e0d4f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceDao.java @@ -21,6 +21,7 @@ import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; @@ -31,7 +32,6 @@ import org.thingsboard.server.dao.sql.JpaAbstractDao; import org.thingsboard.server.dao.util.SqlDao; import java.util.List; -import java.util.Objects; import java.util.UUID; @Slf4j @@ -94,6 +94,11 @@ public class JpaTbResourceDao extends JpaAbstractDao findAllTenantResourcesByTenantId(TbResourceInfoFilter filter, PageLink pageLink) { - ResourceType resourceType = filter.getResourceType(); return DaoUtil.toPageData(resourceInfoRepository .findAllTenantResourcesByTenantId( - filter.getTenantId().getId(), - TenantId.NULL_UUID, - resourceType == null ? null : resourceType.name(), + filter.getTenantId().getId(), TenantId.NULL_UUID, + CollectionsUtil.isNotEmpty(filter.getResourceTypes()) ? filter.getResourceTypes() + .stream().map(Enum::name).collect(Collectors.toList()) : null, Objects.toString(pageLink.getTextSearch(), ""), DaoUtil.toPageable(pageLink))); } @Override public PageData findTenantResourcesByTenantId(TbResourceInfoFilter filter, PageLink pageLink) { - ResourceType resourceType = filter.getResourceType(); return DaoUtil.toPageData(resourceInfoRepository .findTenantResourcesByTenantId( filter.getTenantId().getId(), - resourceType == null ? null : resourceType.name(), + CollectionsUtil.isNotEmpty(filter.getResourceTypes()) ? filter.getResourceTypes() + .stream().map(Enum::name).collect(Collectors.toList()) : null, pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceInfoRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceInfoRepository.java index 7ed0754624..7bbe2a92eb 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceInfoRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceInfoRepository.java @@ -22,6 +22,7 @@ import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.thingsboard.server.dao.model.sql.TbResourceInfoEntity; +import java.util.Collection; import java.util.List; import java.util.UUID; @@ -36,19 +37,19 @@ public interface TbResourceInfoRepository extends JpaRepository findAllTenantResourcesByTenantId(@Param("tenantId") UUID tenantId, @Param("systemAdminId") UUID sysadminId, - @Param("resourceType") String resourceType, + @Param("resourceTypes") Collection resourceTypes, @Param("searchText") String searchText, Pageable pageable); @Query("SELECT ri FROM TbResourceInfoEntity ri WHERE " + "ri.tenantId = :tenantId " + - "AND (:resourceType IS NULL OR ri.resourceType = :resourceType)" + + "AND (:resourceTypes IS NULL OR ri.resourceType IN :resourceTypes)" + "AND (:searchText IS NULL OR ilike(ri.title, CONCAT('%', :searchText, '%')) = true)") Page findTenantResourcesByTenantId(@Param("tenantId") UUID tenantId, - @Param("resourceType") String resourceType, + @Param("resourceTypes") Collection resourceTypes, @Param("searchText") String searchText, Pageable pageable); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceRepository.java index 3a881f5206..41c5e005dd 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceRepository.java @@ -80,4 +80,8 @@ public interface TbResourceRepository extends JpaRepository Date: Tue, 14 Nov 2023 12:08:59 +0200 Subject: [PATCH 123/252] Fix build --- .../service/install/update/ImagesUpdater.java | 32 +++++++++---------- .../resource/DefaultTbImageService.java | 15 +++++++++ .../service/resource/TbImageService.java | 15 +++++++++ .../server/dao/resource/ImageService.java | 17 +++++++++- .../server/dao/resource/BaseImageService.java | 18 +++++++++-- 5 files changed, 78 insertions(+), 19 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/install/update/ImagesUpdater.java b/application/src/main/java/org/thingsboard/server/service/install/update/ImagesUpdater.java index 74ee36ce34..9894ed5360 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/update/ImagesUpdater.java +++ b/application/src/main/java/org/thingsboard/server/service/install/update/ImagesUpdater.java @@ -33,7 +33,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.util.ImageUtils; import org.thingsboard.server.common.data.widget.WidgetTypeDetails; import org.thingsboard.server.common.data.widget.WidgetsBundle; -import org.thingsboard.server.dao.resource.ResourceService; +import org.thingsboard.server.dao.resource.ImageService; import java.nio.file.Files; import java.nio.file.Path; @@ -47,7 +47,7 @@ import java.util.Optional; @RequiredArgsConstructor public class ImagesUpdater { - private final ResourceService resourceService; + private final ImageService imageService; private static final String IMAGE_NAME_SUFFIX = " - image"; private static final String BACKGROUND_IMAGE_NAME_SUFFIX = " - background image"; @@ -201,19 +201,19 @@ public class ImagesUpdater { private String saveImage(TenantId tenantId, String name, String key, byte[] imageData, String mediaType, String existingImageQuery) { - TbResourceInfo resourceInfo = resourceService.findResourceInfoByTenantIdAndKey(tenantId, ResourceType.IMAGE, key); + TbResourceInfo resourceInfo = imageService.getImageInfoByTenantIdAndKey(tenantId, key); if (resourceInfo == null && !tenantId.isSysTenantId() && existingImageQuery != null) { // TODO: need to search among tenant images too (custom widgets) - List existingSystemImages = resourceService.findByTenantIdAndDataAndKeyStartingWith(TenantId.SYS_TENANT_ID, imageData, existingImageQuery); - if (!existingSystemImages.isEmpty()) { - resourceInfo = existingSystemImages.get(0); - if (existingSystemImages.size() > 1) { - log.warn("Found more than one system image resources for key {}", existingImageQuery); - } - String link = resourceService.getResourceLink(resourceInfo); - log.info("Using system image {} for {}", link, key); - return link; - } +// List existingSystemImages = imageService.findByTenantIdAndDataAndKeyStartingWith(TenantId.SYS_TENANT_ID, imageData, existingImageQuery); +// if (!existingSystemImages.isEmpty()) { +// resourceInfo = existingSystemImages.get(0); +// if (existingSystemImages.size() > 1) { +// log.warn("Found more than one system image resources for key {}", existingImageQuery); +// } +// String link = imageService.getImageLink(resourceInfo); +// log.info("Using system image {} for {}", link, key); +// return link; +// } } TbResource resource; if (resourceInfo == null) { @@ -224,16 +224,16 @@ public class ImagesUpdater { } else if (tenantId.isSysTenantId()) { resource = new TbResource(resourceInfo); } else { - return resourceService.getResourceLink(resourceInfo); + return imageService.getImageLink(resourceInfo); } resource.setTitle(name); resource.setFileName(key); resource.setMediaType(mediaType); resource.setData(imageData); - resource = resourceService.saveResource(resource); + resource = imageService.saveImage(resource); log.info("[{}] {} image '{}' ({})", tenantId, resourceInfo == null ? "Created" : "Updated", resource.getTitle(), resource.getResourceKey()); - return resourceService.getResourceLink(resourceInfo); + return imageService.getImageLink(resourceInfo); } private String getText(JsonNode jsonNode, String field) { diff --git a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java index d367bb2aa2..0c58f6a10a 100644 --- a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java +++ b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java @@ -1,3 +1,18 @@ +/** + * Copyright © 2016-2023 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. + */ package org.thingsboard.server.service.resource; import lombok.RequiredArgsConstructor; diff --git a/application/src/main/java/org/thingsboard/server/service/resource/TbImageService.java b/application/src/main/java/org/thingsboard/server/service/resource/TbImageService.java index 5acb1b4180..7039d0edaa 100644 --- a/application/src/main/java/org/thingsboard/server/service/resource/TbImageService.java +++ b/application/src/main/java/org/thingsboard/server/service/resource/TbImageService.java @@ -1,3 +1,18 @@ +/** + * Copyright © 2016-2023 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. + */ package org.thingsboard.server.service.resource; import org.springframework.web.multipart.MultipartFile; diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ImageService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ImageService.java index b31befa36b..a09a00296f 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ImageService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ImageService.java @@ -1,3 +1,18 @@ +/** + * Copyright © 2016-2023 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. + */ package org.thingsboard.server.dao.resource; import org.thingsboard.server.common.data.TbResource; @@ -9,7 +24,7 @@ import org.thingsboard.server.common.data.page.PageLink; public interface ImageService { - TbResourceInfo saveImage(TbResource image); + TbResource saveImage(TbResource image); TbResourceInfo saveImageInfo(TbResourceInfo imageInfo); diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java index 19959baf54..29a6f98409 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java @@ -1,6 +1,20 @@ +/** + * Copyright © 2016-2023 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. + */ package org.thingsboard.server.dao.resource; -import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.TbResource; @@ -22,7 +36,7 @@ public class BaseImageService extends BaseResourceService implements ImageServic } @Override - public TbResourceInfo saveImage(TbResource image) { + public TbResource saveImage(TbResource image) { resourceValidator.validate(image, TbResourceInfo::getTenantId); if (image.getData() != null) { From 0f26d7a5f058ef382bd746369dbc3115b5b786b3 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Tue, 14 Nov 2023 13:48:26 +0200 Subject: [PATCH 124/252] Improve constructRuleChainUpdatedMsg to not change rule chain entity --- .../rpc/constructor/RuleChainMsgConstructor.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java index 202f726ede..7aac9d36bd 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java @@ -37,22 +37,24 @@ import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; public class RuleChainMsgConstructor { public RuleChainUpdateMsg constructRuleChainUpdatedMsg(UpdateMsgType msgType, RuleChain ruleChain, boolean isRoot, EdgeVersion edgeVersion) { - ruleChain.setRoot(isRoot); if (EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)) { - return constructDeprecatedRuleChainUpdatedMsg(msgType, ruleChain); + return constructDeprecatedRuleChainUpdatedMsg(msgType, ruleChain, isRoot); } - return RuleChainUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(ruleChain)) + ruleChain.setRoot(isRoot); + RuleChainUpdateMsg result = RuleChainUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(ruleChain)) .setIdMSB(ruleChain.getId().getId().getMostSignificantBits()) .setIdLSB(ruleChain.getId().getId().getLeastSignificantBits()).build(); + ruleChain.setRoot(false); + return result; } - private RuleChainUpdateMsg constructDeprecatedRuleChainUpdatedMsg(UpdateMsgType msgType, RuleChain ruleChain) { + private RuleChainUpdateMsg constructDeprecatedRuleChainUpdatedMsg(UpdateMsgType msgType, RuleChain ruleChain, boolean isRoot) { RuleChainUpdateMsg.Builder builder = RuleChainUpdateMsg.newBuilder() .setMsgType(msgType) .setIdMSB(ruleChain.getId().getId().getMostSignificantBits()) .setIdLSB(ruleChain.getId().getId().getLeastSignificantBits()) .setName(ruleChain.getName()) - .setRoot(ruleChain.isRoot()) + .setRoot(isRoot) .setDebugMode(ruleChain.isDebugMode()) .setConfiguration(JacksonUtil.toString(ruleChain.getConfiguration())); if (ruleChain.getFirstRuleNodeId() != null) { From 19968b5c103bc935029eb5c80f831f9cbf81570d Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Tue, 14 Nov 2023 16:06:59 +0200 Subject: [PATCH 125/252] Image descriptor, image preview, refactoring --- .../main/data/upgrade/3.6.1/schema_update.sql | 4 +- .../server/controller/ImageController.java | 99 ++++++++---- .../controller/TbResourceController.java | 32 +--- .../service/install/update/ImagesUpdater.java | 46 +++--- .../install/update/SystemImagesMigrator.java | 2 +- .../resource/DefaultTbImageService.java | 46 +++++- .../service/resource/TbImageService.java | 5 +- .../controller/TbResourceControllerTest.java | 54 +------ .../server/dao/resource/ImageService.java | 4 +- .../server/common/data/ImageDescriptor.java | 29 ++++ .../server/common/data/ResourceType.java | 2 +- .../server/common/data/TbResource.java | 4 + .../server/common/data/TbResourceInfo.java | 14 +- .../server/common/data/util/ImageUtils.java | 96 ------------ dao/pom.xml | 8 + .../server/dao/model/ModelConstants.java | 3 +- .../dao/model/sql/TbResourceEntity.java | 25 ++- .../dao/model/sql/TbResourceInfoEntity.java | 16 +- .../server/dao/resource/BaseImageService.java | 47 +++++- .../dao/resource/BaseResourceService.java | 4 +- .../server/dao/resource/TbResourceDao.java | 2 + .../validator/ResourceDataValidator.java | 15 -- .../dao/sql/resource/JpaTbResourceDao.java | 5 + .../resource/TbResourceInfoRepository.java | 8 +- .../sql/resource/TbResourceRepository.java | 3 + .../server/dao/util/ImageUtils.java | 144 ++++++++++++++++++ .../main/resources/sql/schema-entities.sql | 3 +- pom.xml | 11 ++ .../src/app/shared/models/resource.models.ts | 2 - 29 files changed, 444 insertions(+), 289 deletions(-) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/ImageDescriptor.java delete mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/util/ImageUtils.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/util/ImageUtils.java diff --git a/application/src/main/data/upgrade/3.6.1/schema_update.sql b/application/src/main/data/upgrade/3.6.1/schema_update.sql index a594b5f3c8..c19ea2e4e8 100644 --- a/application/src/main/data/upgrade/3.6.1/schema_update.sql +++ b/application/src/main/data/upgrade/3.6.1/schema_update.sql @@ -27,6 +27,8 @@ $$ END IF; END; $$; -ALTER TABLE resource ADD COLUMN IF NOT EXISTS media_type varchar(255); +ALTER TABLE resource + ADD COLUMN IF NOT EXISTS descriptor varchar, + ADD COLUMN IF NOT EXISTS preview bytea; -- RESOURCES UPDATE END diff --git a/application/src/main/java/org/thingsboard/server/controller/ImageController.java b/application/src/main/java/org/thingsboard/server/controller/ImageController.java index 93d01698cf..756c198a72 100644 --- a/application/src/main/java/org/thingsboard/server/controller/ImageController.java +++ b/application/src/main/java/org/thingsboard/server/controller/ImageController.java @@ -32,8 +32,13 @@ import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.ImageDescriptor; +import org.thingsboard.server.common.data.ResourceType; +import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.TbResourceInfo; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.TenantId; @@ -43,7 +48,9 @@ import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.dao.resource.ImageService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.resource.TbImageService; +import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; +import org.thingsboard.server.service.security.permission.Resource; import java.util.function.Supplier; @@ -68,52 +75,75 @@ public class ImageController extends BaseController { private static final String SYSTEM_IMAGE = "system"; private static final String TENANT_IMAGE = "tenant"; - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @PostMapping("/api/image") - public TbResourceInfo uploadImage(MultipartFile file) { -// imageService.saveImage() - return null; + public TbResourceInfo uploadImage(@RequestPart MultipartFile file) throws Exception { + SecurityUser user = getCurrentUser(); + TbResource image = new TbResource(); + image.setTenantId(user.getTenantId()); + accessControlService.checkPermission(user, Resource.TB_RESOURCE, Operation.CREATE, null, image); + + image.setFileName(file.getOriginalFilename()); + image.setTitle(file.getOriginalFilename()); + image.setResourceType(ResourceType.IMAGE); + ImageDescriptor descriptor = new ImageDescriptor(); + descriptor.setMediaType(file.getContentType()); + image.setDescriptor(JacksonUtil.valueToTree(descriptor)); + image.setData(file.getBytes()); + return tbImageService.save(image, user); } - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @PutMapping(IMAGE_URL) - public TbResourceInfo updateImage(MultipartFile file) { - return null; + public TbResourceInfo updateImage(@PathVariable String type, + @PathVariable String key, + @RequestPart MultipartFile file) throws Exception { + TbResourceInfo imageInfo = checkImageInfo(type, key, Operation.WRITE); + ImageDescriptor descriptor = imageInfo.getDescriptor(ImageDescriptor.class); + + TbResource image = new TbResource(imageInfo); + image.setData(file.getBytes()); + descriptor.setMediaType(file.getContentType()); + return tbImageService.save(image, getCurrentUser()); } - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @PutMapping(IMAGE_URL + "/info") - public TbResourceInfo updateImageInfo(@RequestBody TbResourceInfo imageInfo) { - return null; + public TbResourceInfo updateImageInfo(@PathVariable String type, + @PathVariable String key, + @RequestBody TbResourceInfo newImageInfo) throws ThingsboardException { + TbResourceInfo imageInfo = checkImageInfo(type, key, Operation.WRITE); + imageInfo.setTitle(newImageInfo.getTitle()); + return tbImageService.save(imageInfo, getCurrentUser()); } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @GetMapping(value = IMAGE_URL, produces = "image/*") public ResponseEntity downloadImage(@PathVariable String type, @PathVariable String key, - @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws ThingsboardException { - TenantId tenantId = getTenantId(type); - TbResourceInfo imageInfo = imageService.getImageInfoByTenantIdAndKey(tenantId, key); - return downloadIfChanged(etag, imageInfo, () -> imageService.getImageData(tenantId, imageInfo.getId()), imageInfo.getMediaType()); + @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws Exception { + TenantId tenantId = getTenantId(); + TbResourceInfo imageInfo = checkImageInfo(type, key, Operation.READ); + ImageDescriptor descriptor = imageInfo.getDescriptor(ImageDescriptor.class); + return downloadIfChanged(etag, imageInfo, descriptor, () -> imageService.getImageData(tenantId, imageInfo.getId())); } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @GetMapping(value = IMAGE_URL + "/preview", produces = "image/png") public ResponseEntity downloadImagePreview(@PathVariable String type, @PathVariable String key, - @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws ThingsboardException { - TenantId tenantId = getTenantId(type); - TbResourceInfo imageInfo = imageService.getImageInfoByTenantIdAndKey(tenantId, key); - return downloadIfChanged(etag, imageInfo, () -> imageService.getImagePreview(tenantId, imageInfo.getId()), imageInfo.getMediaType()); + @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws Exception { + TenantId tenantId = getTenantId(); + TbResourceInfo imageInfo = checkImageInfo(type, key, Operation.READ); + ImageDescriptor descriptor = imageInfo.getDescriptor(ImageDescriptor.class); + return downloadIfChanged(etag, imageInfo, descriptor.getPreviewDescriptor(), () -> imageService.getImagePreview(tenantId, imageInfo.getId())); } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @GetMapping(IMAGE_URL + "/info") public TbResourceInfo getImageInfo(@PathVariable String type, @PathVariable String key) throws ThingsboardException { - TenantId tenantId = getTenantId(type); - TbResourceInfo imageInfo = imageService.getImageInfoByTenantIdAndKey(tenantId, key); - return checkEntity(getCurrentUser(), imageInfo, Operation.READ); + return checkImageInfo(type, key, Operation.READ); } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @@ -128,6 +158,7 @@ public class ImageController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { + // PE: generic permission PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); TenantId tenantId = getTenantId(); if (getCurrentUser().getAuthority() == Authority.SYS_ADMIN) { @@ -141,17 +172,14 @@ public class ImageController extends BaseController { @DeleteMapping(IMAGE_URL) public void deleteImage(@PathVariable String type, @PathVariable String key) throws ThingsboardException { - TenantId tenantId = getTenantId(type); - TbResourceInfo imageInfo = imageService.getImageInfoByTenantIdAndKey(tenantId, key); - checkEntity(getCurrentUser(), imageInfo, Operation.DELETE); + TbResourceInfo imageInfo = checkImageInfo(type, key, Operation.DELETE); tbImageService.delete(imageInfo, getCurrentUser()); } - private ResponseEntity downloadIfChanged(String etag, TbResourceInfo resourceInfo, - Supplier dataSupplier, String mediaType) throws ThingsboardException { - checkEntity(getCurrentUser(), resourceInfo, Operation.READ); + private ResponseEntity downloadIfChanged(String etag, TbResourceInfo imageInfo, ImageDescriptor imageDescriptor, + Supplier dataSupplier) { if (etag != null) { - if (etag.equals(resourceInfo.getEtag())) { + if (etag.equals(imageInfo.getEtag())) { return ResponseEntity.status(HttpStatus.NOT_MODIFIED) .eTag(etag) .build(); @@ -160,15 +188,22 @@ public class ImageController extends BaseController { byte[] data = dataSupplier.get(); return ResponseEntity.ok() - .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + resourceInfo.getFileName()) - .header("x-filename", resourceInfo.getFileName()) + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + imageInfo.getFileName()) + .header("x-filename", imageInfo.getFileName()) .contentLength(data.length) - .header("Content-Type", mediaType) + .header("Content-Type", imageDescriptor.getMediaType()) .cacheControl(CacheControl.noCache()) - .eTag(resourceInfo.getEtag()) + .eTag(imageInfo.getEtag()) .body(new ByteArrayResource(data)); } + private TbResourceInfo checkImageInfo(String imageType, String key, Operation operation) throws ThingsboardException { + TenantId tenantId = getTenantId(imageType); + TbResourceInfo imageInfo = imageService.getImageInfoByTenantIdAndKey(tenantId, key); + checkEntity(getCurrentUser(), checkNotNull(imageInfo), operation); + return imageInfo; + } + private TenantId getTenantId(String imageType) throws ThingsboardException { TenantId tenantId; if (imageType.equals(TENANT_IMAGE)) { diff --git a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java index a87b26c333..08d7cedd93 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java @@ -54,11 +54,10 @@ import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; import java.util.Base64; - import java.util.Collections; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.function.Supplier; import static org.thingsboard.server.controller.ControllerConstants.AVAILABLE_FOR_ANY_AUTHORIZED_USER; import static org.thingsboard.server.controller.ControllerConstants.LWM2M_OBJECT_DESCRIPTION; @@ -110,17 +109,6 @@ public class TbResourceController extends BaseController { .body(resource); } - @ApiOperation(value = "Download Image (downloadImageIfChanged)", notes = DOWNLOAD_RESOURCE_IF_NOT_CHANGED + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @GetMapping(value = "/images/{resourceKey}", produces = "image/*") - public ResponseEntity downloadImageIfChanged(@PathVariable("resourceKey") String resourceKey, - @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws ThingsboardException { - TenantId tenantId = getTenantId(); - return downloadResourceIfChanged(ResourceType.IMAGE, etag, - () -> resourceService.findResourceInfoByTenantIdAndKey(tenantId, ResourceType.IMAGE, resourceKey), - () -> resourceService.findResourceByTenantIdAndKey(tenantId, ResourceType.IMAGE, resourceKey)); - } - @ApiOperation(value = "Download LWM2M Resource (downloadLwm2mResourceIfChanged)", notes = DOWNLOAD_RESOURCE_IF_NOT_CHANGED + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @GetMapping(value = "/resource/lwm2m/{resourceId}/download", produces = "application/xml") @@ -291,19 +279,9 @@ public class TbResourceController extends BaseController { private ResponseEntity downloadResourceIfChanged(ResourceType resourceType, String strResourceId, String etag) throws ThingsboardException { checkParameter(RESOURCE_ID, strResourceId); - TenantId tenantId = getTenantId(); TbResourceId resourceId = new TbResourceId(toUUID(strResourceId)); - return downloadResourceIfChanged(resourceType, etag, - () -> resourceService.findResourceInfoById(tenantId, resourceId), - () -> resourceService.findResourceById(tenantId, resourceId)); - } - - private ResponseEntity downloadResourceIfChanged(ResourceType resourceType, String etag, - Supplier resourceInfoSupplier, - Supplier resourceSupplier) throws ThingsboardException { if (etag != null) { - TbResourceInfo tbResourceInfo = resourceInfoSupplier.get(); - checkEntity(getCurrentUser(), tbResourceInfo, Operation.READ); + TbResourceInfo tbResourceInfo = checkResourceInfoId(resourceId, Operation.READ); if (etag.equals(tbResourceInfo.getEtag())) { return ResponseEntity.status(HttpStatus.NOT_MODIFIED) .eTag(tbResourceInfo.getEtag()) @@ -311,16 +289,14 @@ public class TbResourceController extends BaseController { } } - // TODO: rate limits - TbResource tbResource = resourceSupplier.get(); - checkEntity(getCurrentUser(), tbResource, Operation.READ); + TbResource tbResource = checkResourceId(resourceId, Operation.READ); ByteArrayResource resource = new ByteArrayResource(tbResource.getData()); return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + tbResource.getFileName()) .header("x-filename", tbResource.getFileName()) .contentLength(resource.contentLength()) - .header("Content-Type", tbResource.getMediaType() != null ? tbResource.getMediaType() : resourceType.getDefaultMediaType()) + .header("Content-Type", resourceType.getMediaType()) .cacheControl(CacheControl.noCache()) .eTag(tbResource.getEtag()) .body(resource); diff --git a/application/src/main/java/org/thingsboard/server/service/install/update/ImagesUpdater.java b/application/src/main/java/org/thingsboard/server/service/install/update/ImagesUpdater.java index 9894ed5360..bb5da33631 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/update/ImagesUpdater.java +++ b/application/src/main/java/org/thingsboard/server/service/install/update/ImagesUpdater.java @@ -30,7 +30,7 @@ import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.TbResourceInfo; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.util.ImageUtils; +import org.thingsboard.server.dao.util.ImageUtils; import org.thingsboard.server.common.data.widget.WidgetTypeDetails; import org.thingsboard.server.common.data.widget.WidgetsBundle; import org.thingsboard.server.dao.resource.ImageService; @@ -38,7 +38,6 @@ import org.thingsboard.server.dao.resource.ImageService; import java.nio.file.Files; import java.nio.file.Path; import java.util.Base64; -import java.util.List; import java.util.Map; import java.util.Optional; @@ -199,41 +198,44 @@ public class ImagesUpdater { return new ImageSaveResult(imageLink, !imageLink.equals(data)); } + @SneakyThrows private String saveImage(TenantId tenantId, String name, String key, byte[] imageData, String mediaType, String existingImageQuery) { - TbResourceInfo resourceInfo = imageService.getImageInfoByTenantIdAndKey(tenantId, key); - if (resourceInfo == null && !tenantId.isSysTenantId() && existingImageQuery != null) { + TbResourceInfo imageInfo = imageService.getImageInfoByTenantIdAndKey(tenantId, key); + if (imageInfo == null && !tenantId.isSysTenantId() && existingImageQuery != null) { // TODO: need to search among tenant images too (custom widgets) // List existingSystemImages = imageService.findByTenantIdAndDataAndKeyStartingWith(TenantId.SYS_TENANT_ID, imageData, existingImageQuery); // if (!existingSystemImages.isEmpty()) { -// resourceInfo = existingSystemImages.get(0); +// imageInfo = existingSystemImages.get(0); // if (existingSystemImages.size() > 1) { // log.warn("Found more than one system image resources for key {}", existingImageQuery); // } -// String link = imageService.getImageLink(resourceInfo); +// String link = imageService.getImageLink(imageInfo); // log.info("Using system image {} for {}", link, key); // return link; // } } - TbResource resource; - if (resourceInfo == null) { - resource = new TbResource(); - resource.setTenantId(tenantId); - resource.setResourceType(ResourceType.IMAGE); - resource.setResourceKey(key); + TbResource image; + if (imageInfo == null) { + image = new TbResource(); + image.setTenantId(tenantId); + image.setResourceType(ResourceType.IMAGE); + image.setResourceKey(key); } else if (tenantId.isSysTenantId()) { - resource = new TbResource(resourceInfo); + image = new TbResource(imageInfo); } else { - return imageService.getImageLink(resourceInfo); + return imageService.getImageLink(imageInfo); } - resource.setTitle(name); - resource.setFileName(key); - resource.setMediaType(mediaType); - resource.setData(imageData); - resource = imageService.saveImage(resource); - log.info("[{}] {} image '{}' ({})", tenantId, resourceInfo == null ? "Created" : "Updated", - resource.getTitle(), resource.getResourceKey()); - return imageService.getImageLink(resourceInfo); + image.setTitle(name); + image.setFileName(key); + image.setDescriptor(JacksonUtil.newObjectNode() + .put("mediaType", mediaType)); + image.setData(imageData); + + TbResourceInfo savedImage = imageService.saveImage(image); + log.info("[{}] {} image '{}' ({})", tenantId, imageInfo == null ? "Created" : "Updated", + image.getTitle(), image.getResourceKey()); + return imageService.getImageLink(savedImage); } private String getText(JsonNode jsonNode, String field) { diff --git a/application/src/main/java/org/thingsboard/server/service/install/update/SystemImagesMigrator.java b/application/src/main/java/org/thingsboard/server/service/install/update/SystemImagesMigrator.java index 52ac77b733..41e912a173 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/update/SystemImagesMigrator.java +++ b/application/src/main/java/org/thingsboard/server/service/install/update/SystemImagesMigrator.java @@ -24,7 +24,7 @@ import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.util.MediaTypeUtils; +import org.thingsboard.server.dao.util.ImageUtils; import java.io.IOException; import java.nio.charset.StandardCharsets; diff --git a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java index 0c58f6a10a..188dff20d5 100644 --- a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java +++ b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java @@ -17,29 +17,65 @@ package org.thingsboard.server.service.resource; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; -import org.springframework.web.multipart.MultipartFile; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.TbResourceInfo; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.id.TbResourceId; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.resource.ImageService; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.entitiy.AbstractTbEntityService; @Service @TbCoreComponent @RequiredArgsConstructor -public class DefaultTbImageService implements TbImageService { +public class DefaultTbImageService extends AbstractTbEntityService implements TbImageService { private final ImageService imageService; @Override - public TbResource save(TbResourceInfo imageInfo, MultipartFile imageFile, User user) { - return null; + public TbResourceInfo save(TbResource image, User user) throws Exception { + ActionType actionType = image.getId() == null ? ActionType.ADDED : ActionType.UPDATED; + TenantId tenantId = image.getTenantId(); + image.setResourceKey(image.getFileName()); // TODO: generate unique resource key file_name+idx + try { + TbResourceInfo savedImage = imageService.saveImage(image); + notificationEntityService.logEntityAction(tenantId, savedImage.getId(), savedImage, actionType, user); + return savedImage; + } catch (Exception e) { + image.setData(null); + notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.TB_RESOURCE), image, actionType, user, e); + throw e; + } } @Override - public void delete(TbResourceInfo imageInfo, User user) { + public TbResourceInfo save(TbResourceInfo imageInfo, User user) { + TenantId tenantId = imageInfo.getTenantId(); + TbResourceId imageId = imageInfo.getId(); + try { + imageInfo = imageService.saveImageInfo(imageInfo); + notificationEntityService.logEntityAction(tenantId, imageId, imageInfo, ActionType.UPDATED, user); + return imageInfo; + } catch (Exception e) { + notificationEntityService.logEntityAction(tenantId, imageId, imageInfo, ActionType.UPDATED, user, e); + throw e; + } + } + @Override + public void delete(TbResourceInfo imageInfo, User user) { + TenantId tenantId = imageInfo.getTenantId(); + TbResourceId imageId = imageInfo.getId(); + try { + imageService.deleteImage(tenantId, imageId); + notificationEntityService.logEntityAction(tenantId, imageId, imageInfo, ActionType.DELETED, user, imageId.toString()); + } catch (Exception e) { + notificationEntityService.logEntityAction(tenantId, imageId, ActionType.DELETED, user, e, imageId.toString()); + throw e; + } } } diff --git a/application/src/main/java/org/thingsboard/server/service/resource/TbImageService.java b/application/src/main/java/org/thingsboard/server/service/resource/TbImageService.java index 7039d0edaa..a1f6cf3e7e 100644 --- a/application/src/main/java/org/thingsboard/server/service/resource/TbImageService.java +++ b/application/src/main/java/org/thingsboard/server/service/resource/TbImageService.java @@ -15,14 +15,15 @@ */ package org.thingsboard.server.service.resource; -import org.springframework.web.multipart.MultipartFile; import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.TbResourceInfo; import org.thingsboard.server.common.data.User; public interface TbImageService { - TbResource save(TbResourceInfo imageInfo, MultipartFile imageFile, User user); + TbResourceInfo save(TbResource image, User user) throws Exception; + + TbResourceInfo save(TbResourceInfo imageInfo, User user); void delete(TbResourceInfo imageInfo, User user); diff --git a/application/src/test/java/org/thingsboard/server/controller/TbResourceControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/TbResourceControllerTest.java index 638d09bfdd..05770f960f 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TbResourceControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/TbResourceControllerTest.java @@ -620,10 +620,9 @@ public class TbResourceControllerTest extends AbstractControllerTest { @Test public void testUpdateResourceData_nonUpdatableResourceType() throws Exception { TbResource resource = new TbResource(); - resource.setResourceType(ResourceType.IMAGE); + resource.setResourceType(ResourceType.PKCS_12); resource.setTitle("My resource"); - resource.setFileName("image.png"); - resource.setMediaType("image/png"); + resource.setFileName("3.pks"); resource.setBase64Data(TEST_DATA); TbResource savedResource = save(resource); resource.setEtag(savedResource.getEtag()); @@ -638,7 +637,6 @@ public class TbResourceControllerTest extends AbstractControllerTest { savedResource = doPost("/api/resource", savedResource, TbResource.class); assertThat(savedResource.getTitle()).isEqualTo("Updated resource"); assertThat(savedResource.getFileName()).isEqualTo(resource.getFileName()); - assertThat(savedResource.getMediaType()).isEqualTo(resource.getMediaType()); assertThat(savedResource.getEtag()).isEqualTo(resource.getEtag()); assertThat(download(savedResource.getId())).asBase64Encoded().isEqualTo(TEST_DATA); } @@ -665,54 +663,6 @@ public class TbResourceControllerTest extends AbstractControllerTest { assertThat(download(savedResource.getId())).asBase64Encoded().isEqualTo(newData); } - @Test - public void testSaveImage_systemLevel() throws Exception { - loginSysAdmin(); - TbResource resource = new TbResource(); - resource.setResourceType(ResourceType.IMAGE); - resource.setTitle("System image"); - resource.setFileName("image.png"); - resource.setMediaType("image/png"); - resource.setBase64Data(TEST_DATA); - - TbResource savedResource = save(resource); - - loginTenantAdmin(); - MockHttpServletResponse imageResponse = doGet(getImageLink(savedResource)).andExpect(status().isOk()) - .andReturn().getResponse(); - assertThat(imageResponse.getContentAsByteArray()) - .isEqualTo(download(savedResource.getId())) - .isEqualTo(Base64.getDecoder().decode(TEST_DATA)); - assertThat(imageResponse.getContentType()).isEqualTo("image/png"); - - loginSysAdmin(); - doDelete("/api/resource/" + savedResource.getId()).andExpect(status().isOk()); - } - - @Test - public void testSaveImage_tenantLevel() throws Exception { - loginTenantAdmin(); - TbResource resource = new TbResource(); - resource.setResourceType(ResourceType.IMAGE); - resource.setTitle("My image"); - resource.setFileName("image.jpg"); - resource.setMediaType("image/jpeg"); - resource.setBase64Data(TEST_DATA); - - TbResource savedResource = save(resource); - String imageLink = getImageLink(savedResource); - - MockHttpServletResponse imageResponse = doGet(imageLink).andExpect(status().isOk()) - .andReturn().getResponse(); - assertThat(imageResponse.getContentAsByteArray()) - .isEqualTo(download(savedResource.getId())) - .isEqualTo(Base64.getDecoder().decode(TEST_DATA)); - assertThat(imageResponse.getContentType()).isEqualTo("image/jpeg"); - - loginDifferentTenant(); - doGet(imageLink).andExpect(status().isNotFound()); - } - private TbResource save(TbResource tbResource) throws Exception { return doPostWithTypedResponse("/api/resource", tbResource, new TypeReference<>() { }); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ImageService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ImageService.java index a09a00296f..3ae45e86c5 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ImageService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ImageService.java @@ -24,7 +24,7 @@ import org.thingsboard.server.common.data.page.PageLink; public interface ImageService { - TbResource saveImage(TbResource image); + TbResourceInfo saveImage(TbResource image) throws Exception; TbResourceInfo saveImageInfo(TbResourceInfo imageInfo); @@ -38,6 +38,8 @@ public interface ImageService { byte[] getImagePreview(TenantId tenantId, TbResourceId imageId); + void deleteImage(TenantId tenantId, TbResourceId imageId); + String getImageLink(TbResourceInfo imageInfo); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/ImageDescriptor.java b/common/data/src/main/java/org/thingsboard/server/common/data/ImageDescriptor.java new file mode 100644 index 0000000000..2bd7c59a85 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ImageDescriptor.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.common.data; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Data; + +@Data +@JsonInclude(JsonInclude.Include.NON_NULL) +public class ImageDescriptor { + private String mediaType; + private int width; + private int height; + private long size; + private ImageDescriptor previewDescriptor; +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/ResourceType.java b/common/data/src/main/java/org/thingsboard/server/common/data/ResourceType.java index 9ea39c43fd..b3222a9682 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/ResourceType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ResourceType.java @@ -27,7 +27,7 @@ public enum ResourceType { IMAGE(null, true, false); @Getter - private final String defaultMediaType; + private final String mediaType; @Getter private final boolean customerAccess; @Getter diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/TbResource.java b/common/data/src/main/java/org/thingsboard/server/common/data/TbResource.java index 7c44943deb..3d6c9ad95b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/TbResource.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/TbResource.java @@ -37,6 +37,9 @@ public class TbResource extends TbResourceInfo { @JsonIgnore private byte[] data; + @JsonIgnore + private byte[] preview; + public TbResource() { super(); } @@ -53,6 +56,7 @@ public class TbResource extends TbResourceInfo { super(resource); this.base64Data = resource.base64Data; this.data = resource.data; + this.preview = resource.preview; } @JsonIgnore diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java index 5b0fc9433d..31ef6b8ea1 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java @@ -16,6 +16,9 @@ package org.thingsboard.server.common.data; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; @@ -55,9 +58,8 @@ public class TbResourceInfo extends BaseData implements HasName, H @Length(fieldName = "file name") @ApiModelProperty(position = 9, value = "Resource file name.", example = "19.xml", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private String fileName; - @ApiModelProperty(position = 10, value = "Resource media type.", example = "image/png", accessMode = ApiModelProperty.AccessMode.READ_ONLY) - private String mediaType; - private ObjectNode descriptor; + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private JsonNode descriptor; public TbResourceInfo() { super(); @@ -76,7 +78,6 @@ public class TbResourceInfo extends BaseData implements HasName, H this.searchText = resourceInfo.searchText; this.etag = resourceInfo.etag; this.fileName = resourceInfo.fileName; - this.mediaType = resourceInfo.mediaType; this.descriptor = resourceInfo.descriptor; } @@ -106,4 +107,9 @@ public class TbResourceInfo extends BaseData implements HasName, H return searchText != null ? searchText : title; } + @JsonIgnore + public T getDescriptor(Class type) throws JsonProcessingException { + return descriptor != null ? mapper.treeToValue(descriptor, type) : null; + } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/util/ImageUtils.java b/common/data/src/main/java/org/thingsboard/server/common/data/util/ImageUtils.java deleted file mode 100644 index f67051a3da..0000000000 --- a/common/data/src/main/java/org/thingsboard/server/common/data/util/ImageUtils.java +++ /dev/null @@ -1,96 +0,0 @@ -/** - * Copyright © 2016-2023 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. - */ -package org.thingsboard.server.common.data.util; - -import lombok.AccessLevel; -import lombok.Data; -import lombok.NoArgsConstructor; -import org.springframework.util.MimeType; -import org.springframework.util.MimeTypeUtils; - -import javax.imageio.ImageIO; -import java.awt.image.BufferedImage; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.Map; - -@NoArgsConstructor(access = AccessLevel.PRIVATE) -public class ImageUtils { - - private static final Map mediaTypeMappings = Map.of( - "jpeg", "jpg", - "svg+xml", "svg" - ); - - public static String mediaTypeToFileExtension(String mimeType) { - String subtype = MimeTypeUtils.parseMimeType(mimeType).getSubtype(); - return mediaTypeMappings.getOrDefault(subtype, subtype); - } - - public static String fileExtensionToMediaType(String type, String extension) { - String subtype = mediaTypeMappings.entrySet().stream() - .filter(mapping -> mapping.getValue().equals(extension)) - .map(Map.Entry::getKey).findFirst().orElse(extension); - return new MimeType(type, subtype).toString(); - } - - public static ImageInfo processImage(byte[] imageData) throws IOException { - BufferedImage image = ImageIO.read(new ByteArrayInputStream(imageData)); - ImageThumbnail thumbnail = getImageThumbnail(image, 250); - return new ImageInfo(image.getWidth(), image.getHeight(), thumbnail); - } - - private static ImageThumbnail getImageThumbnail(BufferedImage originalImage, int maxDimension) throws IOException { - int originalWidth = originalImage.getWidth(); - int originalHeight = originalImage.getHeight(); - int thumbnailWidth; - int thumbnailHeight; - if (originalWidth <= maxDimension && originalHeight <= maxDimension) { - thumbnailWidth = originalWidth; - thumbnailHeight = originalHeight; - } else { - double aspectRatio = (double) originalWidth / originalHeight; - if (originalWidth > originalHeight) { - thumbnailWidth = maxDimension; - thumbnailHeight = (int) (maxDimension / aspectRatio); - } else { - thumbnailWidth = (int) (maxDimension * aspectRatio); - thumbnailHeight = maxDimension; - } - } - BufferedImage thumbnail = new BufferedImage(thumbnailWidth, thumbnailHeight, BufferedImage.TYPE_INT_RGB); - thumbnail.getGraphics().drawImage(originalImage, 0, 0, thumbnailWidth, thumbnailHeight, null); - ByteArrayOutputStream os = new ByteArrayOutputStream(); - ImageIO.write(thumbnail, "ignored", os); - return new ImageThumbnail(thumbnail.getWidth(), thumbnail.getHeight(), os.toByteArray()); - } - - @Data - public static class ImageInfo { - private final int width; - private final int height; - private final ImageThumbnail thumbnail; - } - - @Data - public static class ImageThumbnail { - private final int width; - private final int height; - private final byte[] data; - } - -} diff --git a/dao/pom.xml b/dao/pom.xml index 673a08e0c1..2ac19b9a1e 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -234,6 +234,14 @@ io.hypersistence hypersistence-utils-hibernate-55 + + org.apache.xmlgraphics + batik-transcoder + + + org.apache.xmlgraphics + batik-codec + diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index 3a77a92eb7..21cea59ee3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -499,7 +499,8 @@ public class ModelConstants { public static final String RESOURCE_FILE_NAME_COLUMN = "file_name"; public static final String RESOURCE_DATA_COLUMN = "data"; public static final String RESOURCE_ETAG_COLUMN = "etag"; - public static final String RESOURCE_MEDIA_TYPE_COLUMN = "media_type"; + public static final String RESOURCE_DESCRIPTOR_COLUMN = "descriptor"; + public static final String RESOURCE_PREVIEW_COLUMN = "preview"; /** * Ota Package constants. diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceEntity.java index f26bf100d7..41c5ebc920 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceEntity.java @@ -15,14 +15,17 @@ */ package org.thingsboard.server.dao.model.sql; +import com.fasterxml.jackson.databind.JsonNode; import lombok.Data; import lombok.EqualsAndHashCode; +import org.hibernate.annotations.Type; +import org.hibernate.annotations.TypeDef; import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.dao.model.BaseEntity; import org.thingsboard.server.dao.model.BaseSqlEntity; +import org.thingsboard.server.dao.util.mapping.JsonStringType; import javax.persistence.Column; import javax.persistence.Entity; @@ -30,10 +33,11 @@ import javax.persistence.Table; import java.util.UUID; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_DATA_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_DESCRIPTOR_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_ETAG_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_FILE_NAME_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_KEY_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_MEDIA_TYPE_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_PREVIEW_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TABLE_NAME; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TENANT_ID_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TITLE_COLUMN; @@ -43,8 +47,9 @@ import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPER @Data @EqualsAndHashCode(callSuper = true) @Entity +@TypeDef(name = "json", typeClass = JsonStringType.class) @Table(name = RESOURCE_TABLE_NAME) -public class TbResourceEntity extends BaseSqlEntity implements BaseEntity { +public class TbResourceEntity extends BaseSqlEntity { @Column(name = RESOURCE_TENANT_ID_COLUMN, columnDefinition = "uuid") private UUID tenantId; @@ -70,8 +75,12 @@ public class TbResourceEntity extends BaseSqlEntity implements BaseE @Column(name = RESOURCE_ETAG_COLUMN) private String etag; - @Column(name = RESOURCE_MEDIA_TYPE_COLUMN) - private String mediaType; + @Type(type = "json") + @Column(name = RESOURCE_DESCRIPTOR_COLUMN) + private JsonNode descriptor; + + @Column(name = RESOURCE_PREVIEW_COLUMN) + private byte[] preview; public TbResourceEntity() { } @@ -91,7 +100,8 @@ public class TbResourceEntity extends BaseSqlEntity implements BaseE this.fileName = resource.getFileName(); this.data = resource.getData(); this.etag = resource.getEtag(); - this.mediaType = resource.getMediaType(); + this.descriptor = resource.getDescriptor(); + this.preview = resource.getPreview(); } @Override @@ -106,7 +116,8 @@ public class TbResourceEntity extends BaseSqlEntity implements BaseE resource.setFileName(fileName); resource.setData(data); resource.setEtag(etag); - resource.setMediaType(mediaType); + resource.setDescriptor(descriptor); + resource.setPreview(preview); return resource; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceInfoEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceInfoEntity.java index d74f5a3c8e..9ab5024b32 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceInfoEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceInfoEntity.java @@ -15,24 +15,28 @@ */ package org.thingsboard.server.dao.model.sql; +import com.fasterxml.jackson.databind.JsonNode; import lombok.Data; import lombok.EqualsAndHashCode; +import org.hibernate.annotations.Type; +import org.hibernate.annotations.TypeDef; import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.TbResourceInfo; import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.model.BaseEntity; import org.thingsboard.server.dao.model.BaseSqlEntity; +import org.thingsboard.server.dao.util.mapping.JsonStringType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; import java.util.UUID; +import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_DESCRIPTOR_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_ETAG_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_FILE_NAME_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_KEY_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_MEDIA_TYPE_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TABLE_NAME; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TENANT_ID_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TITLE_COLUMN; @@ -42,6 +46,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPER @Data @EqualsAndHashCode(callSuper = true) @Entity +@TypeDef(name = "json", typeClass = JsonStringType.class) @Table(name = RESOURCE_TABLE_NAME) public class TbResourceInfoEntity extends BaseSqlEntity implements BaseEntity { @@ -66,8 +71,9 @@ public class TbResourceInfoEntity extends BaseSqlEntity implemen @Column(name = RESOURCE_FILE_NAME_COLUMN) private String fileName; - @Column(name = RESOURCE_MEDIA_TYPE_COLUMN) - private String mediaType; + @Type(type = "json") + @Column(name = RESOURCE_DESCRIPTOR_COLUMN) + private JsonNode descriptor; public TbResourceInfoEntity() { } @@ -84,7 +90,7 @@ public class TbResourceInfoEntity extends BaseSqlEntity implemen this.searchText = resource.getSearchText(); this.hashCode = resource.getEtag(); this.fileName = resource.getFileName(); - this.mediaType = resource.getMediaType(); + this.descriptor = resource.getDescriptor(); } @Override @@ -98,7 +104,7 @@ public class TbResourceInfoEntity extends BaseSqlEntity implemen resource.setSearchText(searchText); resource.setEtag(hashCode); resource.setFileName(fileName); - resource.setMediaType(mediaType); + resource.setDescriptor(descriptor); return resource; } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java index 29a6f98409..c046ca1c33 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java @@ -15,7 +15,11 @@ */ package org.thingsboard.server.dao.resource; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.tuple.Pair; import org.springframework.stereotype.Service; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.ImageDescriptor; import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.TbResourceInfo; @@ -25,10 +29,13 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.service.validator.ResourceDataValidator; +import org.thingsboard.server.dao.util.ImageUtils; +import org.thingsboard.server.dao.util.ImageUtils.ProcessedImage; import java.util.Set; @Service +@Slf4j public class BaseImageService extends BaseResourceService implements ImageService { public BaseImageService(TbResourceDao resourceDao, TbResourceInfoDao resourceInfoDao, ResourceDataValidator resourceValidator) { @@ -36,18 +43,39 @@ public class BaseImageService extends BaseResourceService implements ImageServic } @Override - public TbResource saveImage(TbResource image) { + public TbResourceInfo saveImage(TbResource image) throws Exception { resourceValidator.validate(image, TbResourceInfo::getTenantId); - if (image.getData() != null) { - } - // generate preview, etc. - return saveResource(image, false); + ImageDescriptor descriptor = image.getDescriptor(ImageDescriptor.class); + Pair result = processImage(image.getData(), descriptor); + image.setDescriptor(JacksonUtil.valueToTree(result.getLeft())); + image.setPreview(result.getRight()); + + image = saveResource(image, false); + return new TbResourceInfo(image); + } + + private Pair processImage(byte[] data, ImageDescriptor descriptor) throws Exception { + ProcessedImage image = ImageUtils.processImage(data, descriptor.getMediaType(), 250); + ProcessedImage preview = image.getPreview(); + + descriptor.setWidth(image.getWidth()); + descriptor.setHeight(image.getHeight()); + descriptor.setSize(image.getSize()); + + ImageDescriptor previewDescriptor = new ImageDescriptor(); + previewDescriptor.setWidth(preview.getWidth()); + previewDescriptor.setHeight(preview.getHeight()); + previewDescriptor.setMediaType(preview.getMediaType()); + previewDescriptor.setSize(preview.getSize()); + descriptor.setPreviewDescriptor(previewDescriptor); + + return Pair.of(descriptor, preview.getData()); } @Override public TbResourceInfo saveImageInfo(TbResourceInfo imageInfo) { - return null; + return saveResource(new TbResource(imageInfo)); } @Override @@ -80,7 +108,12 @@ public class BaseImageService extends BaseResourceService implements ImageServic @Override public byte[] getImagePreview(TenantId tenantId, TbResourceId imageId) { - return new byte[0]; + return resourceDao.getResourcePreview(tenantId, imageId); + } + + @Override + public void deleteImage(TenantId tenantId, TbResourceId imageId) { + deleteResource(tenantId, imageId); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java index 30748e5ab3..bb46af22ff 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java @@ -15,12 +15,12 @@ */ package org.thingsboard.server.dao.resource; -import com.datastax.oss.driver.api.core.uuid.Uuids; import com.google.common.hash.Hashing; import com.google.common.util.concurrent.ListenableFuture; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.hibernate.exception.ConstraintViolationException; +import org.springframework.context.annotation.Primary; import org.springframework.stereotype.Service; import org.springframework.transaction.event.TransactionalEventListener; import org.thingsboard.server.cache.resourceInfo.ResourceInfoCacheKey; @@ -46,7 +46,6 @@ import org.thingsboard.server.dao.service.validator.ResourceDataValidator; import java.util.List; import java.util.Optional; -import java.util.UUID; import static org.thingsboard.server.dao.device.DeviceServiceImpl.INCORRECT_TENANT_ID; import static org.thingsboard.server.dao.service.Validator.validateId; @@ -54,6 +53,7 @@ import static org.thingsboard.server.dao.service.Validator.validateId; @Service("TbResourceDaoService") @Slf4j @AllArgsConstructor +@Primary public class BaseResourceService extends AbstractCachedEntityService implements ResourceService { public static final String INCORRECT_RESOURCE_ID = "Incorrect resourceId "; diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/TbResourceDao.java b/dao/src/main/java/org/thingsboard/server/dao/resource/TbResourceDao.java index 4bf6f752a0..43b79c5213 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/TbResourceDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/TbResourceDao.java @@ -43,4 +43,6 @@ public interface TbResourceDao extends Dao, TenantEntityWithDataDao byte[] getResourceData(TenantId tenantId, TbResourceId resourceId); + byte[] getResourcePreview(TenantId tenantId, TbResourceId resourceId); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/ResourceDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/ResourceDataValidator.java index 4d5027492d..affde231f5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/ResourceDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/ResourceDataValidator.java @@ -17,8 +17,6 @@ package org.thingsboard.server.dao.service.validator; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; -import org.springframework.http.InvalidMediaTypeException; -import org.springframework.http.MediaType; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.TbResource; @@ -94,19 +92,6 @@ public class ResourceDataValidator extends DataValidator { long maxSumResourcesDataInBytes = profileConfiguration.getMaxResourcesInBytes(); validateMaxSumDataSizePerTenant(tenantId, resourceDao, maxSumResourcesDataInBytes, resource.getData().length, TB_RESOURCE); } - if (resource.getResourceType().getDefaultMediaType() != null) { - resource.setMediaType(resource.getResourceType().getDefaultMediaType()); - } else { - if (resource.getMediaType() == null) { - throw new DataValidationException("Media type is required"); - } else { - try { - MediaType.parseMediaType(resource.getMediaType()); - } catch (InvalidMediaTypeException e) { - throw new DataValidationException("Invalid media type", e); - } - } - } if (StringUtils.isEmpty(resource.getFileName())) { throw new DataValidationException("Resource file name should be specified!"); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceDao.java index 3d163e0d4f..28bebf3e4f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceDao.java @@ -99,6 +99,11 @@ public class JpaTbResourceDao extends JpaAbstractDao findAllTenantResourcesByTenantId(@Param("tenantId") UUID tenantId, @Param("systemAdminId") UUID sysadminId, - @Param("resourceTypes") Collection resourceTypes, + @Param("resourceTypes") List resourceTypes, @Param("searchText") String searchText, Pageable pageable); @Query("SELECT ri FROM TbResourceInfoEntity ri WHERE " + "ri.tenantId = :tenantId " + - "AND (:resourceTypes IS NULL OR ri.resourceType IN :resourceTypes)" + + "AND ri.resourceType IN :resourceTypes " + "AND (:searchText IS NULL OR ilike(ri.title, CONCAT('%', :searchText, '%')) = true)") Page findTenantResourcesByTenantId(@Param("tenantId") UUID tenantId, - @Param("resourceTypes") Collection resourceTypes, + @Param("resourceTypes") List resourceTypes, @Param("searchText") String searchText, Pageable pageable); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceRepository.java index 41c5e005dd..6a47e95ed6 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceRepository.java @@ -84,4 +84,7 @@ public interface TbResourceRepository extends JpaRepository mediaTypeMappings = Map.of( + "jpeg", "jpg", + "svg+xml", "svg" + ); + + public static String mediaTypeToFileExtension(String mimeType) { + String subtype = MimeTypeUtils.parseMimeType(mimeType).getSubtype(); + return mediaTypeMappings.getOrDefault(subtype, subtype); + } + + public static String fileExtensionToMediaType(String type, String extension) { + String subtype = mediaTypeMappings.entrySet().stream() + .filter(mapping -> mapping.getValue().equals(extension)) + .map(Map.Entry::getKey).findFirst().orElse(extension); + return new MimeType(type, subtype).toString(); + } + + public static ProcessedImage processImage(byte[] data, String mediaType, int thumbnailMaxDimension) throws Exception { + if (mediaTypeToFileExtension(mediaType).equals("svg")) { + return processSvgImage(data, thumbnailMaxDimension); + } + + BufferedImage bufferedImage = ImageIO.read(new ByteArrayInputStream(data)); + ProcessedImage image = new ProcessedImage(); + image.setMediaType(mediaType); + image.setWidth(bufferedImage.getWidth()); + image.setHeight(bufferedImage.getHeight()); + image.setData(data); + image.setSize(data.length); + + ProcessedImage preview = new ProcessedImage(); + int[] thumbnailDimensions = getThumbnailDimensions(image.getWidth(), image.getHeight(), thumbnailMaxDimension); + preview.setWidth(thumbnailDimensions[0]); + preview.setHeight(thumbnailDimensions[1]); + + if (preview.getWidth() == image.getWidth() && preview.getHeight() == image.getHeight()) { + if (mediaType.equals("image/png")) { + preview.setMediaType(mediaType); + preview.setData(null); + preview.setSize(data.length); + image.setPreview(preview); + return image; + } + } + + BufferedImage thumbnail = new BufferedImage(preview.getWidth(), preview.getHeight(), BufferedImage.TYPE_INT_RGB); + thumbnail.getGraphics().drawImage(bufferedImage, 0, 0, preview.getWidth(), preview.getHeight(), null); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ImageIO.write(thumbnail, "png", out); + + preview.setMediaType("image/png"); + preview.setData(out.toByteArray()); + preview.setSize(preview.getData().length); + image.setPreview(preview); + return image; + } + + public static ProcessedImage processSvgImage(byte[] data, int thumbnailMaxDimension) throws Exception { + ProcessedImage image = new ProcessedImage(); + image.setWidth(0); + image.setHeight(0); + image.setData(data); + image.setSize(data.length); + + PNGTranscoder transcoder = new PNGTranscoder(); + transcoder.addTranscodingHint(PNGTranscoder.KEY_MAX_WIDTH, (float) thumbnailMaxDimension); + transcoder.addTranscodingHint(PNGTranscoder.KEY_MAX_HEIGHT, (float) thumbnailMaxDimension); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + transcoder.transcode(new TranscoderInput(new ByteArrayInputStream(data)), new TranscoderOutput(out)); + byte[] pngThumbnail = out.toByteArray(); + + ProcessedImage preview = new ProcessedImage(); + preview.setWidth(thumbnailMaxDimension); + preview.setHeight(thumbnailMaxDimension); + preview.setMediaType("image/png"); + preview.setData(pngThumbnail); + preview.setSize(pngThumbnail.length); + image.setPreview(preview); + return image; + } + + private static int[] getThumbnailDimensions(int originalWidth, int originalHeight, int maxDimension) { + if (originalWidth <= maxDimension && originalHeight <= maxDimension) { + return new int[]{originalWidth, originalHeight}; + } + int thumbnailWidth; + int thumbnailHeight; + double aspectRatio = (double) originalWidth / originalHeight; + if (originalWidth > originalHeight) { + thumbnailWidth = maxDimension; + thumbnailHeight = (int) (maxDimension / aspectRatio); + } else { + thumbnailWidth = (int) (maxDimension * aspectRatio); + thumbnailHeight = maxDimension; + } + return new int[]{thumbnailWidth, thumbnailHeight}; + } + + @Data + public static class ProcessedImage { + private String mediaType; + private int width; + private int height; + private byte[] data; + private long size; + private ProcessedImage preview; + } + +} diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index db59878c39..6b30151536 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -716,7 +716,8 @@ CREATE TABLE IF NOT EXISTS resource ( file_name varchar(255) NOT NULL, data bytea, etag varchar, - media_type varchar(255), + descriptor varchar, + preview bytea, CONSTRAINT resource_unq_key UNIQUE (tenant_id, resource_type, resource_key) ); diff --git a/pom.xml b/pom.xml index d5b517c48e..aceeb67cc2 100755 --- a/pom.xml +++ b/pom.xml @@ -153,6 +153,7 @@ 1.12.1 6.4.2 1.34.1 + 1.14 @@ -2029,6 +2030,16 @@ google-oauth-client ${google-oauth-client.version} + + org.apache.xmlgraphics + batik-transcoder + ${apache-xmlgraphics.version} + + + org.apache.xmlgraphics + batik-codec + ${apache-xmlgraphics.version} + diff --git a/ui-ngx/src/app/shared/models/resource.models.ts b/ui-ngx/src/app/shared/models/resource.models.ts index b9a73a416c..61d413f60e 100644 --- a/ui-ngx/src/app/shared/models/resource.models.ts +++ b/ui-ngx/src/app/shared/models/resource.models.ts @@ -61,8 +61,6 @@ export interface ResourceInfo extends Omit, 'name' | 'lab title?: string; resourceType: ResourceType; fileName: string; - mediaType: string; - link: string; } export interface Resource extends ResourceInfo { From 4f22ef9733a38d486b525e7d08c08a978a8bd931 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Tue, 14 Nov 2023 17:36:51 +0200 Subject: [PATCH 126/252] created PubSubExecutor to use each time publisher is created and do not produce too much threads created --- .../server/actors/ActorSystemContext.java | 5 ++++ .../actors/ruleChain/DefaultTbContext.java | 6 +++++ .../executors/PubSubExecutorService.java | 26 +++++++++++++++++++ .../pubsub/TbPubSubProducerTemplate.java | 13 +++++++++- common/util/pom.xml | 4 +++ .../common/util/PubSubExecutor.java | 7 +++++ .../rule/engine/api/TbContext.java | 3 +++ .../rule/engine/gcp/pubsub/TbPubSubNode.java | 6 +++-- 8 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/executors/PubSubExecutorService.java create mode 100644 common/util/src/main/java/org/thingsboard/common/util/PubSubExecutor.java diff --git a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java index 3d4340f6c9..b9f539c877 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java @@ -29,6 +29,7 @@ import org.springframework.data.redis.core.RedisTemplate; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.service.executors.PubSubExecutorService; import org.thingsboard.rule.engine.api.MailService; import org.thingsboard.rule.engine.api.NotificationCenter; import org.thingsboard.rule.engine.api.SmsService; @@ -322,6 +323,10 @@ public class ActorSystemContext { @Getter private NotificationExecutorService notificationExecutor; + @Autowired + @Getter + private PubSubExecutorService pubSubExecutorService; + @Autowired @Getter private SharedEventLoopGroupService sharedEventLoopGroupService; diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java index ca1d174322..299b6274b0 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java @@ -22,6 +22,7 @@ import lombok.extern.slf4j.Slf4j; import org.bouncycastle.util.Arrays; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ListeningExecutor; +import org.thingsboard.server.service.executors.PubSubExecutorService; import org.thingsboard.rule.engine.api.MailService; import org.thingsboard.rule.engine.api.NotificationCenter; import org.thingsboard.rule.engine.api.RuleEngineAlarmService; @@ -538,6 +539,11 @@ class DefaultTbContext implements TbContext { return mainCtx.getNotificationExecutor(); } + @Override + public PubSubExecutorService getPubsubExecutor() { + return mainCtx.getPubSubExecutorService(); + } + @Override @Deprecated public ScriptEngine createJsScriptEngine(String script, String... argNames) { diff --git a/application/src/main/java/org/thingsboard/server/service/executors/PubSubExecutorService.java b/application/src/main/java/org/thingsboard/server/service/executors/PubSubExecutorService.java new file mode 100644 index 0000000000..22d55cc673 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/executors/PubSubExecutorService.java @@ -0,0 +1,26 @@ +package org.thingsboard.server.service.executors; + +import com.google.api.gax.core.FixedExecutorProvider; +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.PubSubExecutor; +import org.thingsboard.common.util.ThingsBoardThreadFactory; + +import javax.annotation.PostConstruct; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; + +@Component +public class PubSubExecutorService implements PubSubExecutor { + private static final int THREADS_PER_CPU = 5; + private FixedExecutorProvider fixedExecutorProvider; + + @PostConstruct + public void init() { + ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(THREADS_PER_CPU * Runtime.getRuntime().availableProcessors(), ThingsBoardThreadFactory.forName("tb-pubsub-producer-scheduler"));; + this.fixedExecutorProvider = FixedExecutorProvider.create(scheduler); + } + + public FixedExecutorProvider getExecutorProvider() { + return fixedExecutorProvider; + } +} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubProducerTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubProducerTemplate.java index 0d84a8f839..46c6bb6413 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubProducerTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubProducerTemplate.java @@ -18,12 +18,14 @@ package org.thingsboard.server.queue.pubsub; import com.google.api.core.ApiFuture; import com.google.api.core.ApiFutureCallback; import com.google.api.core.ApiFutures; +import com.google.api.gax.core.FixedExecutorProvider; import com.google.cloud.pubsub.v1.Publisher; import com.google.gson.Gson; import com.google.protobuf.ByteString; import com.google.pubsub.v1.ProjectTopicName; import com.google.pubsub.v1.PubsubMessage; import lombok.extern.slf4j.Slf4j; +import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.queue.TbQueueAdmin; import org.thingsboard.server.queue.TbQueueCallback; @@ -36,6 +38,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @Slf4j @@ -50,11 +53,16 @@ public class TbPubSubProducerTemplate implements TbQueuePr private final Map publisherMap = new ConcurrentHashMap<>(); private final ExecutorService pubExecutor = Executors.newCachedThreadPool(); + private static final int THREADS_PER_CPU = 5; + private final FixedExecutorProvider fixedExecutorProvider; public TbPubSubProducerTemplate(TbQueueAdmin admin, TbPubSubSettings pubSubSettings, String defaultTopic) { this.defaultTopic = defaultTopic; this.admin = admin; this.pubSubSettings = pubSubSettings; + + ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(THREADS_PER_CPU * Runtime.getRuntime().availableProcessors(), ThingsBoardThreadFactory.forName("tb-pubsub-producer-scheduler"));; + fixedExecutorProvider = FixedExecutorProvider.create(scheduler); } @Override @@ -120,7 +128,10 @@ public class TbPubSubProducerTemplate implements TbQueuePr try { admin.createTopicIfNotExists(topic); ProjectTopicName topicName = ProjectTopicName.of(pubSubSettings.getProjectId(), topic); - Publisher publisher = Publisher.newBuilder(topicName).setCredentialsProvider(pubSubSettings.getCredentialsProvider()).build(); + Publisher publisher = Publisher.newBuilder(topicName) + .setCredentialsProvider(pubSubSettings.getCredentialsProvider()) + .setExecutorProvider(fixedExecutorProvider) + .build(); publisherMap.put(topic, publisher); return publisher; } catch (IOException e) { diff --git a/common/util/pom.xml b/common/util/pom.xml index eab8ae982b..c2f5dfea6c 100644 --- a/common/util/pom.xml +++ b/common/util/pom.xml @@ -53,6 +53,10 @@ guava provided + + com.google.cloud + google-cloud-pubsub + javax.annotation javax.annotation-api diff --git a/common/util/src/main/java/org/thingsboard/common/util/PubSubExecutor.java b/common/util/src/main/java/org/thingsboard/common/util/PubSubExecutor.java new file mode 100644 index 0000000000..5354102578 --- /dev/null +++ b/common/util/src/main/java/org/thingsboard/common/util/PubSubExecutor.java @@ -0,0 +1,7 @@ +package org.thingsboard.common.util; + +import com.google.api.gax.core.FixedExecutorProvider; + +public interface PubSubExecutor { + FixedExecutorProvider getExecutorProvider(); +} diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java index e645110a9d..cd71626afd 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java @@ -17,6 +17,7 @@ package org.thingsboard.rule.engine.api; import io.netty.channel.EventLoopGroup; import org.thingsboard.common.util.ListeningExecutor; +import org.thingsboard.common.util.PubSubExecutor; import org.thingsboard.rule.engine.api.slack.SlackService; import org.thingsboard.rule.engine.api.sms.SmsSenderFactory; import org.thingsboard.server.cluster.TbClusterService; @@ -318,6 +319,8 @@ public interface TbContext { ListeningExecutor getNotificationExecutor(); + PubSubExecutor getPubsubExecutor(); + MailService getMailService(boolean isSystem); SmsService getSmsService(); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNode.java index c55113783c..4c24bdf1cb 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNode.java @@ -20,6 +20,7 @@ import com.google.api.core.ApiFutureCallback; import com.google.api.core.ApiFutures; import com.google.api.gax.core.CredentialsProvider; import com.google.api.gax.core.FixedCredentialsProvider; +import com.google.api.gax.core.FixedExecutorProvider; import com.google.api.gax.retrying.RetrySettings; import com.google.auth.oauth2.ServiceAccountCredentials; import com.google.cloud.pubsub.v1.Publisher; @@ -68,7 +69,7 @@ public class TbPubSubNode extends TbAbstractExternalNode { super.init(ctx); this.config = TbNodeUtils.convert(configuration, TbPubSubNodeConfiguration.class); try { - this.pubSubClient = initPubSubClient(); + this.pubSubClient = initPubSubClient(ctx.getPubsubExecutor().getExecutorProvider()); } catch (Exception e) { throw new TbNodeException(e); } @@ -128,7 +129,7 @@ public class TbPubSubNode extends TbAbstractExternalNode { return TbMsg.transformMsgMetadata(origMsg, metaData); } - private Publisher initPubSubClient() throws IOException { + private Publisher initPubSubClient(FixedExecutorProvider fixedExecutorProvider) throws IOException { ProjectTopicName topicName = ProjectTopicName.of(config.getProjectId(), config.getTopicName()); ServiceAccountCredentials credentials = ServiceAccountCredentials.fromStream( @@ -148,6 +149,7 @@ public class TbPubSubNode extends TbAbstractExternalNode { return Publisher.newBuilder(topicName) .setCredentialsProvider(credProvider) .setRetrySettings(retrySettings) + .setExecutorProvider(fixedExecutorProvider) .build(); } } From 6d0d1c02111509ce0283bd2931b487f066e2ba3e Mon Sep 17 00:00:00 2001 From: ShvaykaD Date: Tue, 14 Nov 2023 19:34:54 +0200 Subject: [PATCH 127/252] validate node only after upgrade is called --- .../server/dao/rule/BaseRuleChainService.java | 3 ++- .../dao/service/validator/RuleChainDataValidator.java | 9 ++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java index 956e4b83f6..6257bb0420 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java @@ -155,7 +155,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC if (ruleChain == null) { return RuleChainUpdateResult.failed(); } - RuleChainDataValidator.validateMetaData(ruleChainMetaData); + RuleChainDataValidator.validateMetaDataFieldsAndConnections(ruleChainMetaData); List nodes = ruleChainMetaData.getNodes(); List toAddOrUpdate = new ArrayList<>(); @@ -194,6 +194,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC for (RuleNode node : toAddOrUpdate) { node.setRuleChainId(ruleChainId); node = ruleNodeUpdater.apply(node); + RuleChainDataValidator.validateRuleNode(node); RuleNode savedNode = ruleNodeDao.save(tenantId, node); relations.add(new EntityRelation(ruleChainMetaData.getRuleChainId(), savedNode.getId(), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN)); diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/RuleChainDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/RuleChainDataValidator.java index 43878adc93..76dad19bcd 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/RuleChainDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/RuleChainDataValidator.java @@ -87,15 +87,18 @@ public class RuleChainDataValidator extends DataValidator { } public static List validateMetaData(RuleChainMetaData ruleChainMetaData) { - ConstraintValidator.validateFields(ruleChainMetaData); - List throwables = ruleChainMetaData.getNodes().stream() + validateMetaDataFieldsAndConnections(ruleChainMetaData); + return ruleChainMetaData.getNodes().stream() .map(RuleChainDataValidator::validateRuleNode) .filter(Objects::nonNull) .collect(Collectors.toList()); + } + + public static void validateMetaDataFieldsAndConnections(RuleChainMetaData ruleChainMetaData) { + ConstraintValidator.validateFields(ruleChainMetaData); if (CollectionUtils.isNotEmpty(ruleChainMetaData.getConnections())) { validateCircles(ruleChainMetaData.getConnections()); } - return throwables; } public static Throwable validateRuleNode(RuleNode ruleNode) { From deaf06970a59f1083af2b7eb214b2ac8de9cdf52 Mon Sep 17 00:00:00 2001 From: imbeacon Date: Wed, 15 Nov 2023 07:56:06 +0200 Subject: [PATCH 128/252] Adapted tests to match new amount of dashboards for tenant --- .../controller/DashboardControllerTest.java | 21 +++++++++++++------ .../server/controller/HomePageApiTest.java | 2 +- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/DashboardControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/DashboardControllerTest.java index 6f0d6f9465..fb8064d6d5 100644 --- a/application/src/test/java/org/thingsboard/server/controller/DashboardControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/DashboardControllerTest.java @@ -327,7 +327,18 @@ public class DashboardControllerTest extends AbstractControllerTest { @Test public void testFindTenantDashboards() throws Exception { - List dashboards = new ArrayList<>(); + List expectedDashboards = new ArrayList<>(); + PageLink pageLink = new PageLink(24); + PageData pageData = null; + do { + pageData = doGetTypedWithPageLink("/api/tenant/dashboards?", + new TypeReference>() { + }, pageLink); + expectedDashboards.addAll(pageData.getData()); + if (pageData.hasNext()) { + pageLink = pageLink.nextPageLink(); + } + } while (pageData.hasNext()); Mockito.reset(tbClusterService, auditLogService); @@ -335,7 +346,7 @@ public class DashboardControllerTest extends AbstractControllerTest { for (int i = 0; i < cntEntity; i++) { Dashboard dashboard = new Dashboard(); dashboard.setTitle("Dashboard" + i); - dashboards.add(new DashboardInfo(doPost("/api/dashboard", dashboard, Dashboard.class))); + expectedDashboards.add(new DashboardInfo(doPost("/api/dashboard", dashboard, Dashboard.class))); } testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new Dashboard(), new Dashboard(), @@ -343,8 +354,6 @@ public class DashboardControllerTest extends AbstractControllerTest { ActionType.ADDED, cntEntity, cntEntity, cntEntity); List loadedDashboards = new ArrayList<>(); - PageLink pageLink = new PageLink(24); - PageData pageData = null; do { pageData = doGetTypedWithPageLink("/api/tenant/dashboards?", new TypeReference>() { @@ -355,10 +364,10 @@ public class DashboardControllerTest extends AbstractControllerTest { } } while (pageData.hasNext()); - dashboards.sort(idComparator); + expectedDashboards.sort(idComparator); loadedDashboards.sort(idComparator); - Assert.assertEquals(dashboards, loadedDashboards); + Assert.assertEquals(expectedDashboards, loadedDashboards); } @Test diff --git a/application/src/test/java/org/thingsboard/server/controller/HomePageApiTest.java b/application/src/test/java/org/thingsboard/server/controller/HomePageApiTest.java index d020fef1cb..c498b73815 100644 --- a/application/src/test/java/org/thingsboard/server/controller/HomePageApiTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/HomePageApiTest.java @@ -408,7 +408,7 @@ public class HomePageApiTest extends AbstractControllerTest { Assert.assertEquals(2, usageInfo.getUsers()); Assert.assertEquals(configuration.getMaxUsers(), usageInfo.getMaxUsers()); - Assert.assertEquals(0, usageInfo.getDashboards()); + Assert.assertEquals(1, usageInfo.getDashboards()); Assert.assertEquals(configuration.getMaxDashboards(), usageInfo.getMaxDashboards()); Assert.assertEquals(0, usageInfo.getTransportMessages()); From c82e3d4791223901dbbf86182f7ea4bd0854e164 Mon Sep 17 00:00:00 2001 From: imbeacon Date: Mon, 11 Sep 2023 19:30:58 +0300 Subject: [PATCH 129/252] Added support for NON CoAP messages --- .../server/transport/coap/CoapTestClient.java | 32 +- .../client/CoapClientIntegrationTest.java | 314 ++++++++++++++++++ .../callback/AbstractSyncSessionCallback.java | 1 + .../coap/callback/CoapOkCallback.java | 4 +- 4 files changed, 346 insertions(+), 5 deletions(-) create mode 100644 application/src/test/java/org/thingsboard/server/transport/coap/client/CoapClientIntegrationTest.java diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/CoapTestClient.java b/application/src/test/java/org/thingsboard/server/transport/coap/CoapTestClient.java index 6c5f682a4f..3124571dc8 100644 --- a/application/src/test/java/org/thingsboard/server/transport/coap/CoapTestClient.java +++ b/application/src/test/java/org/thingsboard/server/transport/coap/CoapTestClient.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.transport.coap; +import lombok.Getter; import org.eclipse.californium.core.CoapClient; import org.eclipse.californium.core.CoapHandler; import org.eclipse.californium.core.CoapObserveRelation; @@ -34,7 +35,10 @@ public class CoapTestClient { private final CoapClient client; - public CoapTestClient(){ + @Getter + private CoAP.Type type = CoAP.Type.CON; + + public CoapTestClient() { this.client = createClient(); } @@ -80,9 +84,13 @@ public class CoapTestClient { return client.setTimeout(CLIENT_REQUEST_TIMEOUT).get(); } - public CoapObserveRelation getObserveRelation(CoapTestCallback callback){ + public CoapObserveRelation getObserveRelation(CoapTestCallback callback) { + return getObserveRelation(callback, true); + } + + public CoapObserveRelation getObserveRelation(CoapTestCallback callback, boolean confirmable) { Request request = Request.newGet().setObserve(); - request.setType(CoAP.Type.CON); + request.setType(confirmable ? CoAP.Type.CON : CoAP.Type.NON); return client.observe(request, callback); } @@ -94,12 +102,28 @@ public class CoapTestClient { } public void setURI(String accessToken, FeatureType featureType) { - if (featureType == null){ + if (featureType == null) { featureType = FeatureType.ATTRIBUTES; } setURI(getFeatureTokenUrl(accessToken, featureType)); } + public void useCONs() { + if (client == null) { + throw new RuntimeException("Failed to connect! CoapClient is not initialized!"); + } + type = CoAP.Type.CON; + client.useCONs(); + } + + public void useNONs() { + if (client == null) { + throw new RuntimeException("Failed to connect! CoapClient is not initialized!"); + } + type = CoAP.Type.NON; + client.useNONs(); + } + private CoapClient createClient() { return new CoapClient(); } diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/client/CoapClientIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/client/CoapClientIntegrationTest.java new file mode 100644 index 0000000000..3760935d41 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/client/CoapClientIntegrationTest.java @@ -0,0 +1,314 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.transport.coap.client; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.californium.core.CoapHandler; +import org.eclipse.californium.core.CoapObserveRelation; +import org.eclipse.californium.core.CoapResponse; +import org.eclipse.californium.core.coap.CoAP; +import org.eclipse.californium.core.coap.MediaTypeRegistry; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.query.EntityKey; +import org.thingsboard.server.common.data.query.EntityKeyType; +import org.thingsboard.server.common.data.query.SingleEntityFilter; +import org.thingsboard.server.common.msg.session.FeatureType; +import org.thingsboard.server.dao.service.DaoSqlTest; +import org.thingsboard.server.transport.coap.AbstractCoapIntegrationTest; +import org.thingsboard.server.transport.coap.CoapTestCallback; +import org.thingsboard.server.transport.coap.CoapTestClient; +import org.thingsboard.server.transport.coap.CoapTestConfigProperties; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.thingsboard.server.common.data.query.EntityKeyType.CLIENT_ATTRIBUTE; +import static org.thingsboard.server.common.data.query.EntityKeyType.SHARED_ATTRIBUTE; + +@Slf4j +@DaoSqlTest +public class CoapClientIntegrationTest extends AbstractCoapIntegrationTest { + + private static final String PAYLOAD_VALUES_STR = "{\"key1\":\"value1\", \"key2\":true, \"key3\": 3.0, \"key4\": 4," + + " \"key5\": {\"someNumber\": 42, \"someArray\": [1,2,3], \"someNestedObject\": {\"key\": \"value\"}}}"; + private static final List EXPECTED_KEYS = Arrays.asList("key1", "key2", "key3", "key4", "key5"); + private static final String DEVICE_RESPONSE = "{\"value1\":\"A\",\"value2\":\"B\"}"; + + + @Before + public void beforeTest() throws Exception { + CoapTestConfigProperties configProperties = CoapTestConfigProperties.builder() + .deviceName("Test Post Attributes device") + .build(); + processBeforeTest(configProperties); + } + + @After + public void afterTest() throws Exception { + processAfterTest(); + } + + @Test + public void testConfirmableRequests() throws Exception { + boolean confirmable = true; + processAttributesTest(confirmable); + processTwoWayRpcTest(confirmable); + processTestRequestAttributesValuesFromTheServer(confirmable); + } + + @Test + public void testNonConfirmableRequests() throws Exception { + boolean confirmable = false; + processAttributesTest(confirmable); + processTwoWayRpcTest(confirmable); + processTestRequestAttributesValuesFromTheServer(confirmable); + } + + protected void processAttributesTest(boolean confirmable) throws Exception { + client = createClientForFeatureWithConfirmableParameter(FeatureType.ATTRIBUTES, confirmable); + CoapResponse coapResponse = client.postMethod(PAYLOAD_VALUES_STR.getBytes()); + assertEquals(CoAP.ResponseCode.CREATED, coapResponse.getCode()); + validateConfirmableFlag(client, coapResponse); + + DeviceId deviceId = savedDevice.getId(); + List actualKeys = getActualKeysList(deviceId); + assertNotNull(actualKeys); + + Set actualKeySet = new HashSet<>(actualKeys); + Set expectedKeySet = new HashSet<>(EXPECTED_KEYS); + assertEquals(expectedKeySet, actualKeySet); + + String attributesValuesUrl = "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/attributes/CLIENT_SCOPE?keys=" + String.join(",", actualKeySet); + ; + List> values = doGetAsyncTyped(attributesValuesUrl, new TypeReference<>() { + }); + assertAttributesValues(values, actualKeySet); + String deleteAttributesUrl = "/api/plugins/telemetry/DEVICE/" + deviceId + "/CLIENT_SCOPE?keys=" + String.join(",", actualKeySet); + doDelete(deleteAttributesUrl); + } + + protected void processTwoWayRpcTest(boolean confirmable) throws Exception { + client = createClientForFeatureWithConfirmableParameter(FeatureType.RPC, confirmable); + CoapTestCallback callbackCoap = new TestCoapCallbackForRPC(client); + + CoapObserveRelation observeRelation = client.getObserveRelation(callbackCoap, confirmable); + String awaitAlias = "await Two Way Rpc (client.getObserveRelation)"; + await(awaitAlias) + .atMost(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS) + .until(() -> CoAP.ResponseCode.VALID.equals(callbackCoap.getResponseCode()) && + callbackCoap.getObserve() != null && + 0 == callbackCoap.getObserve()); + validateCurrentStateNotification(callbackCoap); + + String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"26\",\"value\": 1}}"; + String deviceId = savedDevice.getId().getId().toString(); + int expectedObserveCountAfterGpioRequest1 = callbackCoap.getObserve() + 1; + String actualResult = doPostAsync("/api/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk()); + awaitAlias = "await Two Way Rpc (setGpio(method, params, value) first"; + await(awaitAlias) + .atMost(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS) + .until(() -> CoAP.ResponseCode.CONTENT.equals(callbackCoap.getResponseCode()) && + callbackCoap.getObserve() != null && + expectedObserveCountAfterGpioRequest1 == callbackCoap.getObserve()); + validateTwoWayStateChangedNotification(callbackCoap, actualResult); + + int expectedObserveCountAfterGpioRequest2 = callbackCoap.getObserve() + 1; + actualResult = doPostAsync("/api/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk()); + awaitAlias = "await Two Way Rpc (setGpio(method, params, value) second"; + await(awaitAlias) + .atMost(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS) + .until(() -> CoAP.ResponseCode.CONTENT.equals(callbackCoap.getResponseCode()) && + callbackCoap.getObserve() != null && + expectedObserveCountAfterGpioRequest2 == callbackCoap.getObserve()); + + validateTwoWayStateChangedNotification(callbackCoap, actualResult); + + observeRelation.proactiveCancel(); + assertTrue(observeRelation.isCanceled()); + } + + protected void processTestRequestAttributesValuesFromTheServer(boolean confirmable) throws Exception { + client = createClientForFeatureWithConfirmableParameter(FeatureType.ATTRIBUTES, confirmable); + SingleEntityFilter dtf = new SingleEntityFilter(); + dtf.setSingleEntity(savedDevice.getId()); + List csKeys = getEntityKeys(CLIENT_ATTRIBUTE); + List shKeys = getEntityKeys(SHARED_ATTRIBUTE); + List keys = new ArrayList<>(); + keys.addAll(csKeys); + keys.addAll(shKeys); + getWsClient().subscribeLatestUpdate(keys, dtf); + getWsClient().registerWaitForUpdate(2); + + doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", + PAYLOAD_VALUES_STR, String.class, status().isOk()); + + CoapResponse coapResponse = client.postMethod(PAYLOAD_VALUES_STR); + assertEquals(CoAP.ResponseCode.CREATED, coapResponse.getCode()); + + String update = getWsClient().waitForUpdate(); + assertThat(update).as("ws update received").isNotBlank(); + + String keysParam = String.join(",", EXPECTED_KEYS); + String featureTokenUrl = CoapTestClient.getFeatureTokenUrl(accessToken, FeatureType.ATTRIBUTES) + "?clientKeys=" + keysParam + "&sharedKeys=" + keysParam; + client.setURI(featureTokenUrl); + CoapResponse response = client.getMethod(); + validateConfirmableFlag(client, response); + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + protected void assertAttributesValues(List> deviceValues, Set keySet) { + for (Map map : deviceValues) { + String key = (String) map.get("key"); + Object value = map.get("value"); + assertTrue(keySet.contains(key)); + switch (key) { + case "key1": + assertEquals("value1", value); + break; + case "key2": + assertEquals(true, value); + break; + case "key3": + assertEquals(3.0, value); + break; + case "key4": + assertEquals(4, value); + break; + case "key5": + assertNotNull(value); + assertEquals(3, ((LinkedHashMap) value).size()); + assertEquals(42, ((LinkedHashMap) value).get("someNumber")); + assertEquals(Arrays.asList(1, 2, 3), ((LinkedHashMap) value).get("someArray")); + LinkedHashMap someNestedObject = (LinkedHashMap) ((LinkedHashMap) value).get("someNestedObject"); + assertEquals("value", someNestedObject.get("key")); + break; + } + } + } + + private List getActualKeysList(DeviceId deviceId) throws Exception { + long start = System.currentTimeMillis(); + long end = System.currentTimeMillis() + 5000; + + List actualKeys = null; + while (start <= end) { + actualKeys = doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + deviceId + "/keys/attributes/CLIENT_SCOPE", new TypeReference<>() { + }); + if (actualKeys.size() == EXPECTED_KEYS.size()) { + break; + } + Thread.sleep(100); + start += 100; + } + return actualKeys; + } + + private void validateCurrentStateNotification(CoapTestCallback callback) { + assertArrayEquals(EMPTY_PAYLOAD, callback.getPayloadBytes()); + } + + private void validateConfirmableFlag(CoapTestClient client, CoapResponse response) { + CoAP.Type responseType = response.advanced().getType(); + if (CoAP.Type.CON.equals(responseType) || CoAP.Type.NON.equals(responseType)) { + assertEquals(client.getType(), responseType); + } + } + + private void validateTwoWayStateChangedNotification(CoapTestCallback callback, String actualResult) { + assertEquals(DEVICE_RESPONSE, actualResult); + assertNotNull(callback.getPayloadBytes()); + } + + protected class TestCoapCallbackForRPC extends CoapTestCallback { + + private final CoapTestClient client; + + TestCoapCallbackForRPC(CoapTestClient client) { + this.client = client; + } + + @Override + public void onLoad(CoapResponse response) { + payloadBytes = response.getPayload(); + responseCode = response.getCode(); + observe = response.getOptions().getObserve(); + validateConfirmableFlag(client, response); + if (observe != null) { + if (observe > 0) { + processOnLoadResponse(response, client); + } + } + } + + @Override + public void onError() { + log.warn("Command Response Ack Error, No connect"); + } + } + + protected void processOnLoadResponse(CoapResponse response, CoapTestClient client) { + validateConfirmableFlag(client, response); + JsonNode responseJson = JacksonUtil.fromBytes(response.getPayload()); + int requestId = responseJson.get("id").asInt(); + client.setURI(CoapTestClient.getFeatureTokenUrl(accessToken, FeatureType.RPC, requestId)); + client.postMethod(new CoapHandler() { + @Override + public void onLoad(CoapResponse response) { + validateConfirmableFlag(client, response); + log.warn("RPC {} command response ack: {}", requestId, response.getCode()); + } + + @Override + public void onError() { + log.warn("RPC {} command response ack error, no connect", requestId); + } + }, DEVICE_RESPONSE, MediaTypeRegistry.APPLICATION_JSON); + } + + private CoapTestClient createClientForFeatureWithConfirmableParameter(FeatureType featureType, boolean confirmable) { + CoapTestClient coapTestClient = new CoapTestClient(accessToken, featureType); + if (confirmable) { + coapTestClient.useCONs(); + } else { + coapTestClient.useNONs(); + } + return coapTestClient; + } + + private List getEntityKeys(EntityKeyType scope) { + return CoapClientIntegrationTest.EXPECTED_KEYS.stream().map(key -> new EntityKey(scope, key)).collect(Collectors.toList()); + } +} diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/callback/AbstractSyncSessionCallback.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/callback/AbstractSyncSessionCallback.java index 6dc5cccf38..ca46d989e4 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/callback/AbstractSyncSessionCallback.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/callback/AbstractSyncSessionCallback.java @@ -81,6 +81,7 @@ public abstract class AbstractSyncSessionCallback implements SessionMsgListener protected void respond(Response response) { response.getOptions().setContentFormat(TbCoapContentFormatUtil.getContentFormat(exchange.getRequestOptions().getContentFormat(), state.getContentFormat())); + response.setConfirmable(exchange.advanced().getRequest().isConfirmable()); exchange.respond(response); } diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/callback/CoapOkCallback.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/callback/CoapOkCallback.java index db843a0ea3..04ce043331 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/callback/CoapOkCallback.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/callback/CoapOkCallback.java @@ -34,7 +34,9 @@ public class CoapOkCallback implements TransportServiceCallback { @Override public void onSuccess(Void msg) { - exchange.respond(new Response(onSuccessResponse)); + Response response = new Response(onSuccessResponse); + response.setConfirmable(isConRequest()); + exchange.respond(response); } @Override From c0cde53c52a3688eb7619670c13bfab9c179b05c Mon Sep 17 00:00:00 2001 From: imbeacon Date: Thu, 14 Sep 2023 09:40:58 +0300 Subject: [PATCH 130/252] Removed unneccesary check, refactoring and added assertion check for validation in callback --- .../client/CoapClientIntegrationTest.java | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/client/CoapClientIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/client/CoapClientIntegrationTest.java index 3760935d41..e50812403b 100644 --- a/application/src/test/java/org/thingsboard/server/transport/coap/client/CoapClientIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/coap/client/CoapClientIntegrationTest.java @@ -17,6 +17,7 @@ package org.thingsboard.server.transport.coap.client; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.eclipse.californium.core.CoapHandler; import org.eclipse.californium.core.CoapObserveRelation; @@ -101,7 +102,7 @@ public class CoapClientIntegrationTest extends AbstractCoapIntegrationTest { client = createClientForFeatureWithConfirmableParameter(FeatureType.ATTRIBUTES, confirmable); CoapResponse coapResponse = client.postMethod(PAYLOAD_VALUES_STR.getBytes()); assertEquals(CoAP.ResponseCode.CREATED, coapResponse.getCode()); - validateConfirmableFlag(client, coapResponse); + assertEquals("CoAP response type is wrong!", client.getType(), coapResponse.advanced().getType()); DeviceId deviceId = savedDevice.getId(); List actualKeys = getActualKeysList(deviceId); @@ -185,7 +186,7 @@ public class CoapClientIntegrationTest extends AbstractCoapIntegrationTest { String featureTokenUrl = CoapTestClient.getFeatureTokenUrl(accessToken, FeatureType.ATTRIBUTES) + "?clientKeys=" + keysParam + "&sharedKeys=" + keysParam; client.setURI(featureTokenUrl); CoapResponse response = client.getMethod(); - validateConfirmableFlag(client, response); + assertEquals("CoAP response type is wrong!", client.getType(), response.advanced().getType()); } @SuppressWarnings({"unchecked", "rawtypes"}) @@ -240,13 +241,6 @@ public class CoapClientIntegrationTest extends AbstractCoapIntegrationTest { assertArrayEquals(EMPTY_PAYLOAD, callback.getPayloadBytes()); } - private void validateConfirmableFlag(CoapTestClient client, CoapResponse response) { - CoAP.Type responseType = response.advanced().getType(); - if (CoAP.Type.CON.equals(responseType) || CoAP.Type.NON.equals(responseType)) { - assertEquals(client.getType(), responseType); - } - } - private void validateTwoWayStateChangedNotification(CoapTestCallback callback, String actualResult) { assertEquals(DEVICE_RESPONSE, actualResult); assertNotNull(callback.getPayloadBytes()); @@ -256,6 +250,9 @@ public class CoapClientIntegrationTest extends AbstractCoapIntegrationTest { private final CoapTestClient client; + @Getter + private boolean wasSuccessful = false; + TestCoapCallbackForRPC(CoapTestClient client) { this.client = client; } @@ -265,7 +262,7 @@ public class CoapClientIntegrationTest extends AbstractCoapIntegrationTest { payloadBytes = response.getPayload(); responseCode = response.getCode(); observe = response.getOptions().getObserve(); - validateConfirmableFlag(client, response); + wasSuccessful = client.getType().equals(response.advanced().getType()); if (observe != null) { if (observe > 0) { processOnLoadResponse(response, client); @@ -280,14 +277,12 @@ public class CoapClientIntegrationTest extends AbstractCoapIntegrationTest { } protected void processOnLoadResponse(CoapResponse response, CoapTestClient client) { - validateConfirmableFlag(client, response); JsonNode responseJson = JacksonUtil.fromBytes(response.getPayload()); int requestId = responseJson.get("id").asInt(); client.setURI(CoapTestClient.getFeatureTokenUrl(accessToken, FeatureType.RPC, requestId)); client.postMethod(new CoapHandler() { @Override public void onLoad(CoapResponse response) { - validateConfirmableFlag(client, response); log.warn("RPC {} command response ack: {}", requestId, response.getCode()); } From 2b9eab01508dc1e6da810c6a062b0ac7b2f47986 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Wed, 15 Nov 2023 13:03:40 +0200 Subject: [PATCH 131/252] Minor refactoring --- .../org/thingsboard/server/controller/ImageController.java | 4 ++-- .../server/service/install/update/SystemImagesMigrator.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/ImageController.java b/application/src/main/java/org/thingsboard/server/controller/ImageController.java index 756c198a72..159cd1eb0c 100644 --- a/application/src/main/java/org/thingsboard/server/controller/ImageController.java +++ b/application/src/main/java/org/thingsboard/server/controller/ImageController.java @@ -139,7 +139,7 @@ public class ImageController extends BaseController { return downloadIfChanged(etag, imageInfo, descriptor.getPreviewDescriptor(), () -> imageService.getImagePreview(tenantId, imageInfo.getId())); } - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @GetMapping(IMAGE_URL + "/info") public TbResourceInfo getImageInfo(@PathVariable String type, @PathVariable String key) throws ThingsboardException { @@ -147,7 +147,7 @@ public class ImageController extends BaseController { } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @GetMapping("/images") + @GetMapping("/api/images") public PageData getImages(@ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true) @RequestParam int pageSize, @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true) diff --git a/application/src/main/java/org/thingsboard/server/service/install/update/SystemImagesMigrator.java b/application/src/main/java/org/thingsboard/server/service/install/update/SystemImagesMigrator.java index 41e912a173..5fa176a58a 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/update/SystemImagesMigrator.java +++ b/application/src/main/java/org/thingsboard/server/service/install/update/SystemImagesMigrator.java @@ -189,7 +189,7 @@ public class SystemImagesMigrator { // TEMPORARY return data; } String imageMediaType = StringUtils.substringBetween(data, "data:", ";base64"); - String extension = MediaTypeUtils.mediaTypeToFileExtension(imageMediaType); + String extension = ImageUtils.mediaTypeToFileExtension(imageMediaType); imageKey += "." + extension; byte[] image = Base64.getDecoder().decode(base64Data); From 5f10163dce6405e4a32e50dae0d676e406bcded7 Mon Sep 17 00:00:00 2001 From: ShvaykaD Date: Tue, 14 Nov 2023 13:01:19 +0200 Subject: [PATCH 132/252] fixed copy string values from msg to md in TbCopyKeysNode --- .../rule/engine/transform/TbCopyKeysNode.java | 4 +++- .../rule/engine/transform/TbCopyKeysNodeTest.java | 13 ++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbCopyKeysNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbCopyKeysNode.java index 8d9a7102d0..f413be43c1 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbCopyKeysNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbCopyKeysNode.java @@ -89,7 +89,9 @@ public class TbCopyKeysNode implements TbNode { String keyData = entry.getKey(); if (checkKey(keyData)) { msgChanged = true; - metaData.putValue(keyData, JacksonUtil.toString(entry.getValue())); + String value = entry.getValue().isTextual() ? + entry.getValue().asText() : JacksonUtil.toString(entry.getValue()); + metaData.putValue(keyData, value); } } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbCopyKeysNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbCopyKeysNodeTest.java index e0ab34e35b..0fb9589e90 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbCopyKeysNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbCopyKeysNodeTest.java @@ -96,10 +96,12 @@ public class TbCopyKeysNodeTest { @Test void givenMsgFromMsg_whenOnMsg_thenVerifyOutput() throws Exception { config.setFromMetadata(false); + config.setKeys(Set.of(".*Key$")); nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); node.init(ctx, nodeConfiguration); - String data = "{\"DigitData\":22.5,\"TempDataValue\":10.5}"; + String data = "{\"nullKey\":null,\"stringKey\":\"value1\",\"booleanKey\":true,\"doubleKey\":42.0,\"longKey\":73," + + "\"jsonKey\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}}"; node.onMsg(ctx, getTbMsg(deviceId, data)); ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); @@ -110,8 +112,13 @@ public class TbCopyKeysNodeTest { assertThat(newMsg).isNotNull(); Map metaDataMap = newMsg.getMetaData().getData(); - assertThat(metaDataMap.containsKey("DigitData")).isEqualTo(true); - assertThat(metaDataMap.containsKey("TempDataValue")).isEqualTo(true); + assertThat(metaDataMap.get("nullKey")).isEqualTo("null"); + assertThat(metaDataMap.get("stringKey")).isEqualTo("value1"); + assertThat(metaDataMap.get("booleanKey")).isEqualTo("true"); + assertThat(metaDataMap.get("doubleKey")).isEqualTo("42.0"); + assertThat(metaDataMap.get("longKey")).isEqualTo("73"); + assertThat(metaDataMap.get("jsonKey")) + .isEqualTo("{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}"); } @Test From 08727b4c368b676c0b62e2dd136ef83fec14381f Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Wed, 15 Nov 2023 14:51:58 +0200 Subject: [PATCH 133/252] added executor provider for pubsub queue, added env variable to manage pubsub queue executor provider thread pool --- .../server/actors/ActorSystemContext.java | 4 +- .../actors/ruleChain/DefaultTbContext.java | 6 +-- .../executors/PubSubExecutorService.java | 26 ---------- .../PubSubRuleNodeExecutorProvider.java | 29 +++++++++++ .../src/main/resources/thingsboard.yml | 8 +-- .../provider/PubSubMonolithQueueFactory.java | 50 +++++++++++-------- .../provider/PubSubTbCoreQueueFactory.java | 38 ++++++++------ .../PubSubTbRuleEngineQueueFactory.java | 28 ++++++----- .../PubSubTbVersionControlQueueFactory.java | 13 +++-- .../provider/PubSubTransportQueueFactory.java | 20 +++++--- .../pubsub/TbPubSubConsumerTemplate.java | 4 +- .../pubsub/TbPubSubProducerTemplate.java | 7 ++- .../pubsub/TbPubSubQueueExecutorProvider.java | 37 ++++++++++++++ common/util/pom.xml | 4 -- .../common/util/ExecutorProvider.java | 8 +++ .../common/util/PubSubExecutor.java | 7 --- .../src/main/resources/tb-vc-executor.yml | 2 + .../rule/engine/api/TbContext.java | 4 +- .../rule/engine/gcp/pubsub/TbPubSubNode.java | 2 +- .../src/main/resources/tb-coap-transport.yml | 2 + .../src/main/resources/tb-http-transport.yml | 2 + .../src/main/resources/tb-lwm2m-transport.yml | 2 + .../src/main/resources/tb-mqtt-transport.yml | 2 + .../src/main/resources/tb-snmp-transport.yml | 2 + 24 files changed, 194 insertions(+), 113 deletions(-) delete mode 100644 application/src/main/java/org/thingsboard/server/service/executors/PubSubExecutorService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/executors/PubSubRuleNodeExecutorProvider.java create mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubQueueExecutorProvider.java create mode 100644 common/util/src/main/java/org/thingsboard/common/util/ExecutorProvider.java delete mode 100644 common/util/src/main/java/org/thingsboard/common/util/PubSubExecutor.java diff --git a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java index b9f539c877..24a240661f 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java @@ -29,7 +29,7 @@ import org.springframework.data.redis.core.RedisTemplate; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.service.executors.PubSubExecutorService; +import org.thingsboard.server.service.executors.PubSubRuleNodeExecutorProvider; import org.thingsboard.rule.engine.api.MailService; import org.thingsboard.rule.engine.api.NotificationCenter; import org.thingsboard.rule.engine.api.SmsService; @@ -325,7 +325,7 @@ public class ActorSystemContext { @Autowired @Getter - private PubSubExecutorService pubSubExecutorService; + private PubSubRuleNodeExecutorProvider pubSubRuleNodeExecutorProvider; @Autowired @Getter diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java index 299b6274b0..28963b383a 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java @@ -22,7 +22,6 @@ import lombok.extern.slf4j.Slf4j; import org.bouncycastle.util.Arrays; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ListeningExecutor; -import org.thingsboard.server.service.executors.PubSubExecutorService; import org.thingsboard.rule.engine.api.MailService; import org.thingsboard.rule.engine.api.NotificationCenter; import org.thingsboard.rule.engine.api.RuleEngineAlarmService; @@ -105,6 +104,7 @@ import org.thingsboard.server.dao.widget.WidgetsBundleService; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.TbQueueCallback; import org.thingsboard.server.queue.TbQueueMsgMetadata; +import org.thingsboard.server.service.executors.PubSubRuleNodeExecutorProvider; import org.thingsboard.server.service.script.RuleNodeJsScriptEngine; import org.thingsboard.server.service.script.RuleNodeTbelScriptEngine; @@ -540,8 +540,8 @@ class DefaultTbContext implements TbContext { } @Override - public PubSubExecutorService getPubsubExecutor() { - return mainCtx.getPubSubExecutorService(); + public PubSubRuleNodeExecutorProvider getPubSubRuleNodeExecutorProvider() { + return mainCtx.getPubSubRuleNodeExecutorProvider(); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/executors/PubSubExecutorService.java b/application/src/main/java/org/thingsboard/server/service/executors/PubSubExecutorService.java deleted file mode 100644 index 22d55cc673..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/executors/PubSubExecutorService.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.thingsboard.server.service.executors; - -import com.google.api.gax.core.FixedExecutorProvider; -import org.springframework.stereotype.Component; -import org.thingsboard.common.util.PubSubExecutor; -import org.thingsboard.common.util.ThingsBoardThreadFactory; - -import javax.annotation.PostConstruct; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; - -@Component -public class PubSubExecutorService implements PubSubExecutor { - private static final int THREADS_PER_CPU = 5; - private FixedExecutorProvider fixedExecutorProvider; - - @PostConstruct - public void init() { - ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(THREADS_PER_CPU * Runtime.getRuntime().availableProcessors(), ThingsBoardThreadFactory.forName("tb-pubsub-producer-scheduler"));; - this.fixedExecutorProvider = FixedExecutorProvider.create(scheduler); - } - - public FixedExecutorProvider getExecutorProvider() { - return fixedExecutorProvider; - } -} diff --git a/application/src/main/java/org/thingsboard/server/service/executors/PubSubRuleNodeExecutorProvider.java b/application/src/main/java/org/thingsboard/server/service/executors/PubSubRuleNodeExecutorProvider.java new file mode 100644 index 0000000000..7db74f6195 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/executors/PubSubRuleNodeExecutorProvider.java @@ -0,0 +1,29 @@ +package org.thingsboard.server.service.executors; + +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.ExecutorProvider; +import org.thingsboard.common.util.ThingsBoardThreadFactory; + +import javax.annotation.PostConstruct; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; + +@Component +public class PubSubRuleNodeExecutorProvider implements ExecutorProvider { + + /** + * Refers to com.google.cloud.pubsub.v1.Publisher default executor configuration + */ + private static final int THREADS_PER_CPU = 5; + private ScheduledExecutorService executor; + + @PostConstruct + public void init() { + executor = Executors.newScheduledThreadPool(THREADS_PER_CPU * Runtime.getRuntime().availableProcessors(), ThingsBoardThreadFactory.forName("pubsub-rule-nodes"));; + } + + @Override + public ScheduledExecutorService getExecutor() { + return executor; + } +} diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index bdf0ad8252..114dafa6f0 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -1270,7 +1270,7 @@ swagger: # Queue configuration parameters queue: - type: "${TB_QUEUE_TYPE:in-memory}" # in-memory or kafka (Apache Kafka) or aws-sqs (AWS SQS) or pubsub (PubSub) or service-bus (Azure Service Bus) or rabbitmq (RabbitMQ) + type: "${TB_QUEUE_TYPE:pubsub}" # in-memory or kafka (Apache Kafka) or aws-sqs (AWS SQS) or pubsub (PubSub) or service-bus (Azure Service Bus) or rabbitmq (RabbitMQ) prefix: "${TB_QUEUE_PREFIX:}" # Global queue prefix. If specified, prefix is added before default topic name: 'prefix.default_topic_name'. Prefix is applied to all topics (and consumer groups for kafka) except of js executor topics (please use REMOTE_JS_EVAL_REQUEST_TOPIC and REMOTE_JS_EVAL_RESPONSE_TOPIC to specify custom topic names) in_memory: stats: @@ -1404,13 +1404,15 @@ queue: version-control: "${TB_QUEUE_AWS_SQS_VC_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" pubsub: # Project ID from Google Cloud - project_id: "${TB_QUEUE_PUBSUB_PROJECT_ID:YOUR_PROJECT_ID}" + project_id: "${TB_QUEUE_PUBSUB_PROJECT_ID:pubsub-389210}" # API Credentials in JSON format - service_account: "${TB_QUEUE_PUBSUB_SERVICE_ACCOUNT:YOUR_SERVICE_ACCOUNT}" + service_account: "${TB_QUEUE_PUBSUB_SERVICE_ACCOUNT:{\n \"type\": \"service_account\",\n \"project_id\": \"pubsub-389210\",\n \"private_key_id\": \"e83e233f60bb75f52e6ee9d34046c1e7b5e3f2e6\",\n \"private_key\": \"-----BEGIN PRIVATE KEY-----\\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCmram5Oa60kyFM\\nFwo7d4yYA2U4vLUa2HV7h5G5ZxhXTPk+V4eYneBPSX0bJ/V5L4z7N/xNNwDR80i3\\n+tCFIf9P+jS3sE0ThZDc8Rdgo1Ln8nm4qf/9uBrOdSjn5VKKwvnNXphwTniwzssS\\nWn3AMuL8flgKoOiRaBOMXykIWN4/epKmv1tmnnq57XLW2cRTsE+pCcbqn8W2LEgA\\nNP+NJvvhQ6wsU341F2oTMAsuwsmiOZ1aiKKCafjGb/STPYy/WXsLjrfwK08U2SJh\\n+hHyvzdH1AU40khelnLjQhrltUGk1VAaKtucDKGSAoL5JIkH0Q5qczz1Ys6l3VvK\\nlhDNP8NHAgMBAAECggEAFUVJSRGC6ZlzIN452I/vmcCpLL3sUMI57+tDngj5kr6I\\neju6WrAfHY8vz4TgbzgxwieJY6M20BQ/ffccoDjP97li++QcWfbsHz4HMTZ2kJIh\\nlX7gY9UYWquZ7koWKA4sydgeFQr2nP2u7actsBbuX7GR871IILJK+Fl2h5Grvybk\\nz70RzLY+M26ZVBf+ejKlyaoMVLQ6w272llc5/SJ4jmsbn1urLM2+ChgAjOeqo7Qp\\nBBjUJmjC9b5Tev0jg+FXdw+ba41bXmYM5x265buwbY4vYyIJ+XF6dEl36cdVa1xI\\n+XISQZ66SuvEkHRc+x1hEEMymOtt8d++SeFn4/BkDQKBgQDkGtYA/8751C8xDB4+\\nlpxXf3wN/QjWPpXAV1fyj9sgwz05M1E0KzYpX6H2rWVqFhPkuqZUCeZ5Iqjvybp7\\n8upLJ+lRpHag1dkS4ElICNNcHhIQB9xgmFtdH4euTzuAFTd5nZ43oRt6gGEo0I/S\\nmKmupS2IwpM2/Qm6dP7l2ruS5QKBgQC7D8eTIIu8x2plJ6rebEAneX6aAUzQ9chk\\nRB53wjDx0UCXC/J2l1PX0vGGY8JOQdmDS6MPdFaVZxnqtrrGNBDuSArDNH7bV0ab\\nBKnLStPOwkehQEhg6ShHX7r4EPs6GBTWDQnnCgR8KGq+7rdo/lz7C/TK8+xqRaWq\\nYCUMY4g+uwKBgHtPFokLwHPFhI1bI65p9LJINGPLec93nbSQgvaZVbfsU1hsqWUu\\nRuUu0XtsWPp0XOS1Ed00TOcHGZQm1SzUMFvYg4SjB44CjIprvLG4M6oEh3crCLMf\\nKaS5urs8Eco3rfmMf09LRHOAmwVZWaZa6L+Eg4z+wl5jg7LMNE9FY0kRAoGACU+J\\nwXr3OZg7ZXmJ+bQtpUlY0dWKu0Pgi40QbymNQhwGOP5xPRHfLHjlaKkCfN6uMujK\\n3vQNczZEhfg+Z6sjxJh1YK32OninnQOoZ+P7kuj8o7wNXjV8ucC8D6jYuFWGg/j2\\nKzfGbV+doI9FNcajXiOENa3acJey3T4X2fwCRg0CgYEA0m3iO8HvnWhAs+clP2Ph\\nI8mXNbNbtCa7bOPmhdidSwJpbXNNwKNU7xAXHHueFGlEnNFsQrqy7bPfol4ABOhl\\nV3Zdsuc5v1OXM7IrLZ16cC7fogydSQz8QK86TcC50u/aSmkaevujsJfabC/YF7sR\\nBl8tPN7I4H72JKOuUTn/Sfo=\\n-----END PRIVATE KEY-----\\n\",\n \"client_email\": \"pubsubaccountname@pubsub-389210.iam.gserviceaccount.com\",\n \"client_id\": \"103395736410565244836\",\n \"auth_uri\": \"https://accounts.google.com/o/oauth2/auth\",\n \"token_uri\": \"https://oauth2.googleapis.com/token\",\n \"auth_provider_x509_cert_url\": \"https://www.googleapis.com/oauth2/v1/certs\",\n \"client_x509_cert_url\": \"https://www.googleapis.com/robot/v1/metadata/x509/pubsubaccountname%40pubsub-389210.iam.gserviceaccount.com\",\n \"universe_domain\": \"googleapis.com\"\n}}" # Message size for PubSub queue.Value in bytes max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" # Number of messages per consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" + # Number of threads of pubsub executor provider + executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_SIZE:}" queue-properties: # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consumed again rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubMonolithQueueFactory.java index 554d452556..8ecd6564c8 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubMonolithQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubMonolithQueueFactory.java @@ -44,6 +44,7 @@ import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.pubsub.TbPubSubAdmin; import org.thingsboard.server.queue.pubsub.TbPubSubConsumerTemplate; +import org.thingsboard.server.queue.pubsub.TbPubSubQueueExecutorProvider; import org.thingsboard.server.queue.pubsub.TbPubSubProducerTemplate; import org.thingsboard.server.queue.pubsub.TbPubSubSettings; import org.thingsboard.server.queue.pubsub.TbPubSubSubscriptionSettings; @@ -77,6 +78,7 @@ public class PubSubMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng private final TbQueueAdmin transportApiAdmin; private final TbQueueAdmin notificationAdmin; private final TbQueueAdmin vcAdmin; + private final TbPubSubQueueExecutorProvider tbPubSubQueueExecutorProvider; public PubSubMonolithQueueFactory(TbPubSubSettings pubSubSettings, TbQueueCoreSettings coreSettings, @@ -87,7 +89,8 @@ public class PubSubMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng TbServiceInfoProvider serviceInfoProvider, TbPubSubSubscriptionSettings pubSubSubscriptionSettings, TbQueueRemoteJsInvokeSettings jsInvokeSettings, - TbQueueVersionControlSettings vcSettings) { + TbQueueVersionControlSettings vcSettings, + TbPubSubQueueExecutorProvider tbPubSubQueueExecutorProvider) { this.pubSubSettings = pubSubSettings; this.coreSettings = coreSettings; this.ruleEngineSettings = ruleEngineSettings; @@ -96,6 +99,7 @@ public class PubSubMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng this.topicService = topicService; this.serviceInfoProvider = serviceInfoProvider; this.vcSettings = vcSettings; + this.tbPubSubQueueExecutorProvider = tbPubSubQueueExecutorProvider; this.coreAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getCoreSettings()); this.ruleEngineAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getRuleEngineSettings()); @@ -109,85 +113,91 @@ public class PubSubMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng @Override public TbQueueProducer> createTransportNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, transportNotificationSettings.getNotificationsTopic()); + return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, transportNotificationSettings.getNotificationsTopic(), tbPubSubQueueExecutorProvider); } @Override public TbQueueProducer> createRuleEngineMsgProducer() { - return new TbPubSubProducerTemplate<>(ruleEngineAdmin, pubSubSettings, ruleEngineSettings.getTopic()); + return new TbPubSubProducerTemplate<>(ruleEngineAdmin, pubSubSettings, ruleEngineSettings.getTopic(), tbPubSubQueueExecutorProvider); } @Override public TbQueueProducer> createRuleEngineNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, ruleEngineSettings.getTopic()); + return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, ruleEngineSettings.getTopic(), tbPubSubQueueExecutorProvider); } @Override public TbQueueProducer> createTbCoreMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getTopic()); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getTopic(), tbPubSubQueueExecutorProvider); } @Override public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, coreSettings.getTopic()); + return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, coreSettings.getTopic(), tbPubSubQueueExecutorProvider); } @Override public TbQueueConsumer> createToVersionControlMsgConsumer() { return new TbPubSubConsumerTemplate<>(vcAdmin, pubSubSettings, vcSettings.getTopic(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportProtos.ToVersionControlServiceMsg.parseFrom(msg.getData()), msg.getHeaders()) + msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportProtos.ToVersionControlServiceMsg.parseFrom(msg.getData()), msg.getHeaders()), + tbPubSubQueueExecutorProvider ); } @Override public TbQueueConsumer> createToRuleEngineMsgConsumer(Queue configuration) { return new TbPubSubConsumerTemplate<>(ruleEngineAdmin, pubSubSettings, configuration.getTopic(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineMsg.parseFrom(msg.getData()), msg.getHeaders())); + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineMsg.parseFrom(msg.getData()), msg.getHeaders()), + tbPubSubQueueExecutorProvider); } @Override public TbQueueConsumer> createToRuleEngineNotificationsMsgConsumer() { return new TbPubSubConsumerTemplate<>(notificationAdmin, pubSubSettings, topicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceInfoProvider.getServiceId()).getFullTopicName(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineNotificationMsg.parseFrom(msg.getData()), msg.getHeaders()), + tbPubSubQueueExecutorProvider); } @Override public TbQueueConsumer> createToCoreMsgConsumer() { return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getTopic(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreMsg.parseFrom(msg.getData()), msg.getHeaders())); + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreMsg.parseFrom(msg.getData()), msg.getHeaders()), + tbPubSubQueueExecutorProvider); } @Override public TbQueueConsumer> createToCoreNotificationsMsgConsumer() { return new TbPubSubConsumerTemplate<>(notificationAdmin, pubSubSettings, topicService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreNotificationMsg.parseFrom(msg.getData()), msg.getHeaders()), + tbPubSubQueueExecutorProvider); } @Override public TbQueueConsumer> createTransportApiRequestConsumer() { return new TbPubSubConsumerTemplate<>(transportApiAdmin, pubSubSettings, transportApiSettings.getRequestsTopic(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiRequestMsg.parseFrom(msg.getData()), msg.getHeaders())); + msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiRequestMsg.parseFrom(msg.getData()), msg.getHeaders()), + tbPubSubQueueExecutorProvider); } @Override public TbQueueProducer> createTransportApiResponseProducer() { - return new TbPubSubProducerTemplate<>(transportApiAdmin, pubSubSettings, transportApiSettings.getResponsesTopic()); + return new TbPubSubProducerTemplate<>(transportApiAdmin, pubSubSettings, transportApiSettings.getResponsesTopic(), tbPubSubQueueExecutorProvider); } @Override @Bean public TbQueueRequestTemplate, TbProtoQueueMsg> createRemoteJsRequestTemplate() { - TbQueueProducer> producer = new TbPubSubProducerTemplate<>(jsExecutorAdmin, pubSubSettings, jsInvokeSettings.getRequestTopic()); + TbQueueProducer> producer = new TbPubSubProducerTemplate<>(jsExecutorAdmin, pubSubSettings, jsInvokeSettings.getRequestTopic(), tbPubSubQueueExecutorProvider); TbQueueConsumer> consumer = new TbPubSubConsumerTemplate<>(jsExecutorAdmin, pubSubSettings, jsInvokeSettings.getResponseTopic() + "." + serviceInfoProvider.getServiceId(), msg -> { RemoteJsResponse.Builder builder = RemoteJsResponse.newBuilder(); JsonFormat.parser().ignoringUnknownFields().merge(new String(msg.getData(), StandardCharsets.UTF_8), builder); return new TbProtoQueueMsg<>(msg.getKey(), builder.build(), msg.getHeaders()); - }); + }, tbPubSubQueueExecutorProvider); DefaultTbQueueRequestTemplate.DefaultTbQueueRequestTemplateBuilder , TbProtoQueueMsg> builder = DefaultTbQueueRequestTemplate.builder(); @@ -203,28 +213,28 @@ public class PubSubMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng @Override public TbQueueConsumer> createToUsageStatsServiceMsgConsumer() { return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getUsageStatsTopic(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToUsageStatsServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToUsageStatsServiceMsg.parseFrom(msg.getData()), msg.getHeaders()), tbPubSubQueueExecutorProvider); } @Override public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getOtaPackageTopic(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders()), tbPubSubQueueExecutorProvider); } @Override public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getOtaPackageTopic()); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getOtaPackageTopic(), tbPubSubQueueExecutorProvider); } @Override public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getUsageStatsTopic()); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getUsageStatsTopic(), tbPubSubQueueExecutorProvider); } @Override public TbQueueProducer> createVersionControlMsgProducer() { - return new TbPubSubProducerTemplate<>(vcAdmin, pubSubSettings, vcSettings.getTopic()); + return new TbPubSubProducerTemplate<>(vcAdmin, pubSubSettings, vcSettings.getTopic(), tbPubSubQueueExecutorProvider); } @PreDestroy diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbCoreQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbCoreQueueFactory.java index 3b45992e25..5443339d2a 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbCoreQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbCoreQueueFactory.java @@ -42,6 +42,7 @@ import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.pubsub.TbPubSubAdmin; import org.thingsboard.server.queue.pubsub.TbPubSubConsumerTemplate; +import org.thingsboard.server.queue.pubsub.TbPubSubQueueExecutorProvider; import org.thingsboard.server.queue.pubsub.TbPubSubProducerTemplate; import org.thingsboard.server.queue.pubsub.TbPubSubSettings; import org.thingsboard.server.queue.pubsub.TbPubSubSubscriptionSettings; @@ -72,6 +73,7 @@ public class PubSubTbCoreQueueFactory implements TbCoreQueueFactory { private final TbQueueAdmin transportApiAdmin; private final TbQueueAdmin notificationAdmin; private final TbQueueAdmin ruleEngineAdmin; + private final TbPubSubQueueExecutorProvider tbPubSubQueueExecutorProvider; public PubSubTbCoreQueueFactory(TbPubSubSettings pubSubSettings, TbQueueCoreSettings coreSettings, @@ -81,7 +83,8 @@ public class PubSubTbCoreQueueFactory implements TbCoreQueueFactory { TbQueueRemoteJsInvokeSettings jsInvokeSettings, TbQueueTransportNotificationSettings transportNotificationSettings, TbQueueRuleEngineSettings ruleEngineSettings, - TbPubSubSubscriptionSettings pubSubSubscriptionSettings) { + TbPubSubSubscriptionSettings pubSubSubscriptionSettings, + TbPubSubQueueExecutorProvider tbPubSubQueueExecutorProvider) { this.pubSubSettings = pubSubSettings; this.coreSettings = coreSettings; this.transportApiSettings = transportApiSettings; @@ -90,6 +93,7 @@ public class PubSubTbCoreQueueFactory implements TbCoreQueueFactory { this.jsInvokeSettings = jsInvokeSettings; this.transportNotificationSettings = transportNotificationSettings; this.ruleEngineSettings = ruleEngineSettings; + this.tbPubSubQueueExecutorProvider = tbPubSubQueueExecutorProvider; this.coreAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getCoreSettings()); this.jsExecutorAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getJsExecutorSettings()); @@ -100,64 +104,64 @@ public class PubSubTbCoreQueueFactory implements TbCoreQueueFactory { @Override public TbQueueProducer> createTransportNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, transportNotificationSettings.getNotificationsTopic()); + return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, transportNotificationSettings.getNotificationsTopic(), tbPubSubQueueExecutorProvider); } @Override public TbQueueProducer> createRuleEngineMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getTopic()); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getTopic(), tbPubSubQueueExecutorProvider); } @Override public TbQueueProducer> createRuleEngineNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, ruleEngineSettings.getTopic()); + return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, ruleEngineSettings.getTopic(), tbPubSubQueueExecutorProvider); } @Override public TbQueueProducer> createTbCoreMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getTopic()); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getTopic(), tbPubSubQueueExecutorProvider); } @Override public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, coreSettings.getTopic()); + return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, coreSettings.getTopic(), tbPubSubQueueExecutorProvider); } @Override public TbQueueConsumer> createToCoreMsgConsumer() { return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getTopic(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreMsg.parseFrom(msg.getData()), msg.getHeaders())); + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreMsg.parseFrom(msg.getData()), msg.getHeaders()), tbPubSubQueueExecutorProvider); } @Override public TbQueueConsumer> createToCoreNotificationsMsgConsumer() { return new TbPubSubConsumerTemplate<>(notificationAdmin, pubSubSettings, topicService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreNotificationMsg.parseFrom(msg.getData()), msg.getHeaders()), tbPubSubQueueExecutorProvider); } @Override public TbQueueConsumer> createTransportApiRequestConsumer() { return new TbPubSubConsumerTemplate<>(transportApiAdmin, pubSubSettings, transportApiSettings.getRequestsTopic(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiRequestMsg.parseFrom(msg.getData()), msg.getHeaders())); + msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiRequestMsg.parseFrom(msg.getData()), msg.getHeaders()), tbPubSubQueueExecutorProvider); } @Override public TbQueueProducer> createTransportApiResponseProducer() { - return new TbPubSubProducerTemplate<>(transportApiAdmin, pubSubSettings, transportApiSettings.getResponsesTopic()); + return new TbPubSubProducerTemplate<>(transportApiAdmin, pubSubSettings, transportApiSettings.getResponsesTopic(), tbPubSubQueueExecutorProvider); } @Override @Bean public TbQueueRequestTemplate, TbProtoQueueMsg> createRemoteJsRequestTemplate() { - TbQueueProducer> producer = new TbPubSubProducerTemplate<>(jsExecutorAdmin, pubSubSettings, jsInvokeSettings.getRequestTopic()); + TbQueueProducer> producer = new TbPubSubProducerTemplate<>(jsExecutorAdmin, pubSubSettings, jsInvokeSettings.getRequestTopic(), tbPubSubQueueExecutorProvider); TbQueueConsumer> consumer = new TbPubSubConsumerTemplate<>(jsExecutorAdmin, pubSubSettings, jsInvokeSettings.getResponseTopic() + "." + serviceInfoProvider.getServiceId(), msg -> { JsInvokeProtos.RemoteJsResponse.Builder builder = JsInvokeProtos.RemoteJsResponse.newBuilder(); JsonFormat.parser().ignoringUnknownFields().merge(new String(msg.getData(), StandardCharsets.UTF_8), builder); return new TbProtoQueueMsg<>(msg.getKey(), builder.build(), msg.getHeaders()); - }); + }, tbPubSubQueueExecutorProvider); DefaultTbQueueRequestTemplate.DefaultTbQueueRequestTemplateBuilder , TbProtoQueueMsg> builder = DefaultTbQueueRequestTemplate.builder(); @@ -173,23 +177,25 @@ public class PubSubTbCoreQueueFactory implements TbCoreQueueFactory { @Override public TbQueueConsumer> createToUsageStatsServiceMsgConsumer() { return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getUsageStatsTopic(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToUsageStatsServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToUsageStatsServiceMsg.parseFrom(msg.getData()), msg.getHeaders()), + tbPubSubQueueExecutorProvider); } @Override public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getOtaPackageTopic(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders()), + tbPubSubQueueExecutorProvider); } @Override public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getOtaPackageTopic()); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getOtaPackageTopic(), tbPubSubQueueExecutorProvider); } @Override public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getUsageStatsTopic()); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getUsageStatsTopic(), tbPubSubQueueExecutorProvider); } @Override diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbRuleEngineQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbRuleEngineQueueFactory.java index a60f77852f..b0fde82e7d 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbRuleEngineQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbRuleEngineQueueFactory.java @@ -40,6 +40,7 @@ import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.pubsub.TbPubSubAdmin; import org.thingsboard.server.queue.pubsub.TbPubSubConsumerTemplate; +import org.thingsboard.server.queue.pubsub.TbPubSubQueueExecutorProvider; import org.thingsboard.server.queue.pubsub.TbPubSubProducerTemplate; import org.thingsboard.server.queue.pubsub.TbPubSubSettings; import org.thingsboard.server.queue.pubsub.TbPubSubSubscriptionSettings; @@ -62,6 +63,7 @@ public class PubSubTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory private final TbServiceInfoProvider serviceInfoProvider; private final TbQueueRemoteJsInvokeSettings jsInvokeSettings; private final TbQueueTransportNotificationSettings transportNotificationSettings; + private final TbPubSubQueueExecutorProvider tbPubSubQueueExecutorProvider; private final TbQueueAdmin coreAdmin; private final TbQueueAdmin ruleEngineAdmin; @@ -75,7 +77,8 @@ public class PubSubTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory TbServiceInfoProvider serviceInfoProvider, TbQueueRemoteJsInvokeSettings jsInvokeSettings, TbQueueTransportNotificationSettings transportNotificationSettings, - TbPubSubSubscriptionSettings pubSubSubscriptionSettings) { + TbPubSubSubscriptionSettings pubSubSubscriptionSettings, + TbPubSubQueueExecutorProvider tbPubSubQueueExecutorProvider) { this.pubSubSettings = pubSubSettings; this.coreSettings = coreSettings; this.ruleEngineSettings = ruleEngineSettings; @@ -83,6 +86,7 @@ public class PubSubTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory this.serviceInfoProvider = serviceInfoProvider; this.jsInvokeSettings = jsInvokeSettings; this.transportNotificationSettings = transportNotificationSettings; + this.tbPubSubQueueExecutorProvider = tbPubSubQueueExecutorProvider; this.coreAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getCoreSettings()); this.ruleEngineAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getRuleEngineSettings()); @@ -92,53 +96,53 @@ public class PubSubTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory @Override public TbQueueProducer> createTransportNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic())); + return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic()), tbPubSubQueueExecutorProvider); } @Override public TbQueueProducer> createRuleEngineMsgProducer() { - return new TbPubSubProducerTemplate<>(ruleEngineAdmin, pubSubSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); + return new TbPubSubProducerTemplate<>(ruleEngineAdmin, pubSubSettings, topicService.buildTopicName(ruleEngineSettings.getTopic()), tbPubSubQueueExecutorProvider); } @Override public TbQueueProducer> createRuleEngineNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); + return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(ruleEngineSettings.getTopic()), tbPubSubQueueExecutorProvider); } @Override public TbQueueProducer> createTbCoreMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic())); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic()), tbPubSubQueueExecutorProvider); } @Override public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic())); + return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic()), tbPubSubQueueExecutorProvider); } @Override public TbQueueConsumer> createToRuleEngineMsgConsumer(Queue configuration) { return new TbPubSubConsumerTemplate<>(ruleEngineAdmin, pubSubSettings, topicService.buildTopicName(configuration.getTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineMsg.parseFrom(msg.getData()), msg.getHeaders())); + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineMsg.parseFrom(msg.getData()), msg.getHeaders()), tbPubSubQueueExecutorProvider); } @Override public TbQueueConsumer> createToRuleEngineNotificationsMsgConsumer() { return new TbPubSubConsumerTemplate<>(notificationAdmin, pubSubSettings, topicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceInfoProvider.getServiceId()).getFullTopicName(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineNotificationMsg.parseFrom(msg.getData()), msg.getHeaders()), tbPubSubQueueExecutorProvider); } @Override @Bean public TbQueueRequestTemplate, TbProtoQueueMsg> createRemoteJsRequestTemplate() { - TbQueueProducer> producer = new TbPubSubProducerTemplate<>(jsExecutorAdmin, pubSubSettings, jsInvokeSettings.getRequestTopic()); + TbQueueProducer> producer = new TbPubSubProducerTemplate<>(jsExecutorAdmin, pubSubSettings, jsInvokeSettings.getRequestTopic(), tbPubSubQueueExecutorProvider); TbQueueConsumer> consumer = new TbPubSubConsumerTemplate<>(jsExecutorAdmin, pubSubSettings, jsInvokeSettings.getResponseTopic() + "." + serviceInfoProvider.getServiceId(), msg -> { JsInvokeProtos.RemoteJsResponse.Builder builder = JsInvokeProtos.RemoteJsResponse.newBuilder(); JsonFormat.parser().ignoringUnknownFields().merge(new String(msg.getData(), StandardCharsets.UTF_8), builder); return new TbProtoQueueMsg<>(msg.getKey(), builder.build(), msg.getHeaders()); - }); + }, tbPubSubQueueExecutorProvider); DefaultTbQueueRequestTemplate.DefaultTbQueueRequestTemplateBuilder , TbProtoQueueMsg> builder = DefaultTbQueueRequestTemplate.builder(); @@ -153,12 +157,12 @@ public class PubSubTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory @Override public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic()), tbPubSubQueueExecutorProvider); } @Override public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic())); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic()), tbPubSubQueueExecutorProvider); } @PreDestroy diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbVersionControlQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbVersionControlQueueFactory.java index 8154d04d6d..29207ed429 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbVersionControlQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbVersionControlQueueFactory.java @@ -26,6 +26,7 @@ import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.pubsub.TbPubSubAdmin; import org.thingsboard.server.queue.pubsub.TbPubSubConsumerTemplate; import org.thingsboard.server.queue.pubsub.TbPubSubProducerTemplate; +import org.thingsboard.server.queue.pubsub.TbPubSubQueueExecutorProvider; import org.thingsboard.server.queue.pubsub.TbPubSubSettings; import org.thingsboard.server.queue.pubsub.TbPubSubSubscriptionSettings; import org.thingsboard.server.queue.settings.TbQueueCoreSettings; @@ -41,6 +42,7 @@ public class PubSubTbVersionControlQueueFactory implements TbVersionControlQueue private final TbQueueCoreSettings coreSettings; private final TbQueueVersionControlSettings vcSettings; private final TopicService topicService; + private final TbPubSubQueueExecutorProvider tbPubSubQueueExecutorProvider; private final TbQueueAdmin coreAdmin; private final TbQueueAdmin notificationAdmin; @@ -50,12 +52,14 @@ public class PubSubTbVersionControlQueueFactory implements TbVersionControlQueue TbQueueCoreSettings coreSettings, TbQueueVersionControlSettings vcSettings, TbPubSubSubscriptionSettings pubSubSubscriptionSettings, - TopicService topicService + TopicService topicService, + TbPubSubQueueExecutorProvider tbPubSubQueueExecutorProvider ) { this.pubSubSettings = pubSubSettings; this.coreSettings = coreSettings; this.vcSettings = vcSettings; this.topicService = topicService; + this.tbPubSubQueueExecutorProvider = tbPubSubQueueExecutorProvider; this.coreAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getCoreSettings()); this.notificationAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getNotificationsSettings()); @@ -64,18 +68,19 @@ public class PubSubTbVersionControlQueueFactory implements TbVersionControlQueue @Override public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic()), tbPubSubQueueExecutorProvider); } @Override public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic())); + return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic()), tbPubSubQueueExecutorProvider); } @Override public TbQueueConsumer> createToVersionControlMsgConsumer() { return new TbPubSubConsumerTemplate<>(vcAdmin, pubSubSettings, topicService.buildTopicName(vcSettings.getTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportProtos.ToVersionControlServiceMsg.parseFrom(msg.getData()), msg.getHeaders()) + msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportProtos.ToVersionControlServiceMsg.parseFrom(msg.getData()), msg.getHeaders()), + tbPubSubQueueExecutorProvider ); } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTransportQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTransportQueueFactory.java index edcad873c4..94a454dd5b 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTransportQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTransportQueueFactory.java @@ -37,6 +37,7 @@ import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.pubsub.TbPubSubAdmin; import org.thingsboard.server.queue.pubsub.TbPubSubConsumerTemplate; import org.thingsboard.server.queue.pubsub.TbPubSubProducerTemplate; +import org.thingsboard.server.queue.pubsub.TbPubSubQueueExecutorProvider; import org.thingsboard.server.queue.pubsub.TbPubSubSettings; import org.thingsboard.server.queue.pubsub.TbPubSubSubscriptionSettings; import org.thingsboard.server.queue.settings.TbQueueCoreSettings; @@ -58,6 +59,7 @@ public class PubSubTransportQueueFactory implements TbTransportQueueFactory { private final TbQueueTransportApiSettings transportApiSettings; private final TbQueueTransportNotificationSettings transportNotificationSettings; private final TopicService topicService; + private final TbPubSubQueueExecutorProvider tbPubSubQueueExecutorProvider; private final TbQueueAdmin coreAdmin; private final TbQueueAdmin ruleEngineAdmin; @@ -71,7 +73,8 @@ public class PubSubTransportQueueFactory implements TbTransportQueueFactory { TbQueueTransportApiSettings transportApiSettings, TbQueueTransportNotificationSettings transportNotificationSettings, TbPubSubSubscriptionSettings pubSubSubscriptionSettings, - TopicService topicService) { + TopicService topicService, + TbPubSubQueueExecutorProvider tbPubSubQueueExecutorProvider) { this.pubSubSettings = pubSubSettings; this.serviceInfoProvider = serviceInfoProvider; this.coreSettings = coreSettings; @@ -79,6 +82,7 @@ public class PubSubTransportQueueFactory implements TbTransportQueueFactory { this.transportApiSettings = transportApiSettings; this.transportNotificationSettings = transportNotificationSettings; this.topicService = topicService; + this.tbPubSubQueueExecutorProvider = tbPubSubQueueExecutorProvider; this.coreAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getCoreSettings()); this.ruleEngineAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getRuleEngineSettings()); @@ -88,10 +92,10 @@ public class PubSubTransportQueueFactory implements TbTransportQueueFactory { @Override public TbQueueRequestTemplate, TbProtoQueueMsg> createTransportApiRequestTemplate() { - TbQueueProducer> producer = new TbPubSubProducerTemplate<>(transportApiAdmin, pubSubSettings, topicService.buildTopicName(transportApiSettings.getRequestsTopic())); + TbQueueProducer> producer = new TbPubSubProducerTemplate<>(transportApiAdmin, pubSubSettings, topicService.buildTopicName(transportApiSettings.getRequestsTopic()), tbPubSubQueueExecutorProvider); TbQueueConsumer> consumer = new TbPubSubConsumerTemplate<>(transportApiAdmin, pubSubSettings, topicService.buildTopicName(transportApiSettings.getResponsesTopic() + "." + serviceInfoProvider.getServiceId()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiResponseMsg.parseFrom(msg.getData()), msg.getHeaders())); + msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiResponseMsg.parseFrom(msg.getData()), msg.getHeaders()), tbPubSubQueueExecutorProvider); DefaultTbQueueRequestTemplate.DefaultTbQueueRequestTemplateBuilder , TbProtoQueueMsg> templateBuilder = DefaultTbQueueRequestTemplate.builder(); @@ -106,29 +110,29 @@ public class PubSubTransportQueueFactory implements TbTransportQueueFactory { @Override public TbQueueProducer> createRuleEngineMsgProducer() { - return new TbPubSubProducerTemplate<>(ruleEngineAdmin, pubSubSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); + return new TbPubSubProducerTemplate<>(ruleEngineAdmin, pubSubSettings, topicService.buildTopicName(ruleEngineSettings.getTopic()), tbPubSubQueueExecutorProvider); } @Override public TbQueueProducer> createTbCoreMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic())); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic()), tbPubSubQueueExecutorProvider); } @Override public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic())); + return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic()), tbPubSubQueueExecutorProvider); } @Override public TbQueueConsumer> createTransportNotificationsConsumer() { return new TbPubSubConsumerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic() + "." + serviceInfoProvider.getServiceId()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToTransportMsg.parseFrom(msg.getData()), msg.getHeaders())); + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToTransportMsg.parseFrom(msg.getData()), msg.getHeaders()), tbPubSubQueueExecutorProvider); } @Override public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic()), tbPubSubQueueExecutorProvider); } @PreDestroy diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubConsumerTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubConsumerTemplate.java index 97b7b47843..fd01696873 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubConsumerTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubConsumerTemplate.java @@ -17,6 +17,7 @@ package org.thingsboard.server.queue.pubsub; import com.google.api.core.ApiFuture; import com.google.api.core.ApiFutures; +import com.google.api.gax.core.FixedExecutorProvider; import com.google.cloud.pubsub.v1.stub.GrpcSubscriberStub; import com.google.cloud.pubsub.v1.stub.SubscriberStub; import com.google.cloud.pubsub.v1.stub.SubscriberStubSettings; @@ -62,7 +63,7 @@ public class TbPubSubConsumerTemplate extends AbstractPara private final SubscriberStub subscriber; private volatile int messagesPerTopic; - public TbPubSubConsumerTemplate(TbQueueAdmin admin, TbPubSubSettings pubSubSettings, String topic, TbQueueMsgDecoder decoder) { + public TbPubSubConsumerTemplate(TbQueueAdmin admin, TbPubSubSettings pubSubSettings, String topic, TbQueueMsgDecoder decoder, TbPubSubQueueExecutorProvider tbPubSubQueueExecutorProvider) { super(topic); this.admin = admin; this.pubSubSettings = pubSubSettings; @@ -76,6 +77,7 @@ public class TbPubSubConsumerTemplate extends AbstractPara SubscriberStubSettings.defaultGrpcTransportProviderBuilder() .setMaxInboundMessageSize(pubSubSettings.getMaxMsgSize()) .build()) + .setExecutorProvider(FixedExecutorProvider.create(tbPubSubQueueExecutorProvider.getExecutor())) .build(); this.subscriber = GrpcSubscriberStub.create(subscriberStubSettings); } catch (IOException e) { diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubProducerTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubProducerTemplate.java index 46c6bb6413..48a104f20a 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubProducerTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubProducerTemplate.java @@ -25,6 +25,7 @@ import com.google.protobuf.ByteString; import com.google.pubsub.v1.ProjectTopicName; import com.google.pubsub.v1.PubsubMessage; import lombok.extern.slf4j.Slf4j; +import org.thingsboard.common.util.ExecutorProvider; import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.queue.TbQueueAdmin; @@ -53,16 +54,14 @@ public class TbPubSubProducerTemplate implements TbQueuePr private final Map publisherMap = new ConcurrentHashMap<>(); private final ExecutorService pubExecutor = Executors.newCachedThreadPool(); - private static final int THREADS_PER_CPU = 5; private final FixedExecutorProvider fixedExecutorProvider; - public TbPubSubProducerTemplate(TbQueueAdmin admin, TbPubSubSettings pubSubSettings, String defaultTopic) { + public TbPubSubProducerTemplate(TbQueueAdmin admin, TbPubSubSettings pubSubSettings, String defaultTopic, ExecutorProvider executorProvider) { this.defaultTopic = defaultTopic; this.admin = admin; this.pubSubSettings = pubSubSettings; - ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(THREADS_PER_CPU * Runtime.getRuntime().availableProcessors(), ThingsBoardThreadFactory.forName("tb-pubsub-producer-scheduler"));; - fixedExecutorProvider = FixedExecutorProvider.create(scheduler); + fixedExecutorProvider = FixedExecutorProvider.create(executorProvider.getExecutor()); } @Override diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubQueueExecutorProvider.java b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubQueueExecutorProvider.java new file mode 100644 index 0000000000..2b636b5ee2 --- /dev/null +++ b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubQueueExecutorProvider.java @@ -0,0 +1,37 @@ +package org.thingsboard.server.queue.pubsub; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.ExecutorProvider; +import org.thingsboard.common.util.ThingsBoardThreadFactory; + +import javax.annotation.PostConstruct; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; + +@ConditionalOnExpression("'${queue.type:null}'=='pubsub'") +@Component +public class TbPubSubQueueExecutorProvider implements ExecutorProvider { + + @Value("${queue.pubsub.executor_thread_pool_size}") + private Integer threadPoolSize; + /** + * Refers to com.google.cloud.pubsub.v1.Publisher default executor configuration + */ + private static final int THREADS_PER_CPU = 5; + private ScheduledExecutorService executor; + + @PostConstruct + public void init() { + if (threadPoolSize == null) { + threadPoolSize = THREADS_PER_CPU * Runtime.getRuntime().availableProcessors(); + } + executor = Executors.newScheduledThreadPool(threadPoolSize, ThingsBoardThreadFactory.forName("pubsub-queue-executor"));; + } + + @Override + public ScheduledExecutorService getExecutor() { + return executor; + } +} diff --git a/common/util/pom.xml b/common/util/pom.xml index c2f5dfea6c..eab8ae982b 100644 --- a/common/util/pom.xml +++ b/common/util/pom.xml @@ -53,10 +53,6 @@ guava provided - - com.google.cloud - google-cloud-pubsub - javax.annotation javax.annotation-api diff --git a/common/util/src/main/java/org/thingsboard/common/util/ExecutorProvider.java b/common/util/src/main/java/org/thingsboard/common/util/ExecutorProvider.java new file mode 100644 index 0000000000..4abc46fb16 --- /dev/null +++ b/common/util/src/main/java/org/thingsboard/common/util/ExecutorProvider.java @@ -0,0 +1,8 @@ +package org.thingsboard.common.util; + +import java.util.concurrent.ScheduledExecutorService; + +public interface ExecutorProvider { + + ScheduledExecutorService getExecutor(); +} diff --git a/common/util/src/main/java/org/thingsboard/common/util/PubSubExecutor.java b/common/util/src/main/java/org/thingsboard/common/util/PubSubExecutor.java deleted file mode 100644 index 5354102578..0000000000 --- a/common/util/src/main/java/org/thingsboard/common/util/PubSubExecutor.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.thingsboard.common.util; - -import com.google.api.gax.core.FixedExecutorProvider; - -public interface PubSubExecutor { - FixedExecutorProvider getExecutorProvider(); -} diff --git a/msa/vc-executor/src/main/resources/tb-vc-executor.yml b/msa/vc-executor/src/main/resources/tb-vc-executor.yml index 4ffa1ab702..2353199c2b 100644 --- a/msa/vc-executor/src/main/resources/tb-vc-executor.yml +++ b/msa/vc-executor/src/main/resources/tb-vc-executor.yml @@ -172,6 +172,8 @@ queue: max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" #in bytes # Number of messages per consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" + # Number of threads of pubsub executor provider + executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_SIZE:}" queue-properties: # Pub/Sub properties for Core subscribers, messages which will commit after ackDeadlineInSec period can be consumed again core: "${TB_QUEUE_PUBSUB_CORE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java index cd71626afd..40ed2c378a 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java @@ -16,8 +16,8 @@ package org.thingsboard.rule.engine.api; import io.netty.channel.EventLoopGroup; +import org.thingsboard.common.util.ExecutorProvider; import org.thingsboard.common.util.ListeningExecutor; -import org.thingsboard.common.util.PubSubExecutor; import org.thingsboard.rule.engine.api.slack.SlackService; import org.thingsboard.rule.engine.api.sms.SmsSenderFactory; import org.thingsboard.server.cluster.TbClusterService; @@ -319,7 +319,7 @@ public interface TbContext { ListeningExecutor getNotificationExecutor(); - PubSubExecutor getPubsubExecutor(); + ExecutorProvider getPubSubRuleNodeExecutorProvider(); MailService getMailService(boolean isSystem); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNode.java index 4c24bdf1cb..39ab7bf525 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNode.java @@ -69,7 +69,7 @@ public class TbPubSubNode extends TbAbstractExternalNode { super.init(ctx); this.config = TbNodeUtils.convert(configuration, TbPubSubNodeConfiguration.class); try { - this.pubSubClient = initPubSubClient(ctx.getPubsubExecutor().getExecutorProvider()); + this.pubSubClient = initPubSubClient(FixedExecutorProvider.create(ctx.getPubSubRuleNodeExecutorProvider().getExecutor())); } catch (Exception e) { throw new TbNodeException(e); } diff --git a/transport/coap/src/main/resources/tb-coap-transport.yml b/transport/coap/src/main/resources/tb-coap-transport.yml index 437f551690..5588384efd 100644 --- a/transport/coap/src/main/resources/tb-coap-transport.yml +++ b/transport/coap/src/main/resources/tb-coap-transport.yml @@ -295,6 +295,8 @@ queue: max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" # Number of messages per consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" + # Number of threads of pubsub executor provider + executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_SIZE:}" queue-properties: # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consumed again rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" diff --git a/transport/http/src/main/resources/tb-http-transport.yml b/transport/http/src/main/resources/tb-http-transport.yml index 8d4d6fadf0..2ca2a04d1f 100644 --- a/transport/http/src/main/resources/tb-http-transport.yml +++ b/transport/http/src/main/resources/tb-http-transport.yml @@ -278,6 +278,8 @@ queue: max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" # Number of messages per a consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" + # Number of threads of pubsub executor provider + executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_SIZE:}" queue-properties: # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consume again rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" diff --git a/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml b/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml index 8fd1c11e6e..d8ee8d14c4 100644 --- a/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml +++ b/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml @@ -374,6 +374,8 @@ queue: max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" # Number of messages per consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" + # Number of threads of pubsub executor provider + executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_SIZE:}" queue-properties: # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consumed again rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" diff --git a/transport/mqtt/src/main/resources/tb-mqtt-transport.yml b/transport/mqtt/src/main/resources/tb-mqtt-transport.yml index 6c51d18ab7..6dc1213884 100644 --- a/transport/mqtt/src/main/resources/tb-mqtt-transport.yml +++ b/transport/mqtt/src/main/resources/tb-mqtt-transport.yml @@ -311,6 +311,8 @@ queue: max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" # Number of messages per consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" + # Number of threads of pubsub executor provider + executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_SIZE:}" queue-properties: # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consumed again rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" diff --git a/transport/snmp/src/main/resources/tb-snmp-transport.yml b/transport/snmp/src/main/resources/tb-snmp-transport.yml index eeb6807714..928c3c0642 100644 --- a/transport/snmp/src/main/resources/tb-snmp-transport.yml +++ b/transport/snmp/src/main/resources/tb-snmp-transport.yml @@ -264,6 +264,8 @@ queue: max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" # Number of messages per consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" + # Number of threads of pubsub executor provider + executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_SIZE:}" queue-properties: # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consumed again rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" From aab09a680406b39265bb8ab9fcd3fc933fa6b55d Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Wed, 15 Nov 2023 14:56:17 +0200 Subject: [PATCH 134/252] reverted thingsboard.yml local changes --- application/src/main/resources/thingsboard.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 114dafa6f0..6961ca0655 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -1404,9 +1404,9 @@ queue: version-control: "${TB_QUEUE_AWS_SQS_VC_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" pubsub: # Project ID from Google Cloud - project_id: "${TB_QUEUE_PUBSUB_PROJECT_ID:pubsub-389210}" + project_id: "${TB_QUEUE_PUBSUB_PROJECT_ID:YOUR_PROJECT_ID}" # API Credentials in JSON format - service_account: "${TB_QUEUE_PUBSUB_SERVICE_ACCOUNT:{\n \"type\": \"service_account\",\n \"project_id\": \"pubsub-389210\",\n \"private_key_id\": \"e83e233f60bb75f52e6ee9d34046c1e7b5e3f2e6\",\n \"private_key\": \"-----BEGIN PRIVATE KEY-----\\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCmram5Oa60kyFM\\nFwo7d4yYA2U4vLUa2HV7h5G5ZxhXTPk+V4eYneBPSX0bJ/V5L4z7N/xNNwDR80i3\\n+tCFIf9P+jS3sE0ThZDc8Rdgo1Ln8nm4qf/9uBrOdSjn5VKKwvnNXphwTniwzssS\\nWn3AMuL8flgKoOiRaBOMXykIWN4/epKmv1tmnnq57XLW2cRTsE+pCcbqn8W2LEgA\\nNP+NJvvhQ6wsU341F2oTMAsuwsmiOZ1aiKKCafjGb/STPYy/WXsLjrfwK08U2SJh\\n+hHyvzdH1AU40khelnLjQhrltUGk1VAaKtucDKGSAoL5JIkH0Q5qczz1Ys6l3VvK\\nlhDNP8NHAgMBAAECggEAFUVJSRGC6ZlzIN452I/vmcCpLL3sUMI57+tDngj5kr6I\\neju6WrAfHY8vz4TgbzgxwieJY6M20BQ/ffccoDjP97li++QcWfbsHz4HMTZ2kJIh\\nlX7gY9UYWquZ7koWKA4sydgeFQr2nP2u7actsBbuX7GR871IILJK+Fl2h5Grvybk\\nz70RzLY+M26ZVBf+ejKlyaoMVLQ6w272llc5/SJ4jmsbn1urLM2+ChgAjOeqo7Qp\\nBBjUJmjC9b5Tev0jg+FXdw+ba41bXmYM5x265buwbY4vYyIJ+XF6dEl36cdVa1xI\\n+XISQZ66SuvEkHRc+x1hEEMymOtt8d++SeFn4/BkDQKBgQDkGtYA/8751C8xDB4+\\nlpxXf3wN/QjWPpXAV1fyj9sgwz05M1E0KzYpX6H2rWVqFhPkuqZUCeZ5Iqjvybp7\\n8upLJ+lRpHag1dkS4ElICNNcHhIQB9xgmFtdH4euTzuAFTd5nZ43oRt6gGEo0I/S\\nmKmupS2IwpM2/Qm6dP7l2ruS5QKBgQC7D8eTIIu8x2plJ6rebEAneX6aAUzQ9chk\\nRB53wjDx0UCXC/J2l1PX0vGGY8JOQdmDS6MPdFaVZxnqtrrGNBDuSArDNH7bV0ab\\nBKnLStPOwkehQEhg6ShHX7r4EPs6GBTWDQnnCgR8KGq+7rdo/lz7C/TK8+xqRaWq\\nYCUMY4g+uwKBgHtPFokLwHPFhI1bI65p9LJINGPLec93nbSQgvaZVbfsU1hsqWUu\\nRuUu0XtsWPp0XOS1Ed00TOcHGZQm1SzUMFvYg4SjB44CjIprvLG4M6oEh3crCLMf\\nKaS5urs8Eco3rfmMf09LRHOAmwVZWaZa6L+Eg4z+wl5jg7LMNE9FY0kRAoGACU+J\\nwXr3OZg7ZXmJ+bQtpUlY0dWKu0Pgi40QbymNQhwGOP5xPRHfLHjlaKkCfN6uMujK\\n3vQNczZEhfg+Z6sjxJh1YK32OninnQOoZ+P7kuj8o7wNXjV8ucC8D6jYuFWGg/j2\\nKzfGbV+doI9FNcajXiOENa3acJey3T4X2fwCRg0CgYEA0m3iO8HvnWhAs+clP2Ph\\nI8mXNbNbtCa7bOPmhdidSwJpbXNNwKNU7xAXHHueFGlEnNFsQrqy7bPfol4ABOhl\\nV3Zdsuc5v1OXM7IrLZ16cC7fogydSQz8QK86TcC50u/aSmkaevujsJfabC/YF7sR\\nBl8tPN7I4H72JKOuUTn/Sfo=\\n-----END PRIVATE KEY-----\\n\",\n \"client_email\": \"pubsubaccountname@pubsub-389210.iam.gserviceaccount.com\",\n \"client_id\": \"103395736410565244836\",\n \"auth_uri\": \"https://accounts.google.com/o/oauth2/auth\",\n \"token_uri\": \"https://oauth2.googleapis.com/token\",\n \"auth_provider_x509_cert_url\": \"https://www.googleapis.com/oauth2/v1/certs\",\n \"client_x509_cert_url\": \"https://www.googleapis.com/robot/v1/metadata/x509/pubsubaccountname%40pubsub-389210.iam.gserviceaccount.com\",\n \"universe_domain\": \"googleapis.com\"\n}}" + service_account: "${TB_QUEUE_PUBSUB_SERVICE_ACCOUNT:YOUR_SERVICE_ACCOUNT}" # Message size for PubSub queue.Value in bytes max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" # Number of messages per consumer From f26d827ab8daf5d7bf6766d0e830d7bc9831c196 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Wed, 15 Nov 2023 14:57:41 +0200 Subject: [PATCH 135/252] reverted thingsboard.yml local changes --- application/src/main/resources/thingsboard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 6961ca0655..ccc4953793 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -1270,7 +1270,7 @@ swagger: # Queue configuration parameters queue: - type: "${TB_QUEUE_TYPE:pubsub}" # in-memory or kafka (Apache Kafka) or aws-sqs (AWS SQS) or pubsub (PubSub) or service-bus (Azure Service Bus) or rabbitmq (RabbitMQ) + type: "${TB_QUEUE_TYPE:in-memory}" # in-memory or kafka (Apache Kafka) or aws-sqs (AWS SQS) or pubsub (PubSub) or service-bus (Azure Service Bus) or rabbitmq (RabbitMQ) prefix: "${TB_QUEUE_PREFIX:}" # Global queue prefix. If specified, prefix is added before default topic name: 'prefix.default_topic_name'. Prefix is applied to all topics (and consumer groups for kafka) except of js executor topics (please use REMOTE_JS_EVAL_REQUEST_TOPIC and REMOTE_JS_EVAL_RESPONSE_TOPIC to specify custom topic names) in_memory: stats: From edcc507f4b9ec40ee92c70f2c0d8f8490f7ccbe8 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Wed, 15 Nov 2023 15:14:41 +0200 Subject: [PATCH 136/252] Subscribe first in testSubscribingToUnreadNotificationsCount --- .../server/service/notification/NotificationApiTest.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java index 26be4e9bd3..ba0b4e4763 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java @@ -113,15 +113,13 @@ public class NotificationApiTest extends AbstractNotificationApiTest { @Test public void testSubscribingToUnreadNotificationsCount() { + wsClient.subscribeForUnreadNotificationsCount().waitForReply(true); NotificationTarget notificationTarget = createNotificationTarget(customerUserId); String notificationText1 = "Notification 1"; submitNotificationRequest(notificationTarget.getId(), notificationText1); String notificationText2 = "Notification 2"; submitNotificationRequest(notificationTarget.getId(), notificationText2); - wsClient.subscribeForUnreadNotificationsCount(); - wsClient.waitForReply(true); - await().atMost(2, TimeUnit.SECONDS) .until(() -> wsClient.getLastCountUpdate().getTotalUnreadCount() == 2); } From a4bac1e7948e55b148a974e970305756fda0e613 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Wed, 15 Nov 2023 15:57:58 +0200 Subject: [PATCH 137/252] Implement cache for the entity creation request --- .../transport/DefaultTransportApiService.java | 9 +++ .../src/main/resources/thingsboard.yml | 5 +- common/cluster-api/src/main/proto/queue.proto | 6 ++ .../server/common/data/DataConstants.java | 1 + .../AbstractGatewaySessionHandler.java | 22 ++++-- .../common/transport/TransportService.java | 2 +- .../limits/DefaultEntityLimitsCache.java | 74 +++++++++++++++++++ .../transport/limits/EntityLimitKey.java | 27 +++++++ .../transport/limits/EntityLimitsCache.java | 24 ++++++ .../service/DefaultTransportService.java | 48 ++++++++---- .../dao/exception/EntitiesLimitException.java | 35 +++++++++ .../server/dao/service/DataValidator.java | 3 +- .../src/main/resources/tb-coap-transport.yml | 4 + .../src/main/resources/tb-http-transport.yml | 4 + .../src/main/resources/tb-lwm2m-transport.yml | 4 + .../src/main/resources/tb-mqtt-transport.yml | 4 + .../src/main/resources/tb-snmp-transport.yml | 4 + 17 files changed, 251 insertions(+), 25 deletions(-) create mode 100644 common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/limits/DefaultEntityLimitsCache.java create mode 100644 common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/limits/EntityLimitKey.java create mode 100644 common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/limits/EntityLimitsCache.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/exception/EntitiesLimitException.java diff --git a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java index 3505052fca..631e64a285 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java @@ -78,6 +78,7 @@ import org.thingsboard.server.dao.device.provision.ProvisionFailedException; import org.thingsboard.server.dao.device.provision.ProvisionRequest; import org.thingsboard.server.dao.device.provision.ProvisionResponse; import org.thingsboard.server.dao.device.provision.ProvisionResponseStatus; +import org.thingsboard.server.dao.exception.EntitiesLimitException; import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.relation.RelationService; @@ -164,6 +165,7 @@ public class DefaultTransportApiService implements TransportApiService { public void init() { handlerExecutor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(maxCoreHandlerThreads, "transport-api-service-core-handler")); } + @PreDestroy public void destroy() { if (handlerExecutor != null) { @@ -400,6 +402,13 @@ public class DefaultTransportApiService implements TransportApiService { } catch (JsonProcessingException e) { log.warn("[{}] Failed to lookup device by gateway id and name: [{}]", gatewayId, requestMsg.getDeviceName(), e); throw new RuntimeException(e); + } catch (EntitiesLimitException e) { + log.warn("[{}][{}] API limit exception: [{}]", e.getTenantId(), gatewayId, e.getMessage()); + return TransportApiResponseMsg.newBuilder() + .setGetOrCreateDeviceResponseMsg( + GetOrCreateDeviceFromGatewayResponseMsg.newBuilder() + .setError(TransportProtos.TransportApiRequestErrorCode.ENTITY_LIMIT)) + .build(); } finally { deviceCreationLock.unlock(); } diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index bdf0ad8252..ecc89c84c5 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -583,6 +583,9 @@ cache: rateLimits: timeToLiveInMinutes: "${CACHE_SPECS_RATE_LIMITS_TTL:120}" # Rate limits cache TTL maxSize: "${CACHE_SPECS_RATE_LIMITS_MAX_SIZE:200000}" # 0 means the cache is disabled + entityLimits: + timeToLiveInMinutes: "${CACHE_SPECS_ENTITY_LIMITS_TTL:5}" # Entity limits cache TTL + maxSize: "${CACHE_SPECS_ENTITY_LIMITS_MAX_SIZE:100000}" # 0 means the cache is disabled # Spring data parameters spring.data.redis.repositories.enabled: false # Disable this because it is not required. @@ -875,7 +878,7 @@ transport: ip_limits_enabled: "${TB_TRANSPORT_IP_RATE_LIMITS_ENABLED:false}" # Maximum number of connect attempts with invalid credentials max_wrong_credentials_per_ip: "${TB_TRANSPORT_MAX_WRONG_CREDENTIALS_PER_IP:10}" - # Timeout to expire block IP addresses + # Timeout (in milliseconds) to expire block IP addresses ip_block_timeout: "${TB_TRANSPORT_IP_BLOCK_TIMEOUT:60000}" # Local HTTP transport parameters http: diff --git a/common/cluster-api/src/main/proto/queue.proto b/common/cluster-api/src/main/proto/queue.proto index 016a40f5dc..0cb91faee0 100644 --- a/common/cluster-api/src/main/proto/queue.proto +++ b/common/cluster-api/src/main/proto/queue.proto @@ -255,6 +255,12 @@ message GetOrCreateDeviceFromGatewayRequestMsg { message GetOrCreateDeviceFromGatewayResponseMsg { DeviceInfoProto deviceInfo = 1; bytes profileBody = 2; + TransportApiRequestErrorCode error = 3; +} + +enum TransportApiRequestErrorCode { + UNKNOWN_TRANSPORT_API_ERROR = 0; + ENTITY_LIMIT = 1; } message GetEntityProfileRequestMsg { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java b/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java index 544f5805a6..4a84743283 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java @@ -49,6 +49,7 @@ public class DataConstants { public static final String MQTT_TRANSPORT_NAME = "MQTT"; public static final String HTTP_TRANSPORT_NAME = "HTTP"; public static final String SNMP_TRANSPORT_NAME = "SNMP"; + public static final String MAXIMUM_NUMBER_OF_DEVICES_REACHED = "Maximum number of devices reached!"; public static final String[] allScopes() { diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/AbstractGatewaySessionHandler.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/AbstractGatewaySessionHandler.java index fd0297203d..8f964f8369 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/AbstractGatewaySessionHandler.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/AbstractGatewaySessionHandler.java @@ -35,6 +35,7 @@ import io.netty.handler.codec.mqtt.MqttPublishMessage; import lombok.extern.slf4j.Slf4j; import org.springframework.util.CollectionUtils; import org.springframework.util.ConcurrentReferenceHashMap; +import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.transport.TransportService; @@ -215,8 +216,7 @@ public abstract class AbstractGatewaySessionHandler callback); - void process(GetOrCreateDeviceFromGatewayRequestMsg msg, + void process(TenantId tenantId, GetOrCreateDeviceFromGatewayRequestMsg msg, TransportServiceCallback callback); void process(ProvisionDeviceRequestMsg msg, diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/limits/DefaultEntityLimitsCache.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/limits/DefaultEntityLimitsCache.java new file mode 100644 index 0000000000..3bfab1e3f0 --- /dev/null +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/limits/DefaultEntityLimitsCache.java @@ -0,0 +1,74 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.common.transport.limits; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.Expiry; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.thingsboard.server.queue.util.TbTransportComponent; + +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; + +@Service +@TbTransportComponent +@Slf4j +public class DefaultEntityLimitsCache implements EntityLimitsCache { + + private static final int DEVIATION = 10; + private final Cache cache; + + public DefaultEntityLimitsCache(@Value("${cache.entityLimits.timeToLiveInMinutes:5}") int ttl, + @Value("${cache.entityLimits.maxSize:100000}") int maxSize) { + // We use the 'random' expiration time to avoid peak loads. + long mainPart = (TimeUnit.MINUTES.toNanos(ttl) / 100) * (100 - DEVIATION); + long randomPart = (TimeUnit.MINUTES.toNanos(ttl) / 100) * DEVIATION; + cache = Caffeine.newBuilder() + .expireAfter(new Expiry() { + @Override + public long expireAfterCreate(@NotNull EntityLimitKey key, @NotNull Boolean value, long currentTime) { + return mainPart + (long) (randomPart * ThreadLocalRandom.current().nextDouble()); + } + + @Override + public long expireAfterUpdate(@NotNull EntityLimitKey key, @NotNull Boolean value, long currentTime, long currentDuration) { + return currentDuration; + } + + @Override + public long expireAfterRead(@NotNull EntityLimitKey key, @NotNull Boolean value, long currentTime, long currentDuration) { + return currentDuration; + } + }) + .maximumSize(maxSize) + .build(); + } + + @Override + public boolean get(EntityLimitKey key) { + var result = cache.getIfPresent(key); + return result != null ? result : false; + } + + @Override + public void put(EntityLimitKey key, boolean value) { + cache.put(key, value); + } +} diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/limits/EntityLimitKey.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/limits/EntityLimitKey.java new file mode 100644 index 0000000000..f8957f780d --- /dev/null +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/limits/EntityLimitKey.java @@ -0,0 +1,27 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.common.transport.limits; + +import lombok.Data; +import org.thingsboard.server.common.data.id.TenantId; + +@Data +public class EntityLimitKey { + + private final TenantId tenantId; + private final String deviceName; + +} diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/limits/EntityLimitsCache.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/limits/EntityLimitsCache.java new file mode 100644 index 0000000000..b350a2eb27 --- /dev/null +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/limits/EntityLimitsCache.java @@ -0,0 +1,24 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.common.transport.limits; + +public interface EntityLimitsCache { + + boolean get(EntityLimitKey key); + + void put(EntityLimitKey key, boolean value); + +} diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java index cc4a1bc3df..c4840b2402 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java @@ -76,6 +76,8 @@ import org.thingsboard.server.common.transport.TransportTenantProfileCache; import org.thingsboard.server.common.transport.auth.GetOrCreateDeviceFromGatewayResponse; import org.thingsboard.server.common.transport.auth.TransportDeviceInfo; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; +import org.thingsboard.server.common.transport.limits.EntityLimitKey; +import org.thingsboard.server.common.transport.limits.EntityLimitsCache; import org.thingsboard.server.common.transport.limits.TransportRateLimitService; import org.thingsboard.server.common.transport.util.JsonUtils; import org.thingsboard.server.gen.transport.TransportProtos; @@ -161,6 +163,7 @@ public class DefaultTransportService implements TransportService { @Value("${transport.stats.enabled:false}") private boolean statsEnabled; + @Autowired @Lazy private TbApiUsageReportClient apiUsageClient; @@ -184,6 +187,8 @@ public class DefaultTransportService implements TransportService { private final TransportResourceCache transportResourceCache; private final NotificationRuleProcessor notificationRuleProcessor; + private final EntityLimitsCache entityLimitsCache; + protected TbQueueRequestTemplate, TbProtoQueueMsg> transportApiRequestTemplate; protected TbQueueProducer> ruleEngineMsgProducer; protected TbQueueProducer> tbCoreMsgProducer; @@ -212,7 +217,8 @@ public class DefaultTransportService implements TransportService { TransportTenantProfileCache tenantProfileCache, TransportRateLimitService rateLimitService, DataDecodingEncodingService dataDecodingEncodingService, SchedulerComponent scheduler, TransportResourceCache transportResourceCache, - ApplicationEventPublisher eventPublisher, NotificationRuleProcessor notificationRuleProcessor) { + ApplicationEventPublisher eventPublisher, NotificationRuleProcessor notificationRuleProcessor, + EntityLimitsCache entityLimitsCache) { this.partitionService = partitionService; this.serviceInfoProvider = serviceInfoProvider; this.queueProvider = queueProvider; @@ -227,6 +233,7 @@ public class DefaultTransportService implements TransportService { this.transportResourceCache = transportResourceCache; this.eventPublisher = eventPublisher; this.notificationRuleProcessor = notificationRuleProcessor; + this.entityLimitsCache = entityLimitsCache; } @PostConstruct @@ -249,7 +256,7 @@ public class DefaultTransportService implements TransportService { } @AfterStartUp(order = AfterStartUp.TRANSPORT_SERVICE) - private void start() { + public void start() { mainConsumerExecutor.execute(() -> { while (!stopped) { try { @@ -473,24 +480,33 @@ public class DefaultTransportService implements TransportService { AsyncCallbackTemplate.withCallback(response, callback::onSuccess, callback::onError, transportCallbackExecutor); } + @Override - public void process(TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg requestMsg, TransportServiceCallback callback) { + public void process(TenantId tenantId, TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg requestMsg, TransportServiceCallback callback) { TbProtoQueueMsg protoMsg = new TbProtoQueueMsg<>(UUID.randomUUID(), TransportApiRequestMsg.newBuilder().setGetOrCreateDeviceRequestMsg(requestMsg).build()); log.trace("Processing msg: {}", requestMsg); - ListenableFuture response = Futures.transform(transportApiRequestTemplate.send(protoMsg), tmp -> { - TransportProtos.GetOrCreateDeviceFromGatewayResponseMsg msg = tmp.getValue().getGetOrCreateDeviceResponseMsg(); - GetOrCreateDeviceFromGatewayResponse.GetOrCreateDeviceFromGatewayResponseBuilder result = GetOrCreateDeviceFromGatewayResponse.builder(); - if (msg.hasDeviceInfo()) { - TransportDeviceInfo tdi = getTransportDeviceInfo(msg.getDeviceInfo()); - result.deviceInfo(tdi); - ByteString profileBody = msg.getProfileBody(); - if (profileBody != null && !profileBody.isEmpty()) { - result.deviceProfile(deviceProfileCache.getOrCreate(tdi.getDeviceProfileId(), profileBody)); + var key = new EntityLimitKey(tenantId, StringUtils.truncate(requestMsg.getDeviceName(), 256)); + if (entityLimitsCache.get(key)) { + transportCallbackExecutor.submit(() -> callback.onError(new RuntimeException(DataConstants.MAXIMUM_NUMBER_OF_DEVICES_REACHED))); + } else { + ListenableFuture response = Futures.transform(transportApiRequestTemplate.send(protoMsg), tmp -> { + TransportProtos.GetOrCreateDeviceFromGatewayResponseMsg msg = tmp.getValue().getGetOrCreateDeviceResponseMsg(); + GetOrCreateDeviceFromGatewayResponse.GetOrCreateDeviceFromGatewayResponseBuilder result = GetOrCreateDeviceFromGatewayResponse.builder(); + if (msg.hasDeviceInfo()) { + TransportDeviceInfo tdi = getTransportDeviceInfo(msg.getDeviceInfo()); + result.deviceInfo(tdi); + ByteString profileBody = msg.getProfileBody(); + if (!profileBody.isEmpty()) { + result.deviceProfile(deviceProfileCache.getOrCreate(tdi.getDeviceProfileId(), profileBody)); + } + } else if (TransportProtos.TransportApiRequestErrorCode.ENTITY_LIMIT.equals(msg.getError())) { + entityLimitsCache.put(key, true); + throw new RuntimeException(DataConstants.MAXIMUM_NUMBER_OF_DEVICES_REACHED); } - } - return result.build(); - }, MoreExecutors.directExecutor()); - AsyncCallbackTemplate.withCallback(response, callback::onSuccess, callback::onError, transportCallbackExecutor); + return result.build(); + }, MoreExecutors.directExecutor()); + AsyncCallbackTemplate.withCallback(response, callback::onSuccess, callback::onError, transportCallbackExecutor); + } } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/exception/EntitiesLimitException.java b/dao/src/main/java/org/thingsboard/server/dao/exception/EntitiesLimitException.java new file mode 100644 index 0000000000..2f618e94d5 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/exception/EntitiesLimitException.java @@ -0,0 +1,35 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.dao.exception; + +import lombok.Getter; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.TenantId; + +public class EntitiesLimitException extends DataValidationException { + private static final long serialVersionUID = -9211462514373279196L; + + @Getter + private final TenantId tenantId; + @Getter + private final EntityType entityType; + + public EntitiesLimitException(TenantId tenantId, EntityType entityType) { + super(entityType.getNormalName() + "s limit reached"); + this.tenantId = tenantId; + this.entityType = entityType; + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java index d81c05044f..2c011b810e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java @@ -26,6 +26,7 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.TenantEntityWithDataDao; import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.exception.EntitiesLimitException; import org.thingsboard.server.dao.usagerecord.ApiLimitService; import java.util.HashSet; @@ -117,7 +118,7 @@ public abstract class DataValidator> { protected void validateNumberOfEntitiesPerTenant(TenantId tenantId, EntityType entityType) { if (!apiLimitService.checkEntitiesLimit(tenantId, entityType)) { - throw new DataValidationException(entityType.getNormalName() + "s limit reached"); + throw new EntitiesLimitException(tenantId, entityType); } } diff --git a/transport/coap/src/main/resources/tb-coap-transport.yml b/transport/coap/src/main/resources/tb-coap-transport.yml index 437f551690..959b3285de 100644 --- a/transport/coap/src/main/resources/tb-coap-transport.yml +++ b/transport/coap/src/main/resources/tb-coap-transport.yml @@ -49,6 +49,10 @@ zk: cache: # caffeine or redis type: "${CACHE_TYPE:redis}" + # Deliberately placed outside the 'specs' group above + entityLimits: + timeToLiveInMinutes: "${CACHE_SPECS_ENTITY_LIMITS_TTL:5}" # Entity limits cache TTL + maxSize: "${CACHE_SPECS_ENTITY_LIMITS_MAX_SIZE:100000}" # 0 means the cache is disabled # Redis configuration parameters redis: diff --git a/transport/http/src/main/resources/tb-http-transport.yml b/transport/http/src/main/resources/tb-http-transport.yml index 8d4d6fadf0..7edaa640a1 100644 --- a/transport/http/src/main/resources/tb-http-transport.yml +++ b/transport/http/src/main/resources/tb-http-transport.yml @@ -79,6 +79,10 @@ zk: cache: # caffeine or redis type: "${CACHE_TYPE:redis}" + # Deliberately placed outside the 'specs' group above + entityLimits: + timeToLiveInMinutes: "${CACHE_SPECS_ENTITY_LIMITS_TTL:5}" # Entity limits cache TTL + maxSize: "${CACHE_SPECS_ENTITY_LIMITS_MAX_SIZE:100000}" # 0 means the cache is disabled # Redis configuration parameters redis: diff --git a/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml b/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml index 8fd1c11e6e..cebc0425d5 100644 --- a/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml +++ b/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml @@ -49,6 +49,10 @@ zk: cache: # caffeine or redis type: "${CACHE_TYPE:redis}" + # Deliberately placed outside the 'specs' group above + entityLimits: + timeToLiveInMinutes: "${CACHE_SPECS_ENTITY_LIMITS_TTL:5}" # Entity limits cache TTL + maxSize: "${CACHE_SPECS_ENTITY_LIMITS_MAX_SIZE:100000}" # 0 means the cache is disabled # Redis configuration parameters redis: diff --git a/transport/mqtt/src/main/resources/tb-mqtt-transport.yml b/transport/mqtt/src/main/resources/tb-mqtt-transport.yml index 6c51d18ab7..330473a624 100644 --- a/transport/mqtt/src/main/resources/tb-mqtt-transport.yml +++ b/transport/mqtt/src/main/resources/tb-mqtt-transport.yml @@ -49,6 +49,10 @@ zk: cache: # caffeine or redis type: "${CACHE_TYPE:redis}" + # Deliberately placed outside the 'specs' group above + entityLimits: + timeToLiveInMinutes: "${CACHE_SPECS_ENTITY_LIMITS_TTL:5}" # Entity limits cache TTL + maxSize: "${CACHE_SPECS_ENTITY_LIMITS_MAX_SIZE:100000}" # 0 means the cache is disabled # Redis configuration parameters redis: diff --git a/transport/snmp/src/main/resources/tb-snmp-transport.yml b/transport/snmp/src/main/resources/tb-snmp-transport.yml index eeb6807714..ca13212293 100644 --- a/transport/snmp/src/main/resources/tb-snmp-transport.yml +++ b/transport/snmp/src/main/resources/tb-snmp-transport.yml @@ -49,6 +49,10 @@ zk: cache: # caffeine or redis type: "${CACHE_TYPE:redis}" + # Deliberately placed outside the 'specs' group above + entityLimits: + timeToLiveInMinutes: "${CACHE_SPECS_ENTITY_LIMITS_TTL:5}" # Entity limits cache TTL + maxSize: "${CACHE_SPECS_ENTITY_LIMITS_MAX_SIZE:100000}" # 0 means the cache is disabled # Redis configuration parameters redis: From e7aabe80a43c30e29a77c6d6e15c1bb8f932052d Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Wed, 15 Nov 2023 16:10:18 +0200 Subject: [PATCH 138/252] Unique image resource key based on index --- .../server/controller/ImageController.java | 26 ++++++------ .../resource/DefaultTbImageService.java | 1 - .../server/common/data/ImageDescriptor.java | 1 + .../thingsboard/common/util/RegexUtils.java | 11 +++++ .../server/dao/resource/BaseImageService.java | 41 +++++++++++++++++-- .../dao/resource/BaseResourceService.java | 23 ++++++----- .../dao/resource/TbResourceInfoDao.java | 4 ++ .../validator/ResourceDataValidator.java | 5 ++- .../sql/resource/JpaTbResourceInfoDao.java | 10 +++++ .../resource/TbResourceInfoRepository.java | 9 +++- 10 files changed, 102 insertions(+), 29 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/ImageController.java b/application/src/main/java/org/thingsboard/server/controller/ImageController.java index 159cd1eb0c..add38ee272 100644 --- a/application/src/main/java/org/thingsboard/server/controller/ImageController.java +++ b/application/src/main/java/org/thingsboard/server/controller/ImageController.java @@ -125,7 +125,8 @@ public class ImageController extends BaseController { TenantId tenantId = getTenantId(); TbResourceInfo imageInfo = checkImageInfo(type, key, Operation.READ); ImageDescriptor descriptor = imageInfo.getDescriptor(ImageDescriptor.class); - return downloadIfChanged(etag, imageInfo, descriptor, () -> imageService.getImageData(tenantId, imageInfo.getId())); + return downloadIfChanged(etag, descriptor, imageInfo.getFileName(), + () -> imageService.getImageData(tenantId, imageInfo.getId())); } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @@ -136,7 +137,8 @@ public class ImageController extends BaseController { TenantId tenantId = getTenantId(); TbResourceInfo imageInfo = checkImageInfo(type, key, Operation.READ); ImageDescriptor descriptor = imageInfo.getDescriptor(ImageDescriptor.class); - return downloadIfChanged(etag, imageInfo, descriptor.getPreviewDescriptor(), () -> imageService.getImagePreview(tenantId, imageInfo.getId())); + return downloadIfChanged(etag, descriptor.getPreviewDescriptor(), imageInfo.getFileName(), + () -> imageService.getImagePreview(tenantId, imageInfo.getId())); } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @@ -176,24 +178,22 @@ public class ImageController extends BaseController { tbImageService.delete(imageInfo, getCurrentUser()); } - private ResponseEntity downloadIfChanged(String etag, TbResourceInfo imageInfo, ImageDescriptor imageDescriptor, - Supplier dataSupplier) { - if (etag != null) { - if (etag.equals(imageInfo.getEtag())) { - return ResponseEntity.status(HttpStatus.NOT_MODIFIED) - .eTag(etag) - .build(); - } + private ResponseEntity downloadIfChanged(String actualEtag, ImageDescriptor imageDescriptor, + String fileName, Supplier dataSupplier) { + if (imageDescriptor.getEtag().equals(actualEtag)) { + return ResponseEntity.status(HttpStatus.NOT_MODIFIED) + .eTag(actualEtag) + .build(); } byte[] data = dataSupplier.get(); return ResponseEntity.ok() - .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + imageInfo.getFileName()) - .header("x-filename", imageInfo.getFileName()) + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + fileName) + .header("x-filename", fileName) .contentLength(data.length) .header("Content-Type", imageDescriptor.getMediaType()) .cacheControl(CacheControl.noCache()) - .eTag(imageInfo.getEtag()) + .eTag(imageDescriptor.getEtag()) .body(new ByteArrayResource(data)); } diff --git a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java index 188dff20d5..65304c8270 100644 --- a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java +++ b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java @@ -39,7 +39,6 @@ public class DefaultTbImageService extends AbstractTbEntityService implements Tb public TbResourceInfo save(TbResource image, User user) throws Exception { ActionType actionType = image.getId() == null ? ActionType.ADDED : ActionType.UPDATED; TenantId tenantId = image.getTenantId(); - image.setResourceKey(image.getFileName()); // TODO: generate unique resource key file_name+idx try { TbResourceInfo savedImage = imageService.saveImage(image); notificationEntityService.logEntityAction(tenantId, savedImage.getId(), savedImage, actionType, user); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/ImageDescriptor.java b/common/data/src/main/java/org/thingsboard/server/common/data/ImageDescriptor.java index 2bd7c59a85..367531565f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/ImageDescriptor.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ImageDescriptor.java @@ -25,5 +25,6 @@ public class ImageDescriptor { private int width; private int height; private long size; + private String etag; private ImageDescriptor previewDescriptor; } diff --git a/common/util/src/main/java/org/thingsboard/common/util/RegexUtils.java b/common/util/src/main/java/org/thingsboard/common/util/RegexUtils.java index 87d1e11bab..bbeb86efca 100644 --- a/common/util/src/main/java/org/thingsboard/common/util/RegexUtils.java +++ b/common/util/src/main/java/org/thingsboard/common/util/RegexUtils.java @@ -19,6 +19,7 @@ import lombok.AccessLevel; import lombok.NoArgsConstructor; import java.util.function.UnaryOperator; +import java.util.regex.Matcher; import java.util.regex.Pattern; @NoArgsConstructor(access = AccessLevel.PRIVATE) @@ -37,4 +38,14 @@ public class RegexUtils { return pattern.matcher(input).matches(); } + public static String getMatch(String input, Pattern pattern, int group) { + Matcher matcher = pattern.matcher(input); + if (matcher.find()) { + try { + return matcher.group(group); + } catch (Exception ignored) {} + } + return null; + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java index c046ca1c33..778192b49c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java @@ -16,9 +16,12 @@ package org.thingsboard.server.dao.resource; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.common.util.RegexUtils; import org.thingsboard.server.common.data.ImageDescriptor; import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.TbResource; @@ -32,7 +35,9 @@ import org.thingsboard.server.dao.service.validator.ResourceDataValidator; import org.thingsboard.server.dao.util.ImageUtils; import org.thingsboard.server.dao.util.ImageUtils.ProcessedImage; +import java.util.Objects; import java.util.Set; +import java.util.regex.Pattern; @Service @Slf4j @@ -42,17 +47,22 @@ public class BaseImageService extends BaseResourceService implements ImageServic super(resourceDao, resourceInfoDao, resourceValidator); } + @Transactional @Override public TbResourceInfo saveImage(TbResource image) throws Exception { + if (image.getId() == null) { + image.setResourceKey(getUniqueKey(image.getTenantId(), image.getFileName())); + } resourceValidator.validate(image, TbResourceInfo::getTenantId); ImageDescriptor descriptor = image.getDescriptor(ImageDescriptor.class); Pair result = processImage(image.getData(), descriptor); - image.setDescriptor(JacksonUtil.valueToTree(result.getLeft())); + descriptor = result.getLeft(); + image.setEtag(descriptor.getEtag()); + image.setDescriptor(JacksonUtil.valueToTree(descriptor)); image.setPreview(result.getRight()); - image = saveResource(image, false); - return new TbResourceInfo(image); + return new TbResourceInfo(doSaveResource(image)); } private Pair processImage(byte[] data, ImageDescriptor descriptor) throws Exception { @@ -62,17 +72,42 @@ public class BaseImageService extends BaseResourceService implements ImageServic descriptor.setWidth(image.getWidth()); descriptor.setHeight(image.getHeight()); descriptor.setSize(image.getSize()); + descriptor.setEtag(calculateEtag(data)); ImageDescriptor previewDescriptor = new ImageDescriptor(); previewDescriptor.setWidth(preview.getWidth()); previewDescriptor.setHeight(preview.getHeight()); previewDescriptor.setMediaType(preview.getMediaType()); previewDescriptor.setSize(preview.getSize()); + previewDescriptor.setEtag(preview.getData() != null ? calculateEtag(preview.getData()) : descriptor.getEtag()); descriptor.setPreviewDescriptor(previewDescriptor); return Pair.of(descriptor, preview.getData()); } + private String getUniqueKey(TenantId tenantId, String filename) { + if (!resourceInfoDao.existsByTenantIdAndResourceTypeAndResourceKey(tenantId, ResourceType.IMAGE, filename)) { + return filename; + } + + String basename = StringUtils.substringBeforeLast(filename, "."); + String extension = StringUtils.substringAfterLast(filename, "."); + + Pattern similarImagesPattern = Pattern.compile( + Pattern.quote(basename) + "_(\\d+)\\.?" + Pattern.quote(extension) + ); + int maxImageIdx = resourceInfoDao.findKeysByTenantIdAndResourceTypeAndResourceKeyStartingWith( + tenantId, ResourceType.IMAGE, basename + "_").stream() + .map(key -> RegexUtils.getMatch(key, similarImagesPattern, 1)) + .filter(Objects::nonNull).mapToInt(Integer::parseInt) + .max().orElse(0); + String uniqueKey = basename + "_" + (maxImageIdx + 1); + if (!extension.isEmpty()) { + uniqueKey += "." + extension; + } + return uniqueKey; + } + @Override public TbResourceInfo saveImageInfo(TbResourceInfo imageInfo) { return saveResource(new TbResource(imageInfo)); diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java index bb46af22ff..bb881e5dcd 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java @@ -67,11 +67,19 @@ public class BaseResourceService extends AbstractCachedEntityService { TbResourceInfo findByTenantIdAndKey(TenantId tenantId, ResourceType resourceType, String resourceKey); + boolean existsByTenantIdAndResourceTypeAndResourceKey(TenantId tenantId, ResourceType resourceType, String resourceKey); + + List findKeysByTenantIdAndResourceTypeAndResourceKeyStartingWith(TenantId tenantId, ResourceType resourceType, String resourceKeyQuery); + List findByTenantIdAndEtagAndKeyStartingWith(TenantId tenantId, String etag, String query); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/ResourceDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/ResourceDataValidator.java index affde231f5..37e3f3cb84 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/ResourceDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/ResourceDataValidator.java @@ -15,10 +15,10 @@ */ package org.thingsboard.server.dao.service.validator; +import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; @@ -95,6 +95,9 @@ public class ResourceDataValidator extends DataValidator { if (StringUtils.isEmpty(resource.getFileName())) { throw new DataValidationException("Resource file name should be specified!"); } + if (StringUtils.containsAny(resource.getFileName(), "/", "\\")) { + throw new DataValidationException("File name contains forbidden symbols"); + } if (StringUtils.isEmpty(resource.getResourceKey())) { throw new DataValidationException("Resource key should be specified!"); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceInfoDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceInfoDao.java index 62d02233e0..fc7a29af11 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceInfoDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceInfoDao.java @@ -82,6 +82,16 @@ public class JpaTbResourceInfoDao extends JpaAbstractDao findKeysByTenantIdAndResourceTypeAndResourceKeyStartingWith(TenantId tenantId, ResourceType resourceType, String resourceKeyQuery) { + return resourceInfoRepository.findKeysByTenantIdAndResourceTypeAndResourceKeyStartingWith(tenantId.getId(), resourceType.name(), resourceKeyQuery); + } + @Override public List findByTenantIdAndEtagAndKeyStartingWith(TenantId tenantId, String etag, String query) { return DaoUtil.convertDataList(resourceInfoRepository.findByTenantIdAndHashCodeAndResourceKeyStartingWith(tenantId.getId(), etag, query)); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceInfoRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceInfoRepository.java index e7881c7c74..9c526ae321 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceInfoRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceInfoRepository.java @@ -22,7 +22,6 @@ import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.thingsboard.server.dao.model.sql.TbResourceInfoEntity; -import java.util.Collection; import java.util.List; import java.util.UUID; @@ -55,6 +54,14 @@ public interface TbResourceInfoRepository extends JpaRepository findKeysByTenantIdAndResourceTypeAndResourceKeyStartingWith(@Param("tenantId") UUID tenantId, + @Param("resourceType") String resourceType, + @Param("resourceKeyStartsWith") String resourceKeyStartsWith); + List findByTenantIdAndHashCodeAndResourceKeyStartingWith(UUID tenantId, String hashCode, String query); } From 1bb01ef161dddb46e450c6af308d21f4ed9ab1b0 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Wed, 15 Nov 2023 16:41:06 +0200 Subject: [PATCH 139/252] UI: Fixed uncontroled focus on field --- .../shared/components/entity/entity-subtype-list.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/shared/components/entity/entity-subtype-list.component.ts b/ui-ngx/src/app/shared/components/entity/entity-subtype-list.component.ts index 6628d14439..89900fb065 100644 --- a/ui-ngx/src/app/shared/components/entity/entity-subtype-list.component.ts +++ b/ui-ngx/src/app/shared/components/entity/entity-subtype-list.component.ts @@ -265,8 +265,8 @@ export class EntitySubTypeListComponent implements ControlValueAccessor, OnInit, const value = (event.value || '').trim(); if (value) { this.add(value); + this.clear(''); } - this.clear(''); } remove(entitySubtype: string) { const index = this.entitySubtypeList.indexOf(entitySubtype); From ebd50555eff87a0b799d0c5b72a7f68e460ac8c0 Mon Sep 17 00:00:00 2001 From: imbeacon Date: Wed, 15 Nov 2023 17:29:47 +0200 Subject: [PATCH 140/252] Updated test --- .../org/thingsboard/server/controller/HomePageApiTest.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/HomePageApiTest.java b/application/src/test/java/org/thingsboard/server/controller/HomePageApiTest.java index c498b73815..148c4afea9 100644 --- a/application/src/test/java/org/thingsboard/server/controller/HomePageApiTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/HomePageApiTest.java @@ -92,6 +92,8 @@ public class HomePageApiTest extends AbstractControllerTest { @MockBean private SmsService smsService; + private static final int DEFAULT_DASHBOARDS_COUNT = 1; + //For system administrator @Test public void testTenantsCountWsCmd() throws Exception { @@ -408,7 +410,7 @@ public class HomePageApiTest extends AbstractControllerTest { Assert.assertEquals(2, usageInfo.getUsers()); Assert.assertEquals(configuration.getMaxUsers(), usageInfo.getMaxUsers()); - Assert.assertEquals(1, usageInfo.getDashboards()); + Assert.assertEquals(DEFAULT_DASHBOARDS_COUNT, usageInfo.getDashboards()); Assert.assertEquals(configuration.getMaxDashboards(), usageInfo.getMaxDashboards()); Assert.assertEquals(0, usageInfo.getTransportMessages()); @@ -478,7 +480,8 @@ public class HomePageApiTest extends AbstractControllerTest { } usageInfo = doGet("/api/usage", UsageInfo.class); - Assert.assertEquals(dashboards.size(), usageInfo.getDashboards()); + int expectedDashboardsCount = dashboards.size() + DEFAULT_DASHBOARDS_COUNT; + Assert.assertEquals(expectedDashboardsCount, usageInfo.getDashboards()); } private Long getInitialEntityCount(EntityType entityType) throws Exception { From a8aeaa80c101f1e5154502516f54d1bec8085019 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Wed, 15 Nov 2023 17:48:36 +0200 Subject: [PATCH 141/252] Update constructRuleChainUpdatedMsg --- .../service/edge/rpc/constructor/RuleChainMsgConstructor.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java index 7aac9d36bd..705bea0fae 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java @@ -37,6 +37,7 @@ import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; public class RuleChainMsgConstructor { public RuleChainUpdateMsg constructRuleChainUpdatedMsg(UpdateMsgType msgType, RuleChain ruleChain, boolean isRoot, EdgeVersion edgeVersion) { + boolean isTemplateRoot = ruleChain.isRoot(); if (EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)) { return constructDeprecatedRuleChainUpdatedMsg(msgType, ruleChain, isRoot); } @@ -44,7 +45,7 @@ public class RuleChainMsgConstructor { RuleChainUpdateMsg result = RuleChainUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(ruleChain)) .setIdMSB(ruleChain.getId().getId().getMostSignificantBits()) .setIdLSB(ruleChain.getId().getId().getLeastSignificantBits()).build(); - ruleChain.setRoot(false); + ruleChain.setRoot(isTemplateRoot); return result; } From 286c46f8c67cfe80ecd8daa254100532a7f93aa3 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Thu, 16 Nov 2023 12:59:05 +0200 Subject: [PATCH 142/252] Provide better naming for edgeVersion boolean version check. Minor refactoring --- .../rpc/constructor/AdminSettingsMsgConstructor.java | 7 ++++--- .../rpc/constructor/AssetProfileMsgConstructor.java | 1 - .../rpc/constructor/TenantProfileMsgConstructor.java | 4 ++-- .../rpc/constructor/WidgetsBundleMsgConstructor.java | 5 +++-- .../edge/rpc/processor/asset/AssetEdgeProcessor.java | 4 ++-- .../processor/asset/AssetProfileEdgeProcessor.java | 8 ++++---- .../edge/rpc/processor/asset/BaseAssetProcessor.java | 3 +-- .../processor/asset/BaseAssetProfileProcessor.java | 4 ++-- .../rpc/processor/device/BaseDeviceProcessor.java | 2 +- .../processor/device/BaseDeviceProfileProcessor.java | 12 ++++++------ .../rpc/processor/device/DeviceEdgeProcessor.java | 4 ++-- .../processor/device/DeviceProfileEdgeProcessor.java | 8 ++++---- .../entityview/BaseEntityViewProcessor.java | 2 +- .../entityview/EntityViewEdgeProcessor.java | 4 ++-- .../edge/rpc/processor/queue/QueueEdgeProcessor.java | 1 - .../processor/relation/BaseRelationProcessor.java | 6 ++---- .../processor/relation/RelationEdgeProcessor.java | 4 ++-- .../processor/resource/BaseResourceProcessor.java | 4 ++-- .../processor/telemetry/BaseTelemetryProcessor.java | 4 +--- .../edge/rpc/processor/user/UserEdgeProcessor.java | 1 - 20 files changed, 41 insertions(+), 47 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AdminSettingsMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AdminSettingsMsgConstructor.java index 26037e221e..e82f3553c3 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AdminSettingsMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AdminSettingsMsgConstructor.java @@ -18,6 +18,7 @@ package org.thingsboard.server.service.edge.rpc.constructor; import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.AdminSettings; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.gen.edge.v1.AdminSettingsUpdateMsg; import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.queue.util.TbCoreComponent; @@ -29,16 +30,16 @@ public class AdminSettingsMsgConstructor { public AdminSettingsUpdateMsg constructAdminSettingsUpdateMsg(AdminSettings adminSettings, EdgeVersion edgeVersion) { if (EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)) { - return constructDeprecatedWidgetTypeUpdateMsg(adminSettings); + return constructDeprecatedAdminSettingsUpdateMsg(adminSettings); } return AdminSettingsUpdateMsg.newBuilder().setEntity(JacksonUtil.toString(adminSettings)).build(); } - private AdminSettingsUpdateMsg constructDeprecatedWidgetTypeUpdateMsg(AdminSettings adminSettings) { + private AdminSettingsUpdateMsg constructDeprecatedAdminSettingsUpdateMsg(AdminSettings adminSettings) { AdminSettingsUpdateMsg.Builder builder = AdminSettingsUpdateMsg.newBuilder() .setKey(adminSettings.getKey()) .setJsonValue(JacksonUtil.toString(adminSettings.getJsonValue())); - if (adminSettings.getId() != null) { + if (TenantId.SYS_TENANT_ID.equals(adminSettings.getTenantId())) { builder.setIsSystem(true); } return builder.build(); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetProfileMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetProfileMsgConstructor.java index f58271917f..0c6247754c 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetProfileMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetProfileMsgConstructor.java @@ -74,5 +74,4 @@ public class AssetProfileMsgConstructor { .setIdMSB(assetProfileId.getId().getMostSignificantBits()) .setIdLSB(assetProfileId.getId().getLeastSignificantBits()).build(); } - } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/TenantProfileMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/TenantProfileMsgConstructor.java index a93c730ee7..6b47b6859d 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/TenantProfileMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/TenantProfileMsgConstructor.java @@ -35,9 +35,9 @@ public class TenantProfileMsgConstructor { private DataDecodingEncodingService dataDecodingEncodingService; public TenantProfileUpdateMsg constructTenantProfileUpdateMsg(UpdateMsgType msgType, TenantProfile tenantProfile, EdgeVersion edgeVersion) { - ByteString profileData = EdgeVersionUtils.isEdgeVersionOlderThan(edgeVersion, EdgeVersion.V_3_6_1) ? - ByteString.empty() : ByteString.copyFrom(dataDecodingEncodingService.encode(tenantProfile.getProfileData())); if (EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)) { + ByteString profileData = EdgeVersionUtils.isEdgeVersionOlderThan(edgeVersion, EdgeVersion.V_3_6_1) ? + ByteString.empty() : ByteString.copyFrom(dataDecodingEncodingService.encode(tenantProfile.getProfileData())); return constructDeprecatedTenantProfileUpdateMsg(msgType, tenantProfile, profileData); } return TenantProfileUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(tenantProfile)).build(); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetsBundleMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetsBundleMsgConstructor.java index 4d7e7be2f9..5093ddf554 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetsBundleMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetsBundleMsgConstructor.java @@ -38,8 +38,9 @@ public class WidgetsBundleMsgConstructor { if (EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)) { return constructDeprecatedWidgetsBundleUpdateMsg(msgType, widgetsBundle, widgets); } - return WidgetsBundleUpdateMsg.newBuilder().setWidgets(JacksonUtil.toString(widgets)).setEntity(JacksonUtil.toString(widgetsBundle)) - .setMsgType(msgType).setIdMSB(widgetsBundle.getId().getId().getMostSignificantBits()) + return WidgetsBundleUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(widgetsBundle)) + .setWidgets(JacksonUtil.toString(widgets)) + .setIdMSB(widgetsBundle.getId().getId().getMostSignificantBits()) .setIdLSB(widgetsBundle.getId().getId().getLeastSignificantBits()).build(); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessor.java index 2a140175ed..36a04dbb62 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessor.java @@ -148,8 +148,8 @@ public class AssetEdgeProcessor extends BaseAssetProcessor { } @Override - protected void setCustomerId(TenantId tenantId, CustomerId customerId, Asset asset, AssetUpdateMsg assetUpdateMsg, boolean isEdgeVersionDeprecated) { - CustomerId customerUUID = isEdgeVersionDeprecated + protected void setCustomerId(TenantId tenantId, CustomerId customerId, Asset asset, AssetUpdateMsg assetUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2) { + CustomerId customerUUID = isEdgeVersionOlderThan_3_6_2 ? safeGetCustomerId(assetUpdateMsg.getCustomerIdMSB(), assetUpdateMsg.getCustomerIdLSB()) : asset.getCustomerId() != null ? asset.getCustomerId() : customerId; asset.setCustomerId(customerUUID); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetProfileEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetProfileEdgeProcessor.java index 544cf46690..7f8efe1392 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetProfileEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetProfileEdgeProcessor.java @@ -134,16 +134,16 @@ public class AssetProfileEdgeProcessor extends BaseAssetProfileProcessor { } @Override - protected void setDefaultEdgeRuleChainId(AssetProfile assetProfile, RuleChainId ruleChainId, AssetProfileUpdateMsg assetProfileUpdateMsg, boolean isEdgeVersionDeprecated) { - UUID defaultEdgeRuleChainUUID = isEdgeVersionDeprecated + protected void setDefaultEdgeRuleChainId(AssetProfile assetProfile, RuleChainId ruleChainId, AssetProfileUpdateMsg assetProfileUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2) { + UUID defaultEdgeRuleChainUUID = isEdgeVersionOlderThan_3_6_2 ? safeGetUUID(assetProfileUpdateMsg.getDefaultRuleChainIdMSB(), assetProfileUpdateMsg.getDefaultRuleChainIdLSB()) : ruleChainId != null ? ruleChainId.getId() : null; assetProfile.setDefaultEdgeRuleChainId(defaultEdgeRuleChainUUID != null ? new RuleChainId(defaultEdgeRuleChainUUID) : null); } @Override - protected void setDefaultDashboardId(TenantId tenantId, DashboardId dashboardId, AssetProfile assetProfile, AssetProfileUpdateMsg assetProfileUpdateMsg, boolean isEdgeVersionDeprecated) { - UUID defaultDashboardUUID = isEdgeVersionDeprecated + protected void setDefaultDashboardId(TenantId tenantId, DashboardId dashboardId, AssetProfile assetProfile, AssetProfileUpdateMsg assetProfileUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2) { + UUID defaultDashboardUUID = isEdgeVersionOlderThan_3_6_2 ? safeGetUUID(assetProfileUpdateMsg.getDefaultDashboardIdMSB(), assetProfileUpdateMsg.getDefaultDashboardIdLSB()) : assetProfile.getDefaultDashboardId() != null ? assetProfile.getDefaultDashboardId().getId() : (dashboardId != null ? dashboardId.getId() : null); assetProfile.setDefaultDashboardId(defaultDashboardUUID != null ? new DashboardId(defaultDashboardUUID) : null); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProcessor.java index 916083a362..b5a1b48a1c 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProcessor.java @@ -94,6 +94,5 @@ public abstract class BaseAssetProcessor extends BaseEdgeProcessor { return asset; } - protected abstract void setCustomerId(TenantId tenantId, CustomerId customerId, Asset asset, AssetUpdateMsg assetUpdateMsg, boolean isEdgeVersionDeprecated); - + protected abstract void setCustomerId(TenantId tenantId, CustomerId customerId, Asset asset, AssetUpdateMsg assetUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProfileProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProfileProcessor.java index 4d106eeacb..bae0c8c068 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProfileProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProfileProcessor.java @@ -95,7 +95,7 @@ public abstract class BaseAssetProfileProcessor extends BaseEdgeProcessor { protected abstract void setDefaultRuleChainId(TenantId tenantId, AssetProfile assetProfile, RuleChainId ruleChainId); - protected abstract void setDefaultEdgeRuleChainId(AssetProfile assetProfile, RuleChainId ruleChainId, AssetProfileUpdateMsg assetProfileUpdateMsg, boolean isEdgeVersionDeprecated); + protected abstract void setDefaultEdgeRuleChainId(AssetProfile assetProfile, RuleChainId ruleChainId, AssetProfileUpdateMsg assetProfileUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2); - protected abstract void setDefaultDashboardId(TenantId tenantId, DashboardId dashboardId, AssetProfile assetProfile, AssetProfileUpdateMsg assetProfileUpdateMsg, boolean isEdgeVersionDeprecated); + protected abstract void setDefaultDashboardId(TenantId tenantId, DashboardId dashboardId, AssetProfile assetProfile, AssetProfileUpdateMsg assetProfileUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProcessor.java index 4b03c4d76b..27d4b31642 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProcessor.java @@ -159,5 +159,5 @@ public abstract class BaseDeviceProcessor extends BaseEdgeProcessor { return deviceCredentials; } - protected abstract void setCustomerId(TenantId tenantId, CustomerId customerId, Device device, DeviceUpdateMsg deviceUpdateMsg, boolean isEdgeVersionDeprecated); + protected abstract void setCustomerId(TenantId tenantId, CustomerId customerId, Device device, DeviceUpdateMsg deviceUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProfileProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProfileProcessor.java index 27ed9eb3c9..c9402730f3 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProfileProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProfileProcessor.java @@ -45,12 +45,12 @@ public abstract class BaseDeviceProfileProcessor extends BaseEdgeProcessor { @Autowired private DataDecodingEncodingService dataDecodingEncodingService; - protected Pair saveOrUpdateDeviceProfile(TenantId tenantId, DeviceProfileId deviceProfileId, DeviceProfileUpdateMsg deviceProfileUpdateMsg, boolean isEdgeVersionProtoDeprecated) { + protected Pair saveOrUpdateDeviceProfile(TenantId tenantId, DeviceProfileId deviceProfileId, DeviceProfileUpdateMsg deviceProfileUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2) { boolean created = false; boolean deviceProfileNameUpdated = false; deviceCreationLock.lock(); try { - DeviceProfile deviceProfile = isEdgeVersionProtoDeprecated + DeviceProfile deviceProfile = isEdgeVersionOlderThan_3_6_2 ? createDeviceProfile(tenantId, deviceProfileId, deviceProfileUpdateMsg) : JacksonUtil.fromStringIgnoreUnknownProperties(deviceProfileUpdateMsg.getEntity(), DeviceProfile.class); if (deviceProfile == null) { @@ -75,8 +75,8 @@ public abstract class BaseDeviceProfileProcessor extends BaseEdgeProcessor { RuleChainId ruleChainId = deviceProfile.getDefaultRuleChainId(); setDefaultRuleChainId(tenantId, deviceProfile, created ? null : deviceProfileById.getDefaultRuleChainId()); - setDefaultEdgeRuleChainId(deviceProfile, ruleChainId, deviceProfileUpdateMsg, isEdgeVersionProtoDeprecated); - setDefaultDashboardId(tenantId, created ? null : deviceProfileById.getDefaultDashboardId(), deviceProfile, deviceProfileUpdateMsg, isEdgeVersionProtoDeprecated); + setDefaultEdgeRuleChainId(deviceProfile, ruleChainId, deviceProfileUpdateMsg, isEdgeVersionOlderThan_3_6_2); + setDefaultDashboardId(tenantId, created ? null : deviceProfileById.getDefaultDashboardId(), deviceProfile, deviceProfileUpdateMsg, isEdgeVersionOlderThan_3_6_2); deviceProfileValidator.validate(deviceProfile, DeviceProfile::getTenantId); if (created) { @@ -127,7 +127,7 @@ public abstract class BaseDeviceProfileProcessor extends BaseEdgeProcessor { protected abstract void setDefaultRuleChainId(TenantId tenantId, DeviceProfile deviceProfile, RuleChainId ruleChainId); - protected abstract void setDefaultEdgeRuleChainId(DeviceProfile deviceProfile, RuleChainId ruleChainId, DeviceProfileUpdateMsg deviceProfileUpdateMsg, boolean isEdgeVersionDeprecated); + protected abstract void setDefaultEdgeRuleChainId(DeviceProfile deviceProfile, RuleChainId ruleChainId, DeviceProfileUpdateMsg deviceProfileUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2); - protected abstract void setDefaultDashboardId(TenantId tenantId, DashboardId dashboardId, DeviceProfile deviceProfile, DeviceProfileUpdateMsg deviceProfileUpdateMsg, boolean isEdgeVersionDeprecated); + protected abstract void setDefaultDashboardId(TenantId tenantId, DashboardId dashboardId, DeviceProfile deviceProfile, DeviceProfileUpdateMsg deviceProfileUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java index 20cb591320..257cee336f 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java @@ -289,8 +289,8 @@ public class DeviceEdgeProcessor extends BaseDeviceProcessor { } @Override - protected void setCustomerId(TenantId tenantId, CustomerId customerId, Device device, DeviceUpdateMsg deviceUpdateMsg, boolean isEdgeVersionDeprecated) { - CustomerId customerUUID = isEdgeVersionDeprecated + protected void setCustomerId(TenantId tenantId, CustomerId customerId, Device device, DeviceUpdateMsg deviceUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2) { + CustomerId customerUUID = isEdgeVersionOlderThan_3_6_2 ? safeGetCustomerId(deviceUpdateMsg.getCustomerIdMSB(), deviceUpdateMsg.getCustomerIdLSB()) : device.getCustomerId() != null ? device.getCustomerId() : customerId; device.setCustomerId(customerUUID); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceProfileEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceProfileEdgeProcessor.java index 3814992491..7e265e6907 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceProfileEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceProfileEdgeProcessor.java @@ -134,16 +134,16 @@ public class DeviceProfileEdgeProcessor extends BaseDeviceProfileProcessor { } @Override - protected void setDefaultEdgeRuleChainId(DeviceProfile deviceProfile, RuleChainId ruleChainId, DeviceProfileUpdateMsg deviceProfileUpdateMsg, boolean isEdgeVersionDeprecated) { - UUID defaultEdgeRuleChainUUID = isEdgeVersionDeprecated + protected void setDefaultEdgeRuleChainId(DeviceProfile deviceProfile, RuleChainId ruleChainId, DeviceProfileUpdateMsg deviceProfileUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2) { + UUID defaultEdgeRuleChainUUID = isEdgeVersionOlderThan_3_6_2 ? safeGetUUID(deviceProfileUpdateMsg.getDefaultRuleChainIdMSB(), deviceProfileUpdateMsg.getDefaultRuleChainIdLSB()) : ruleChainId != null ? ruleChainId.getId() : null; deviceProfile.setDefaultEdgeRuleChainId(defaultEdgeRuleChainUUID != null ? new RuleChainId(defaultEdgeRuleChainUUID) : null); } @Override - protected void setDefaultDashboardId(TenantId tenantId, DashboardId dashboardId, DeviceProfile deviceProfile, DeviceProfileUpdateMsg deviceProfileUpdateMsg, boolean isEdgeVersionDeprecated) { - UUID defaultDashboardUUID = isEdgeVersionDeprecated + protected void setDefaultDashboardId(TenantId tenantId, DashboardId dashboardId, DeviceProfile deviceProfile, DeviceProfileUpdateMsg deviceProfileUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2) { + UUID defaultDashboardUUID = isEdgeVersionOlderThan_3_6_2 ? safeGetUUID(deviceProfileUpdateMsg.getDefaultDashboardIdMSB(), deviceProfileUpdateMsg.getDefaultDashboardIdLSB()) : deviceProfile.getDefaultDashboardId() != null ? deviceProfile.getDefaultDashboardId().getId() : (dashboardId != null ? dashboardId.getId() : null); deviceProfile.setDefaultDashboardId(defaultDashboardUUID != null ? new DashboardId(defaultDashboardUUID) : null); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/BaseEntityViewProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/BaseEntityViewProcessor.java index 02bc2b9529..7ffa4874fd 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/BaseEntityViewProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/BaseEntityViewProcessor.java @@ -92,5 +92,5 @@ public abstract class BaseEntityViewProcessor extends BaseEdgeProcessor { return entityView; } - protected abstract void setCustomerId(TenantId tenantId, CustomerId customerId, EntityView entityView, EntityViewUpdateMsg entityViewUpdateMsg, boolean isEdgeVersionDeprecated); + protected abstract void setCustomerId(TenantId tenantId, CustomerId customerId, EntityView entityView, EntityViewUpdateMsg entityViewUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewEdgeProcessor.java index 7290f731ff..50c5755661 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewEdgeProcessor.java @@ -140,8 +140,8 @@ public class EntityViewEdgeProcessor extends BaseEntityViewProcessor { } @Override - protected void setCustomerId(TenantId tenantId, CustomerId customerId, EntityView entityView, EntityViewUpdateMsg entityViewUpdateMsg, boolean isEdgeVersionDeprecated) { - CustomerId customerUUID = isEdgeVersionDeprecated + protected void setCustomerId(TenantId tenantId, CustomerId customerId, EntityView entityView, EntityViewUpdateMsg entityViewUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2) { + CustomerId customerUUID = isEdgeVersionOlderThan_3_6_2 ? safeGetCustomerId(entityViewUpdateMsg.getCustomerIdMSB(), entityViewUpdateMsg.getCustomerIdLSB()) : entityView.getCustomerId() != null ? entityView.getCustomerId() : customerId; entityView.setCustomerId(customerUUID); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/queue/QueueEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/queue/QueueEdgeProcessor.java index 0df98dbcac..ce7bd33f9b 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/queue/QueueEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/queue/QueueEdgeProcessor.java @@ -61,5 +61,4 @@ public class QueueEdgeProcessor extends BaseEdgeProcessor { } return downlinkMsg; } - } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/BaseRelationProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/BaseRelationProcessor.java index d673b1a613..d5987c29cd 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/BaseRelationProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/BaseRelationProcessor.java @@ -25,20 +25,18 @@ import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.RelationTypeGroup; -import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; -import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; import java.util.UUID; @Slf4j public abstract class BaseRelationProcessor extends BaseEdgeProcessor { - protected ListenableFuture processRelationMsg(TenantId tenantId, RelationUpdateMsg relationUpdateMsg, EdgeVersion edgeVersion) { + protected ListenableFuture processRelationMsg(TenantId tenantId, RelationUpdateMsg relationUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2) { log.trace("[{}] processRelationMsg [{}]", tenantId, relationUpdateMsg); try { - EntityRelation entityRelation = EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion) + EntityRelation entityRelation = isEdgeVersionOlderThan_3_6_2 ? createEntityRelation(relationUpdateMsg) : JacksonUtil.fromStringIgnoreUnknownProperties(relationUpdateMsg.getEntity(), EntityRelation.class); if (entityRelation == null) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessor.java index 8d75be10ee..8c6854ff3d 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessor.java @@ -35,6 +35,7 @@ import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; import java.util.ArrayList; import java.util.HashSet; @@ -50,8 +51,7 @@ public class RelationEdgeProcessor extends BaseRelationProcessor { log.trace("[{}] executing processRelationMsgFromEdge [{}] from edge [{}]", tenantId, relationUpdateMsg, edge.getId()); try { edgeSynchronizationManager.getEdgeId().set(edge.getId()); - - return processRelationMsg(tenantId, relationUpdateMsg, edgeVersion); + return processRelationMsg(tenantId, relationUpdateMsg, EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)); } finally { edgeSynchronizationManager.getEdgeId().remove(); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/BaseResourceProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/BaseResourceProcessor.java index 20a8aa372b..5ed377fed2 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/BaseResourceProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/BaseResourceProcessor.java @@ -31,10 +31,10 @@ import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; @Slf4j public abstract class BaseResourceProcessor extends BaseEdgeProcessor { - protected boolean saveOrUpdateTbResource(TenantId tenantId, TbResourceId tbResourceId, ResourceUpdateMsg resourceUpdateMsg, boolean isEdgeDeprecated) { + protected boolean saveOrUpdateTbResource(TenantId tenantId, TbResourceId tbResourceId, ResourceUpdateMsg resourceUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2) { boolean resourceKeyUpdated = false; try { - TbResource resource = isEdgeDeprecated + TbResource resource = isEdgeVersionOlderThan_3_6_2 ? createTbResource(tenantId, resourceUpdateMsg) : JacksonUtil.fromStringIgnoreUnknownProperties(resourceUpdateMsg.getEntity(), TbResource.class); if (resource == null) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/BaseTelemetryProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/BaseTelemetryProcessor.java index 2eaa69af82..186f030a79 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/BaseTelemetryProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/BaseTelemetryProcessor.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.service.edge.rpc.processor.telemetry; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; @@ -28,7 +27,6 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.msg.rule.engine.DeviceAttributesEventNotificationMsg; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; @@ -54,6 +52,7 @@ import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; +import org.thingsboard.server.common.msg.rule.engine.DeviceAttributesEventNotificationMsg; import org.thingsboard.server.common.transport.adaptor.JsonConverter; import org.thingsboard.server.common.transport.util.JsonUtils; import org.thingsboard.server.dao.model.ModelConstants; @@ -349,5 +348,4 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor { return bodyJackson == null ? null : entityDataMsgConstructor.constructEntityDataMsg(tenantId, entityId, actionType, JsonParser.parseString(bodyJackson)); } - } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/user/UserEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/user/UserEdgeProcessor.java index 3a3b00f4c8..8d27c5fc79 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/user/UserEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/user/UserEdgeProcessor.java @@ -69,5 +69,4 @@ public class UserEdgeProcessor extends BaseEdgeProcessor { } return downlinkMsg; } - } From 6eadef0d68cdbbf0ab41c5d93cd8b317c0d7efb8 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Thu, 16 Nov 2023 13:15:32 +0200 Subject: [PATCH 143/252] Use JsonGetter/Setter to encode resource data; minor fixes --- .../controller/TbResourceController.java | 8 +--- .../constructor/ResourceMsgConstructor.java | 4 +- .../resource/BaseResourceProcessor.java | 2 +- .../controller/TbResourceControllerTest.java | 45 ++++++++++--------- .../server/edge/ResourceEdgeTest.java | 4 +- .../sql/BaseTbResourceServiceTest.java | 33 +++++++------- .../lwm2m/AbstractLwM2MIntegrationTest.java | 2 +- .../server/common/data/ResourceType.java | 2 +- .../server/common/data/TbResource.java | 45 ++++++++++++------- .../sql/resource/JpaTbResourceInfoDao.java | 16 +++++-- .../resource/resources-library.component.html | 2 +- .../resource/resources-library.component.ts | 12 ++--- .../src/app/shared/models/resource.models.ts | 2 +- 13 files changed, 97 insertions(+), 80 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java index 08d7cedd93..b8abaab0ae 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java @@ -43,7 +43,6 @@ import org.thingsboard.server.common.data.TbResourceInfo; import org.thingsboard.server.common.data.TbResourceInfoFilter; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.TbResourceId; -import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.lwm2m.LwM2mObject; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; @@ -53,7 +52,6 @@ import org.thingsboard.server.service.resource.TbResourceService; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; -import java.util.Base64; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -170,9 +168,7 @@ public class TbResourceController extends BaseController { @PathVariable(RESOURCE_ID) String strResourceId) throws ThingsboardException { checkParameter(RESOURCE_ID, strResourceId); TbResourceId resourceId = new TbResourceId(toUUID(strResourceId)); - TbResource resource = checkResourceId(resourceId, Operation.READ); - resource.setBase64Data(Base64.getEncoder().encodeToString(resource.getData())); - return resource; + return checkResourceId(resourceId, Operation.READ); } @ApiOperation(value = "Create Or Update Resource (saveResource)", @@ -191,7 +187,7 @@ public class TbResourceController extends BaseController { @RequestBody TbResource resource) throws Exception { resource.setTenantId(getTenantId()); checkEntity(resource.getId(), resource, Resource.TB_RESOURCE); - return tbResourceService.save(resource, getCurrentUser()); + return new TbResourceInfo(tbResourceService.save(resource, getCurrentUser())); } @ApiOperation(value = "Get Resource Infos (getResources)", diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ResourceMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ResourceMsgConstructor.java index 14da03c37d..02f2d5e000 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ResourceMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ResourceMsgConstructor.java @@ -23,8 +23,6 @@ import org.thingsboard.server.gen.edge.v1.ResourceUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; -import java.util.Base64; - @Component @TbCoreComponent public class ResourceMsgConstructor { @@ -39,7 +37,7 @@ public class ResourceMsgConstructor { .setResourceType(tbResource.getResourceType().name()) .setFileName(tbResource.getFileName()); if (tbResource.getData() != null) { - builder.setData(Base64.getEncoder().encodeToString(tbResource.getData())); + builder.setData(tbResource.getEncodedData()); } if (tbResource.getEtag() != null) { builder.setEtag(tbResource.getEtag()); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/BaseResourceProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/BaseResourceProcessor.java index 88d382a6ed..892f7535c0 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/BaseResourceProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/BaseResourceProcessor.java @@ -46,7 +46,7 @@ public abstract class BaseResourceProcessor extends BaseEdgeProcessor { resource.setResourceKey(resourceUpdateMsg.getResourceKey()); resource.setResourceType(ResourceType.valueOf(resourceUpdateMsg.getResourceType())); resource.setFileName(resourceUpdateMsg.getFileName()); - resource.setBase64Data(resourceUpdateMsg.hasData() ? resourceUpdateMsg.getData() : null); + resource.setEncodedData(resourceUpdateMsg.hasData() ? resourceUpdateMsg.getData() : null); resource.setEtag(resourceUpdateMsg.hasEtag() ? resourceUpdateMsg.getEtag() : null); resourceValidator.validate(resource, TbResourceInfo::getTenantId); if (created) { diff --git a/application/src/test/java/org/thingsboard/server/controller/TbResourceControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/TbResourceControllerTest.java index 05770f960f..94c69a9722 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TbResourceControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/TbResourceControllerTest.java @@ -101,7 +101,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JKS); resource.setTitle("My first resource"); resource.setFileName(DEFAULT_FILE_NAME); - resource.setBase64Data(TEST_DATA); + resource.setEncodedData(TEST_DATA); TbResource savedResource = save(resource); @@ -119,6 +119,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { Assert.assertArrayEquals(resource.getData(), download(savedResource.getId())); savedResource.setTitle("My new resource"); + savedResource.setData(null); save(savedResource); @@ -136,7 +137,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JKS); resource.setTitle(StringUtils.randomAlphabetic(300)); resource.setFileName(DEFAULT_FILE_NAME); - resource.setBase64Data(TEST_DATA); + resource.setEncodedData(TEST_DATA); Mockito.reset(tbClusterService, auditLogService); @@ -155,7 +156,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JKS); resource.setTitle("My first resource"); resource.setFileName(DEFAULT_FILE_NAME); - resource.setBase64Data(TEST_DATA); + resource.setEncodedData(TEST_DATA); TbResource savedResource = save(resource); @@ -184,7 +185,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JKS); resource.setTitle("My first resource"); resource.setFileName(DEFAULT_FILE_NAME); - resource.setBase64Data(TEST_DATA); + resource.setEncodedData(TEST_DATA); TbResource savedResource = save(resource); @@ -200,7 +201,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JKS); resource.setTitle("My first resource"); resource.setFileName(DEFAULT_FILE_NAME); - resource.setBase64Data(TEST_DATA); + resource.setEncodedData(TEST_DATA); TbResource savedResource = save(resource); @@ -225,7 +226,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JKS); resource.setTitle("My first resource"); resource.setFileName(DEFAULT_FILE_NAME); - resource.setBase64Data(TEST_DATA); + resource.setEncodedData(TEST_DATA); TbResource savedResource = save(resource); @@ -256,7 +257,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setTitle("Resource" + i); resource.setResourceType(ResourceType.JKS); resource.setFileName(i + DEFAULT_FILE_NAME); - resource.setBase64Data(TEST_DATA); + resource.setEncodedData(TEST_DATA); resources.add(new TbResourceInfo(save(resource))); } List loadedResources = new ArrayList<>(); @@ -293,7 +294,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setTitle("JKS Resource" + i); resource.setResourceType(ResourceType.JKS); resource.setFileName(i + DEFAULT_FILE_NAME); - resource.setBase64Data(TEST_DATA); + resource.setEncodedData(TEST_DATA); resources.add(new TbResourceInfo(save(resource))); } @@ -303,7 +304,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setTitle("LWM2M Resource" + i); resource.setResourceType(ResourceType.PKCS_12); resource.setFileName(i + DEFAULT_FILE_NAME_2); - resource.setBase64Data(TEST_DATA); + resource.setEncodedData(TEST_DATA); save(resource); } @@ -340,7 +341,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setTitle("Resource" + i); resource.setResourceType(ResourceType.JKS); resource.setFileName(i + DEFAULT_FILE_NAME); - resource.setBase64Data(TEST_DATA); + resource.setEncodedData(TEST_DATA); resources.add(new TbResourceInfo(save(resource))); } List loadedResources = new ArrayList<>(); @@ -400,7 +401,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setTitle("JKS Resource" + i); resource.setResourceType(ResourceType.JKS); resource.setFileName(i + DEFAULT_FILE_NAME); - resource.setBase64Data(TEST_DATA); + resource.setEncodedData(TEST_DATA); TbResourceInfo saved = new TbResourceInfo(save(resource)); jksResources.add(saved); } @@ -411,7 +412,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setTitle("LWM2M Resource" + i); resource.setResourceType(ResourceType.PKCS_12); resource.setFileName(i + DEFAULT_FILE_NAME_2); - resource.setBase64Data(TEST_DATA); + resource.setEncodedData(TEST_DATA); TbResource saved = save(resource); lwm2mesources.add(saved); } @@ -477,7 +478,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setTitle("Resource" + i); resource.setResourceType(ResourceType.JKS); resource.setFileName(i + DEFAULT_FILE_NAME); - resource.setBase64Data(TEST_DATA); + resource.setEncodedData(TEST_DATA); expectedResources.add(new TbResourceInfo(save(resource))); } @@ -488,7 +489,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setTitle("Resource" + i); resource.setResourceType(ResourceType.JKS); resource.setFileName(i + DEFAULT_FILE_NAME); - resource.setBase64Data(TEST_DATA); + resource.setEncodedData(TEST_DATA); TbResourceInfo savedResource = new TbResourceInfo(save(resource)); systemResources.add(savedResource); if (i >= 73) { @@ -532,7 +533,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JS_MODULE); resource.setTitle("Js resource"); resource.setFileName(JS_TEST_FILE_NAME); - resource.setBase64Data(TEST_DATA); + resource.setEncodedData(TEST_DATA); TbResource savedResource = save(resource); @@ -563,7 +564,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JS_MODULE); resource.setTitle("Js resource"); resource.setFileName(JS_TEST_FILE_NAME); - resource.setBase64Data(TEST_DATA); + resource.setEncodedData(TEST_DATA); TbResource savedResource = save(resource); @@ -607,7 +608,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JS_MODULE); resource.setTitle("Js resource"); resource.setFileName(JS_TEST_FILE_NAME); - resource.setBase64Data(TEST_DATA); + resource.setEncodedData(TEST_DATA); TbResource savedResource = save(resource); @@ -623,16 +624,16 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setResourceType(ResourceType.PKCS_12); resource.setTitle("My resource"); resource.setFileName("3.pks"); - resource.setBase64Data(TEST_DATA); + resource.setEncodedData(TEST_DATA); TbResource savedResource = save(resource); resource.setEtag(savedResource.getEtag()); - savedResource.setBase64Data(TEST_DATA); + savedResource.setEncodedData(TEST_DATA); doPost("/api/resource", savedResource) .andExpect(status().isBadRequest()) .andExpect(statusReason(containsString("can't be updated"))); - savedResource.setBase64Data(null); + savedResource.setData(null); savedResource.setTitle("Updated resource"); savedResource = doPost("/api/resource", savedResource, TbResource.class); assertThat(savedResource.getTitle()).isEqualTo("Updated resource"); @@ -647,12 +648,12 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JS_MODULE); resource.setTitle("My resource"); resource.setFileName("module.js"); - resource.setBase64Data(TEST_DATA); + resource.setEncodedData(TEST_DATA); TbResource savedResource = save(resource); resource.setEtag(savedResource.getEtag()); String newData = Base64.getEncoder().encodeToString(new byte[]{1, 2, 3}); - savedResource.setBase64Data(newData); + savedResource.setEncodedData(newData); savedResource.setFileName("new-module.js"); savedResource.setTitle("Updated title"); savedResource = save(savedResource); diff --git a/application/src/test/java/org/thingsboard/server/edge/ResourceEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/ResourceEdgeTest.java index 7a0564debf..61f3ef49aa 100644 --- a/application/src/test/java/org/thingsboard/server/edge/ResourceEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/ResourceEdgeTest.java @@ -45,7 +45,7 @@ public class ResourceEdgeTest extends AbstractEdgeTest { resource.setResourceType(ResourceType.JKS); resource.setTitle("Edge Test Resource"); resource.setFileName(FILE_NAME); - resource.setBase64Data(TEST_DATA); + resource.setEncodedData(TEST_DATA); edgeImitator.expectMessageAmount(1); TbResource savedResource = doPost("/api/resource", resource, TbResource.class); @@ -119,6 +119,6 @@ public class ResourceEdgeTest extends AbstractEdgeTest { TbResource tbResource = doGet("/api/resource/" + uuid, TbResource.class); Assert.assertNotNull(tbResource); Assert.assertEquals("Edge Test Resource", tbResource.getName()); - Assert.assertEquals(TEST_DATA, tbResource.getBase64Data()); + Assert.assertEquals(TEST_DATA, tbResource.getEncodedData()); } } diff --git a/application/src/test/java/org/thingsboard/server/service/resource/sql/BaseTbResourceServiceTest.java b/application/src/test/java/org/thingsboard/server/service/resource/sql/BaseTbResourceServiceTest.java index e441a0b144..278420589f 100644 --- a/application/src/test/java/org/thingsboard/server/service/resource/sql/BaseTbResourceServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/resource/sql/BaseTbResourceServiceTest.java @@ -198,7 +198,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { for (int i = 2; i < 4; i++) { createResource("test" + i, i + DEFAULT_FILE_NAME); - assertEquals(i*4, resourceService.sumDataSizeByTenantId(tenantId)); + assertEquals(i * 4, resourceService.sumDataSizeByTenantId(tenantId)); } } @@ -209,7 +209,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JKS); resource.setFileName(filename); byte[] b = new byte[]{1, 2, 3, 4}; - resource.setBase64Data(Base64.getEncoder().encodeToString(b)); + resource.setData(b); return tbResourceService.save(resource); } @@ -220,7 +220,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JKS); resource.setTitle("My first resource"); resource.setFileName(DEFAULT_FILE_NAME); - resource.setBase64Data(TEST_BASE64_DATA); + resource.setData(TEST_DATA); TbResource savedResource = tbResourceService.save(resource); @@ -233,7 +233,6 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { assertArrayEquals(TEST_DATA, savedResource.getData()); savedResource.setTitle("My new resource"); - savedResource.setBase64Data(null); savedResource.setData(null); tbResourceService.save(savedResource); @@ -250,7 +249,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setTenantId(tenantId); resource.setResourceType(ResourceType.LWM2M_MODEL); resource.setFileName("test_model.xml"); - resource.setBase64Data(Base64.getEncoder().encodeToString(LWM2M_TEST_MODEL.getBytes())); + resource.setEncodedData(Base64.getEncoder().encodeToString(LWM2M_TEST_MODEL.getBytes())); TbResource savedResource = tbResourceService.save(resource); @@ -271,7 +270,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JKS); resource.setTitle("My resource"); resource.setFileName(DEFAULT_FILE_NAME); - resource.setBase64Data(TEST_BASE64_DATA); + resource.setData(TEST_DATA); TbResource savedResource = tbResourceService.save(resource); assertEquals(TenantId.SYS_TENANT_ID, savedResource.getTenantId()); @@ -286,7 +285,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JKS); resource.setTitle("My resource"); resource.setFileName(DEFAULT_FILE_NAME); - resource.setBase64Data(TEST_BASE64_DATA); + resource.setData(TEST_DATA); TbResource savedResource = tbResourceService.save(resource); @@ -295,7 +294,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JKS); resource.setTitle("My resource"); resource.setFileName(DEFAULT_FILE_NAME); - resource.setBase64Data(TEST_BASE64_DATA); + resource.setData(TEST_DATA); try { Assertions.assertThrows(DataValidationException.class, () -> { @@ -312,7 +311,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setTenantId(tenantId); resource.setResourceType(ResourceType.JKS); resource.setFileName(DEFAULT_FILE_NAME); - resource.setBase64Data(TEST_BASE64_DATA); + resource.setData(TEST_DATA); Assertions.assertThrows(DataValidationException.class, () -> { tbResourceService.save(resource); }); @@ -325,7 +324,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JKS); resource.setTitle("My resource"); resource.setFileName(DEFAULT_FILE_NAME); - resource.setBase64Data(TEST_BASE64_DATA); + resource.setData(TEST_DATA); Assertions.assertThrows(DataValidationException.class, () -> { tbResourceService.save(resource); }); @@ -352,7 +351,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JKS); resource.setTitle("My resource"); resource.setFileName(DEFAULT_FILE_NAME); - resource.setBase64Data(TEST_BASE64_DATA); + resource.setData(TEST_DATA); TbResource savedResource = tbResourceService.save(resource); TbResource foundResource = resourceService.findResourceById(tenantId, savedResource.getId()); @@ -368,7 +367,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setTenantId(tenantId); resource.setTitle("My resource"); resource.setFileName(DEFAULT_FILE_NAME); - resource.setBase64Data(TEST_BASE64_DATA); + resource.setData(TEST_DATA); TbResource savedResource = tbResourceService.save(resource); TbResource foundResource = resourceService.findResourceByTenantIdAndKey(tenantId, savedResource.getResourceType(), savedResource.getResourceKey()); @@ -383,7 +382,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JKS); resource.setTitle("My resource"); resource.setFileName(DEFAULT_FILE_NAME); - resource.setBase64Data(TEST_BASE64_DATA); + resource.setData(TEST_DATA); TbResource savedResource = tbResourceService.save(resource); TbResource foundResource = resourceService.findResourceById(tenantId, savedResource.getId()); @@ -409,7 +408,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setTitle("Resource" + i); resource.setResourceType(ResourceType.JKS); resource.setFileName(i + DEFAULT_FILE_NAME); - resource.setBase64Data(TEST_BASE64_DATA); + resource.setData(TEST_DATA); resources.add(new TbResourceInfo(tbResourceService.save(resource))); } @@ -463,7 +462,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setTitle("System Resource" + i); resource.setResourceType(ResourceType.JKS); resource.setFileName(i + DEFAULT_FILE_NAME); - resource.setBase64Data(TEST_BASE64_DATA); + resource.setData(TEST_DATA); TbResourceInfo tbResourceInfo = new TbResourceInfo(tbResourceService.save(resource)); if (i >= 50) { resources.add(tbResourceInfo); @@ -476,7 +475,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setTitle("Tenant Resource" + i); resource.setResourceType(ResourceType.JKS); resource.setFileName(i + DEFAULT_FILE_NAME); - resource.setBase64Data(TEST_BASE64_DATA); + resource.setData(TEST_DATA); resources.add(new TbResourceInfo(tbResourceService.save(resource))); } @@ -487,7 +486,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { TbResourceInfoFilter filter = TbResourceInfoFilter.builder() .tenantId(tenantId) .build(); - pageData = resourceService.findAllTenantResourcesByTenantId(filter, pageLink); + pageData = resourceService.findAllTenantResourcesByTenantId(filter, pageLink); loadedResources.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java index e3b0d0d9a2..00c6ba6a09 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java @@ -206,7 +206,7 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte lwModel.setFileName(resourceName); lwModel.setTenantId(tenantId); byte[] bytes = IOUtils.toByteArray(AbstractLwM2MIntegrationTest.class.getClassLoader().getResourceAsStream("lwm2m/" + resourceName)); - lwModel.setBase64Data(Base64.getEncoder().encodeToString(bytes)); + lwModel.setData(bytes); lwModel = doPostWithTypedResponse("/api/resource", lwModel, new TypeReference<>() { }); Assert.assertNotNull(lwModel); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/ResourceType.java b/common/data/src/main/java/org/thingsboard/server/common/data/ResourceType.java index b3222a9682..22af597627 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/ResourceType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ResourceType.java @@ -24,7 +24,7 @@ public enum ResourceType { JKS("application/x-java-keystore", false, false), PKCS_12("application/x-pkcs12", false, false), JS_MODULE("application/javascript", true, true), - IMAGE(null, true, false); + IMAGE(null, true, true); @Getter private final String mediaType; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/TbResource.java b/common/data/src/main/java/org/thingsboard/server/common/data/TbResource.java index 3d6c9ad95b..c76d2b5c2d 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/TbResource.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/TbResource.java @@ -15,7 +15,9 @@ */ package org.thingsboard.server.common.data; +import com.fasterxml.jackson.annotation.JsonGetter; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonSetter; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; @@ -23,6 +25,7 @@ import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.id.TbResourceId; import java.util.Base64; +import java.util.Optional; @Slf4j @Data @@ -31,9 +34,6 @@ public class TbResource extends TbResourceInfo { private static final long serialVersionUID = 7379609705527272306L; - @ApiModelProperty(position = 12, value = "Resource data.", example = "77u/PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCEtLQpGSUxFIElORk9STUFUSU9OCgpPTUEgUGVybWFuZW50IERvY3VtZW50CiAgIEZpbGU6IE9NQS1TVVAtTHdNMk1fQmluYXJ5QXBwRGF0YUNvbnRhaW5lci1WMV8wXzEtMjAxOTAyMjEtQQogICBUeXBlOiB4bWwKClB1YmxpYyBSZWFjaGFibGUgSW5mb3JtYXRpb24KICAgUGF0aDogaHR0cDovL3d3dy5vcGVubW9iaWxlYWxsaWFuY2Uub3JnL3RlY2gvcHJvZmlsZXMKICAgTmFtZTogTHdNMk1fQmluYXJ5QXBwRGF0YUNvbnRhaW5lci12MV8wXzEueG1sCgpOT1JNQVRJVkUgSU5GT1JNQVRJT04KCiAgSW5mb3JtYXRpb24gYWJvdXQgdGhpcyBmaWxlIGNhbiBiZSBmb3VuZCBpbiB0aGUgbGF0ZXN0IHJldmlzaW9uIG9mCgogIE9NQS1UUy1MV00yTV9CaW5hcnlBcHBEYXRhQ29udGFpbmVyLVYxXzBfMQoKICBUaGlzIGlzIGF2YWlsYWJsZSBhdCBodHRwOi8vd3d3Lm9wZW5tb2JpbGVhbGxpYW5jZS5vcmcvCgogIFNlbmQgY29tbWVudHMgdG8gaHR0cHM6Ly9naXRodWIuY29tL09wZW5Nb2JpbGVBbGxpYW5jZS9PTUFfTHdNMk1fZm9yX0RldmVsb3BlcnMvaXNzdWVzCgpDSEFOR0UgSElTVE9SWQoKMTUwNjIwMTggU3RhdHVzIGNoYW5nZWQgdG8gQXBwcm92ZWQgYnkgRE0sIERvYyBSZWYgIyBPTUEtRE0mU0UtMjAxOC0wMDYxLUlOUF9MV00yTV9BUFBEQVRBX1YxXzBfRVJQX2Zvcl9maW5hbF9BcHByb3ZhbAoyMTAyMjAxOSBTdGF0dXMgY2hhbmdlZCB0byBBcHByb3ZlZCBieSBJUFNPLCBEb2MgUmVmICMgT01BLUlQU08tMjAxOS0wMDI1LUlOUF9Md00yTV9PYmplY3RfQXBwX0RhdGFfQ29udGFpbmVyXzFfMF8xX2Zvcl9GaW5hbF9BcHByb3ZhbAoKTEVHQUwgRElTQ0xBSU1FUgoKQ29weXJpZ2h0IDIwMTkgT3BlbiBNb2JpbGUgQWxsaWFuY2UuCgpSZWRpc3RyaWJ1dGlvbiBhbmQgdXNlIGluIHNvdXJjZSBhbmQgYmluYXJ5IGZvcm1zLCB3aXRoIG9yIHdpdGhvdXQKbW9kaWZpY2F0aW9uLCBhcmUgcGVybWl0dGVkIHByb3ZpZGVkIHRoYXQgdGhlIGZvbGxvd2luZyBjb25kaXRpb25zCmFyZSBtZXQ6CgoxLiBSZWRpc3RyaWJ1dGlvbnMgb2Ygc291cmNlIGNvZGUgbXVzdCByZXRhaW4gdGhlIGFib3ZlIGNvcHlyaWdodApub3RpY2UsIHRoaXMgbGlzdCBvZiBjb25kaXRpb25zIGFuZCB0aGUgZm9sbG93aW5nIGRpc2NsYWltZXIuCjIuIFJlZGlzdHJpYnV0aW9ucyBpbiBiaW5hcnkgZm9ybSBtdXN0IHJlcHJvZHVjZSB0aGUgYWJvdmUgY29weXJpZ2h0Cm5vdGljZSwgdGhpcyBsaXN0IG9mIGNvbmRpdGlvbnMgYW5kIHRoZSBmb2xsb3dpbmcgZGlzY2xhaW1lciBpbiB0aGUKZG9jdW1lbnRhdGlvbiBhbmQvb3Igb3RoZXIgbWF0ZXJpYWxzIHByb3ZpZGVkIHdpdGggdGhlIGRpc3RyaWJ1dGlvbi4KMy4gTmVpdGhlciB0aGUgbmFtZSBvZiB0aGUgY29weXJpZ2h0IGhvbGRlciBub3IgdGhlIG5hbWVzIG9mIGl0cwpjb250cmlidXRvcnMgbWF5IGJlIHVzZWQgdG8gZW5kb3JzZSBvciBwcm9tb3RlIHByb2R1Y3RzIGRlcml2ZWQKZnJvbSB0aGlzIHNvZnR3YXJlIHdpdGhvdXQgc3BlY2lmaWMgcHJpb3Igd3JpdHRlbiBwZXJtaXNzaW9uLgoKVEhJUyBTT0ZUV0FSRSBJUyBQUk9WSURFRCBCWSBUSEUgQ09QWVJJR0hUIEhPTERFUlMgQU5EIENPTlRSSUJVVE9SUwoiQVMgSVMiIEFORCBBTlkgRVhQUkVTUyBPUiBJTVBMSUVEIFdBUlJBTlRJRVMsIElOQ0xVRElORywgQlVUIE5PVApMSU1JVEVEIFRPLCBUSEUgSU1QTElFRCBXQVJSQU5USUVTIE9GIE1FUkNIQU5UQUJJTElUWSBBTkQgRklUTkVTUwpGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UgQVJFIERJU0NMQUlNRUQuIElOIE5PIEVWRU5UIFNIQUxMIFRIRQpDT1BZUklHSFQgSE9MREVSIE9SIENPTlRSSUJVVE9SUyBCRSBMSUFCTEUgRk9SIEFOWSBESVJFQ1QsIElORElSRUNULApJTkNJREVOVEFMLCBTUEVDSUFMLCBFWEVNUExBUlksIE9SIENPTlNFUVVFTlRJQUwgREFNQUdFUyAoSU5DTFVESU5HLApCVVQgTk9UIExJTUlURUQgVE8sIFBST0NVUkVNRU5UIE9GIFNVQlNUSVRVVEUgR09PRFMgT1IgU0VSVklDRVM7CkxPU1MgT0YgVVNFLCBEQVRBLCBPUiBQUk9GSVRTOyBPUiBCVVNJTkVTUyBJTlRFUlJVUFRJT04pIEhPV0VWRVIKQ0FVU0VEIEFORCBPTiBBTlkgVEhFT1JZIE9GIExJQUJJTElUWSwgV0hFVEhFUiBJTiBDT05UUkFDVCwgU1RSSUNUCkxJQUJJTElUWSwgT1IgVE9SVCAoSU5DTFVESU5HIE5FR0xJR0VOQ0UgT1IgT1RIRVJXSVNFKSBBUklTSU5HIElOCkFOWSBXQVkgT1VUIE9GIFRIRSBVU0UgT0YgVEhJUyBTT0ZUV0FSRSwgRVZFTiBJRiBBRFZJU0VEIE9GIFRIRQpQT1NTSUJJTElUWSBPRiBTVUNIIERBTUFHRS4KClRoZSBhYm92ZSBsaWNlbnNlIGlzIHVzZWQgYXMgYSBsaWNlbnNlIHVuZGVyIGNvcHlyaWdodCBvbmx5LiBQbGVhc2UKcmVmZXJlbmNlIHRoZSBPTUEgSVBSIFBvbGljeSBmb3IgcGF0ZW50IGxpY2Vuc2luZyB0ZXJtczoKaHR0cHM6Ly93d3cub21hc3BlY3dvcmtzLm9yZy9hYm91dC9pbnRlbGxlY3R1YWwtcHJvcGVydHktcmlnaHRzLwoKLS0+CjxMV00yTSB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4c2k6bm9OYW1lc3BhY2VTY2hlbWFMb2NhdGlvbj0iaHR0cDovL29wZW5tb2JpbGVhbGxpYW5jZS5vcmcvdGVjaC9wcm9maWxlcy9MV00yTS54c2QiPgoJPE9iamVjdCBPYmplY3RUeXBlPSJNT0RlZmluaXRpb24iPgoJCTxOYW1lPkJpbmFyeUFwcERhdGFDb250YWluZXI8L05hbWU+CgkJPERlc2NyaXB0aW9uMT48IVtDREFUQVtUaGlzIEx3TTJNIE9iamVjdHMgcHJvdmlkZXMgdGhlIGFwcGxpY2F0aW9uIHNlcnZpY2UgZGF0YSByZWxhdGVkIHRvIGEgTHdNMk0gU2VydmVyLCBlZy4gV2F0ZXIgbWV0ZXIgZGF0YS4gClRoZXJlIGFyZSBzZXZlcmFsIG1ldGhvZHMgdG8gY3JlYXRlIGluc3RhbmNlIHRvIGluZGljYXRlIHRoZSBtZXNzYWdlIGRpcmVjdGlvbiBiYXNlZCBvbiB0aGUgbmVnb3RpYXRpb24gYmV0d2VlbiBBcHBsaWNhdGlvbiBhbmQgTHdNMk0uIFRoZSBDbGllbnQgYW5kIFNlcnZlciBzaG91bGQgbmVnb3RpYXRlIHRoZSBpbnN0YW5jZShzKSB1c2VkIHRvIGV4Y2hhbmdlIHRoZSBkYXRhLiBGb3IgZXhhbXBsZToKIC0gVXNpbmcgYSBzaW5nbGUgaW5zdGFuY2UgZm9yIGJvdGggZGlyZWN0aW9ucyBjb21tdW5pY2F0aW9uLCBmcm9tIENsaWVudCB0byBTZXJ2ZXIgYW5kIGZyb20gU2VydmVyIHRvIENsaWVudC4KIC0gVXNpbmcgYW4gaW5zdGFuY2UgZm9yIGNvbW11bmljYXRpb24gZnJvbSBDbGllbnQgdG8gU2VydmVyIGFuZCBhbm90aGVyIG9uZSBmb3IgY29tbXVuaWNhdGlvbiBmcm9tIFNlcnZlciB0byBDbGllbnQKIC0gVXNpbmcgc2V2ZXJhbCBpbnN0YW5jZXMKXV0+PC9EZXNjcmlwdGlvbjE+CgkJPE9iamVjdElEPjE5PC9PYmplY3RJRD4KCQk8T2JqZWN0VVJOPnVybjpvbWE6bHdtMm06b21hOjE5PC9PYmplY3RVUk4+CgkJPExXTTJNVmVyc2lvbj4xLjA8L0xXTTJNVmVyc2lvbj4KCQk8T2JqZWN0VmVyc2lvbj4xLjA8L09iamVjdFZlcnNpb24+CgkJPE11bHRpcGxlSW5zdGFuY2VzPk11bHRpcGxlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQk8TWFuZGF0b3J5Pk9wdGlvbmFsPC9NYW5kYXRvcnk+CgkJPFJlc291cmNlcz4KCQkJPEl0ZW0gSUQ9IjAiPjxOYW1lPkRhdGE8L05hbWU+CgkJCQk8T3BlcmF0aW9ucz5SVzwvT3BlcmF0aW9ucz4KCQkJCTxNdWx0aXBsZUluc3RhbmNlcz5NdWx0aXBsZTwvTXVsdGlwbGVJbnN0YW5jZXM+CgkJCQk8TWFuZGF0b3J5Pk1hbmRhdG9yeTwvTWFuZGF0b3J5PgoJCQkJPFR5cGU+T3BhcXVlPC9UeXBlPgoJCQkJPFJhbmdlRW51bWVyYXRpb24gLz4KCQkJCTxVbml0cyAvPgoJCQkJPERlc2NyaXB0aW9uPjwhW0NEQVRBW0luZGljYXRlcyB0aGUgYXBwbGljYXRpb24gZGF0YSBjb250ZW50Ll1dPjwvRGVzY3JpcHRpb24+CgkJCTwvSXRlbT4KCQkJPEl0ZW0gSUQ9IjEiPjxOYW1lPkRhdGEgUHJpb3JpdHk8L05hbWU+CgkJCQk8T3BlcmF0aW9ucz5SVzwvT3BlcmF0aW9ucz4KCQkJCTxNdWx0aXBsZUluc3RhbmNlcz5TaW5nbGU8L011bHRpcGxlSW5zdGFuY2VzPgoJCQkJPE1hbmRhdG9yeT5PcHRpb25hbDwvTWFuZGF0b3J5PgoJCQkJPFR5cGU+SW50ZWdlcjwvVHlwZT4KCQkJCTxSYW5nZUVudW1lcmF0aW9uPjEgYnl0ZXM8L1JhbmdlRW51bWVyYXRpb24+CgkJCQk8VW5pdHMgLz4KCQkJCTxEZXNjcmlwdGlvbj48IVtDREFUQVtJbmRpY2F0ZXMgdGhlIEFwcGxpY2F0aW9uIGRhdGEgcHJpb3JpdHk6CjA6SW1tZWRpYXRlCjE6QmVzdEVmZm9ydAoyOkxhdGVzdAozLTEwMDogUmVzZXJ2ZWQgZm9yIGZ1dHVyZSB1c2UuCjEwMS0yNTQ6IFByb3ByaWV0YXJ5IG1vZGUuXV0+PC9EZXNjcmlwdGlvbj4KCQkJPC9JdGVtPgoJCQk8SXRlbSBJRD0iMiI+PE5hbWU+RGF0YSBDcmVhdGlvbiBUaW1lPC9OYW1lPgoJCQkJPE9wZXJhdGlvbnM+Ulc8L09wZXJhdGlvbnM+CgkJCQk8TXVsdGlwbGVJbnN0YW5jZXM+U2luZ2xlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQkJCTxNYW5kYXRvcnk+T3B0aW9uYWw8L01hbmRhdG9yeT4KCQkJCTxUeXBlPlRpbWU8L1R5cGU+CgkJCQk8UmFuZ2VFbnVtZXJhdGlvbiAvPgoJCQkJPFVuaXRzIC8+CgkJCQk8RGVzY3JpcHRpb24+PCFbQ0RBVEFbSW5kaWNhdGVzIHRoZSBEYXRhIGluc3RhbmNlIGNyZWF0aW9uIHRpbWVzdGFtcC5dXT48L0Rlc2NyaXB0aW9uPgoJCQk8L0l0ZW0+CgkJCTxJdGVtIElEPSIzIj48TmFtZT5EYXRhIERlc2NyaXB0aW9uPC9OYW1lPgoJCQkJPE9wZXJhdGlvbnM+Ulc8L09wZXJhdGlvbnM+CgkJCQk8TXVsdGlwbGVJbnN0YW5jZXM+U2luZ2xlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQkJCTxNYW5kYXRvcnk+T3B0aW9uYWw8L01hbmRhdG9yeT4KCQkJCTxUeXBlPlN0cmluZzwvVHlwZT4KCQkJCTxSYW5nZUVudW1lcmF0aW9uPjMyIGJ5dGVzPC9SYW5nZUVudW1lcmF0aW9uPgoJCQkJPFVuaXRzIC8+CgkJCQk8RGVzY3JpcHRpb24+PCFbQ0RBVEFbSW5kaWNhdGVzIHRoZSBkYXRhIGRlc2NyaXB0aW9uLgplLmcuICJtZXRlciByZWFkaW5nIi5dXT48L0Rlc2NyaXB0aW9uPgoJCQk8L0l0ZW0+CgkJCTxJdGVtIElEPSI0Ij48TmFtZT5EYXRhIEZvcm1hdDwvTmFtZT4KCQkJCTxPcGVyYXRpb25zPlJXPC9PcGVyYXRpb25zPgoJCQkJPE11bHRpcGxlSW5zdGFuY2VzPlNpbmdsZTwvTXVsdGlwbGVJbnN0YW5jZXM+CgkJCQk8TWFuZGF0b3J5Pk9wdGlvbmFsPC9NYW5kYXRvcnk+CgkJCQk8VHlwZT5TdHJpbmc8L1R5cGU+CgkJCQk8UmFuZ2VFbnVtZXJhdGlvbj4zMiBieXRlczwvUmFuZ2VFbnVtZXJhdGlvbj4KCQkJCTxVbml0cyAvPgoJCQkJPERlc2NyaXB0aW9uPjwhW0NEQVRBW0luZGljYXRlcyB0aGUgZm9ybWF0IG9mIHRoZSBBcHBsaWNhdGlvbiBEYXRhLgplLmcuIFlHLU1ldGVyLVdhdGVyLVJlYWRpbmcKVVRGOC1zdHJpbmcKXV0+PC9EZXNjcmlwdGlvbj4KCQkJPC9JdGVtPgoJCQk8SXRlbSBJRD0iNSI+PE5hbWU+QXBwIElEPC9OYW1lPgoJCQkJPE9wZXJhdGlvbnM+Ulc8L09wZXJhdGlvbnM+CgkJCQk8TXVsdGlwbGVJbnN0YW5jZXM+U2luZ2xlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQkJCTxNYW5kYXRvcnk+T3B0aW9uYWw8L01hbmRhdG9yeT4KCQkJCTxUeXBlPkludGVnZXI8L1R5cGU+CgkJCQk8UmFuZ2VFbnVtZXJhdGlvbj4yIGJ5dGVzPC9SYW5nZUVudW1lcmF0aW9uPgoJCQkJPFVuaXRzIC8+CgkJCQk8RGVzY3JpcHRpb24+PCFbQ0RBVEFbSW5kaWNhdGVzIHRoZSBkZXN0aW5hdGlvbiBBcHBsaWNhdGlvbiBJRC5dXT48L0Rlc2NyaXB0aW9uPgoJCQk8L0l0ZW0+PC9SZXNvdXJjZXM+CgkJPERlc2NyaXB0aW9uMj48IVtDREFUQVtdXT48L0Rlc2NyaXB0aW9uMj4KCTwvT2JqZWN0Pgo8L0xXTTJNPgo=", accessMode = ApiModelProperty.AccessMode.READ_WRITE) - private String base64Data; - @JsonIgnore private byte[] data; @@ -54,22 +54,37 @@ public class TbResource extends TbResourceInfo { public TbResource(TbResource resource) { super(resource); - this.base64Data = resource.base64Data; this.data = resource.data; this.preview = resource.preview; } - @JsonIgnore - public byte[] getData() { - if (data == null && base64Data != null) { - try { - data = Base64.getDecoder().decode(base64Data); - } catch (Exception e) { - throw new IllegalArgumentException("Failed to decode resource data from Base64", e); - } - base64Data = null; - } - return data; + @ApiModelProperty(position = 12, value = "Resource data.", example = "77u/PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCEtLQpGSUxFIElORk9STUFUSU9OCgpPTUEgUGVybWFuZW50IERvY3VtZW50CiAgIEZpbGU6IE9NQS1TVVAtTHdNMk1fQmluYXJ5QXBwRGF0YUNvbnRhaW5lci1WMV8wXzEtMjAxOTAyMjEtQQogICBUeXBlOiB4bWwKClB1YmxpYyBSZWFjaGFibGUgSW5mb3JtYXRpb24KICAgUGF0aDogaHR0cDovL3d3dy5vcGVubW9iaWxlYWxsaWFuY2Uub3JnL3RlY2gvcHJvZmlsZXMKICAgTmFtZTogTHdNMk1fQmluYXJ5QXBwRGF0YUNvbnRhaW5lci12MV8wXzEueG1sCgpOT1JNQVRJVkUgSU5GT1JNQVRJT04KCiAgSW5mb3JtYXRpb24gYWJvdXQgdGhpcyBmaWxlIGNhbiBiZSBmb3VuZCBpbiB0aGUgbGF0ZXN0IHJldmlzaW9uIG9mCgogIE9NQS1UUy1MV00yTV9CaW5hcnlBcHBEYXRhQ29udGFpbmVyLVYxXzBfMQoKICBUaGlzIGlzIGF2YWlsYWJsZSBhdCBodHRwOi8vd3d3Lm9wZW5tb2JpbGVhbGxpYW5jZS5vcmcvCgogIFNlbmQgY29tbWVudHMgdG8gaHR0cHM6Ly9naXRodWIuY29tL09wZW5Nb2JpbGVBbGxpYW5jZS9PTUFfTHdNMk1fZm9yX0RldmVsb3BlcnMvaXNzdWVzCgpDSEFOR0UgSElTVE9SWQoKMTUwNjIwMTggU3RhdHVzIGNoYW5nZWQgdG8gQXBwcm92ZWQgYnkgRE0sIERvYyBSZWYgIyBPTUEtRE0mU0UtMjAxOC0wMDYxLUlOUF9MV00yTV9BUFBEQVRBX1YxXzBfRVJQX2Zvcl9maW5hbF9BcHByb3ZhbAoyMTAyMjAxOSBTdGF0dXMgY2hhbmdlZCB0byBBcHByb3ZlZCBieSBJUFNPLCBEb2MgUmVmICMgT01BLUlQU08tMjAxOS0wMDI1LUlOUF9Md00yTV9PYmplY3RfQXBwX0RhdGFfQ29udGFpbmVyXzFfMF8xX2Zvcl9GaW5hbF9BcHByb3ZhbAoKTEVHQUwgRElTQ0xBSU1FUgoKQ29weXJpZ2h0IDIwMTkgT3BlbiBNb2JpbGUgQWxsaWFuY2UuCgpSZWRpc3RyaWJ1dGlvbiBhbmQgdXNlIGluIHNvdXJjZSBhbmQgYmluYXJ5IGZvcm1zLCB3aXRoIG9yIHdpdGhvdXQKbW9kaWZpY2F0aW9uLCBhcmUgcGVybWl0dGVkIHByb3ZpZGVkIHRoYXQgdGhlIGZvbGxvd2luZyBjb25kaXRpb25zCmFyZSBtZXQ6CgoxLiBSZWRpc3RyaWJ1dGlvbnMgb2Ygc291cmNlIGNvZGUgbXVzdCByZXRhaW4gdGhlIGFib3ZlIGNvcHlyaWdodApub3RpY2UsIHRoaXMgbGlzdCBvZiBjb25kaXRpb25zIGFuZCB0aGUgZm9sbG93aW5nIGRpc2NsYWltZXIuCjIuIFJlZGlzdHJpYnV0aW9ucyBpbiBiaW5hcnkgZm9ybSBtdXN0IHJlcHJvZHVjZSB0aGUgYWJvdmUgY29weXJpZ2h0Cm5vdGljZSwgdGhpcyBsaXN0IG9mIGNvbmRpdGlvbnMgYW5kIHRoZSBmb2xsb3dpbmcgZGlzY2xhaW1lciBpbiB0aGUKZG9jdW1lbnRhdGlvbiBhbmQvb3Igb3RoZXIgbWF0ZXJpYWxzIHByb3ZpZGVkIHdpdGggdGhlIGRpc3RyaWJ1dGlvbi4KMy4gTmVpdGhlciB0aGUgbmFtZSBvZiB0aGUgY29weXJpZ2h0IGhvbGRlciBub3IgdGhlIG5hbWVzIG9mIGl0cwpjb250cmlidXRvcnMgbWF5IGJlIHVzZWQgdG8gZW5kb3JzZSBvciBwcm9tb3RlIHByb2R1Y3RzIGRlcml2ZWQKZnJvbSB0aGlzIHNvZnR3YXJlIHdpdGhvdXQgc3BlY2lmaWMgcHJpb3Igd3JpdHRlbiBwZXJtaXNzaW9uLgoKVEhJUyBTT0ZUV0FSRSBJUyBQUk9WSURFRCBCWSBUSEUgQ09QWVJJR0hUIEhPTERFUlMgQU5EIENPTlRSSUJVVE9SUwoiQVMgSVMiIEFORCBBTlkgRVhQUkVTUyBPUiBJTVBMSUVEIFdBUlJBTlRJRVMsIElOQ0xVRElORywgQlVUIE5PVApMSU1JVEVEIFRPLCBUSEUgSU1QTElFRCBXQVJSQU5USUVTIE9GIE1FUkNIQU5UQUJJTElUWSBBTkQgRklUTkVTUwpGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UgQVJFIERJU0NMQUlNRUQuIElOIE5PIEVWRU5UIFNIQUxMIFRIRQpDT1BZUklHSFQgSE9MREVSIE9SIENPTlRSSUJVVE9SUyBCRSBMSUFCTEUgRk9SIEFOWSBESVJFQ1QsIElORElSRUNULApJTkNJREVOVEFMLCBTUEVDSUFMLCBFWEVNUExBUlksIE9SIENPTlNFUVVFTlRJQUwgREFNQUdFUyAoSU5DTFVESU5HLApCVVQgTk9UIExJTUlURUQgVE8sIFBST0NVUkVNRU5UIE9GIFNVQlNUSVRVVEUgR09PRFMgT1IgU0VSVklDRVM7CkxPU1MgT0YgVVNFLCBEQVRBLCBPUiBQUk9GSVRTOyBPUiBCVVNJTkVTUyBJTlRFUlJVUFRJT04pIEhPV0VWRVIKQ0FVU0VEIEFORCBPTiBBTlkgVEhFT1JZIE9GIExJQUJJTElUWSwgV0hFVEhFUiBJTiBDT05UUkFDVCwgU1RSSUNUCkxJQUJJTElUWSwgT1IgVE9SVCAoSU5DTFVESU5HIE5FR0xJR0VOQ0UgT1IgT1RIRVJXSVNFKSBBUklTSU5HIElOCkFOWSBXQVkgT1VUIE9GIFRIRSBVU0UgT0YgVEhJUyBTT0ZUV0FSRSwgRVZFTiBJRiBBRFZJU0VEIE9GIFRIRQpQT1NTSUJJTElUWSBPRiBTVUNIIERBTUFHRS4KClRoZSBhYm92ZSBsaWNlbnNlIGlzIHVzZWQgYXMgYSBsaWNlbnNlIHVuZGVyIGNvcHlyaWdodCBvbmx5LiBQbGVhc2UKcmVmZXJlbmNlIHRoZSBPTUEgSVBSIFBvbGljeSBmb3IgcGF0ZW50IGxpY2Vuc2luZyB0ZXJtczoKaHR0cHM6Ly93d3cub21hc3BlY3dvcmtzLm9yZy9hYm91dC9pbnRlbGxlY3R1YWwtcHJvcGVydHktcmlnaHRzLwoKLS0+CjxMV00yTSB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4c2k6bm9OYW1lc3BhY2VTY2hlbWFMb2NhdGlvbj0iaHR0cDovL29wZW5tb2JpbGVhbGxpYW5jZS5vcmcvdGVjaC9wcm9maWxlcy9MV00yTS54c2QiPgoJPE9iamVjdCBPYmplY3RUeXBlPSJNT0RlZmluaXRpb24iPgoJCTxOYW1lPkJpbmFyeUFwcERhdGFDb250YWluZXI8L05hbWU+CgkJPERlc2NyaXB0aW9uMT48IVtDREFUQVtUaGlzIEx3TTJNIE9iamVjdHMgcHJvdmlkZXMgdGhlIGFwcGxpY2F0aW9uIHNlcnZpY2UgZGF0YSByZWxhdGVkIHRvIGEgTHdNMk0gU2VydmVyLCBlZy4gV2F0ZXIgbWV0ZXIgZGF0YS4gClRoZXJlIGFyZSBzZXZlcmFsIG1ldGhvZHMgdG8gY3JlYXRlIGluc3RhbmNlIHRvIGluZGljYXRlIHRoZSBtZXNzYWdlIGRpcmVjdGlvbiBiYXNlZCBvbiB0aGUgbmVnb3RpYXRpb24gYmV0d2VlbiBBcHBsaWNhdGlvbiBhbmQgTHdNMk0uIFRoZSBDbGllbnQgYW5kIFNlcnZlciBzaG91bGQgbmVnb3RpYXRlIHRoZSBpbnN0YW5jZShzKSB1c2VkIHRvIGV4Y2hhbmdlIHRoZSBkYXRhLiBGb3IgZXhhbXBsZToKIC0gVXNpbmcgYSBzaW5nbGUgaW5zdGFuY2UgZm9yIGJvdGggZGlyZWN0aW9ucyBjb21tdW5pY2F0aW9uLCBmcm9tIENsaWVudCB0byBTZXJ2ZXIgYW5kIGZyb20gU2VydmVyIHRvIENsaWVudC4KIC0gVXNpbmcgYW4gaW5zdGFuY2UgZm9yIGNvbW11bmljYXRpb24gZnJvbSBDbGllbnQgdG8gU2VydmVyIGFuZCBhbm90aGVyIG9uZSBmb3IgY29tbXVuaWNhdGlvbiBmcm9tIFNlcnZlciB0byBDbGllbnQKIC0gVXNpbmcgc2V2ZXJhbCBpbnN0YW5jZXMKXV0+PC9EZXNjcmlwdGlvbjE+CgkJPE9iamVjdElEPjE5PC9PYmplY3RJRD4KCQk8T2JqZWN0VVJOPnVybjpvbWE6bHdtMm06b21hOjE5PC9PYmplY3RVUk4+CgkJPExXTTJNVmVyc2lvbj4xLjA8L0xXTTJNVmVyc2lvbj4KCQk8T2JqZWN0VmVyc2lvbj4xLjA8L09iamVjdFZlcnNpb24+CgkJPE11bHRpcGxlSW5zdGFuY2VzPk11bHRpcGxlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQk8TWFuZGF0b3J5Pk9wdGlvbmFsPC9NYW5kYXRvcnk+CgkJPFJlc291cmNlcz4KCQkJPEl0ZW0gSUQ9IjAiPjxOYW1lPkRhdGE8L05hbWU+CgkJCQk8T3BlcmF0aW9ucz5SVzwvT3BlcmF0aW9ucz4KCQkJCTxNdWx0aXBsZUluc3RhbmNlcz5NdWx0aXBsZTwvTXVsdGlwbGVJbnN0YW5jZXM+CgkJCQk8TWFuZGF0b3J5Pk1hbmRhdG9yeTwvTWFuZGF0b3J5PgoJCQkJPFR5cGU+T3BhcXVlPC9UeXBlPgoJCQkJPFJhbmdlRW51bWVyYXRpb24gLz4KCQkJCTxVbml0cyAvPgoJCQkJPERlc2NyaXB0aW9uPjwhW0NEQVRBW0luZGljYXRlcyB0aGUgYXBwbGljYXRpb24gZGF0YSBjb250ZW50Ll1dPjwvRGVzY3JpcHRpb24+CgkJCTwvSXRlbT4KCQkJPEl0ZW0gSUQ9IjEiPjxOYW1lPkRhdGEgUHJpb3JpdHk8L05hbWU+CgkJCQk8T3BlcmF0aW9ucz5SVzwvT3BlcmF0aW9ucz4KCQkJCTxNdWx0aXBsZUluc3RhbmNlcz5TaW5nbGU8L011bHRpcGxlSW5zdGFuY2VzPgoJCQkJPE1hbmRhdG9yeT5PcHRpb25hbDwvTWFuZGF0b3J5PgoJCQkJPFR5cGU+SW50ZWdlcjwvVHlwZT4KCQkJCTxSYW5nZUVudW1lcmF0aW9uPjEgYnl0ZXM8L1JhbmdlRW51bWVyYXRpb24+CgkJCQk8VW5pdHMgLz4KCQkJCTxEZXNjcmlwdGlvbj48IVtDREFUQVtJbmRpY2F0ZXMgdGhlIEFwcGxpY2F0aW9uIGRhdGEgcHJpb3JpdHk6CjA6SW1tZWRpYXRlCjE6QmVzdEVmZm9ydAoyOkxhdGVzdAozLTEwMDogUmVzZXJ2ZWQgZm9yIGZ1dHVyZSB1c2UuCjEwMS0yNTQ6IFByb3ByaWV0YXJ5IG1vZGUuXV0+PC9EZXNjcmlwdGlvbj4KCQkJPC9JdGVtPgoJCQk8SXRlbSBJRD0iMiI+PE5hbWU+RGF0YSBDcmVhdGlvbiBUaW1lPC9OYW1lPgoJCQkJPE9wZXJhdGlvbnM+Ulc8L09wZXJhdGlvbnM+CgkJCQk8TXVsdGlwbGVJbnN0YW5jZXM+U2luZ2xlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQkJCTxNYW5kYXRvcnk+T3B0aW9uYWw8L01hbmRhdG9yeT4KCQkJCTxUeXBlPlRpbWU8L1R5cGU+CgkJCQk8UmFuZ2VFbnVtZXJhdGlvbiAvPgoJCQkJPFVuaXRzIC8+CgkJCQk8RGVzY3JpcHRpb24+PCFbQ0RBVEFbSW5kaWNhdGVzIHRoZSBEYXRhIGluc3RhbmNlIGNyZWF0aW9uIHRpbWVzdGFtcC5dXT48L0Rlc2NyaXB0aW9uPgoJCQk8L0l0ZW0+CgkJCTxJdGVtIElEPSIzIj48TmFtZT5EYXRhIERlc2NyaXB0aW9uPC9OYW1lPgoJCQkJPE9wZXJhdGlvbnM+Ulc8L09wZXJhdGlvbnM+CgkJCQk8TXVsdGlwbGVJbnN0YW5jZXM+U2luZ2xlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQkJCTxNYW5kYXRvcnk+T3B0aW9uYWw8L01hbmRhdG9yeT4KCQkJCTxUeXBlPlN0cmluZzwvVHlwZT4KCQkJCTxSYW5nZUVudW1lcmF0aW9uPjMyIGJ5dGVzPC9SYW5nZUVudW1lcmF0aW9uPgoJCQkJPFVuaXRzIC8+CgkJCQk8RGVzY3JpcHRpb24+PCFbQ0RBVEFbSW5kaWNhdGVzIHRoZSBkYXRhIGRlc2NyaXB0aW9uLgplLmcuICJtZXRlciByZWFkaW5nIi5dXT48L0Rlc2NyaXB0aW9uPgoJCQk8L0l0ZW0+CgkJCTxJdGVtIElEPSI0Ij48TmFtZT5EYXRhIEZvcm1hdDwvTmFtZT4KCQkJCTxPcGVyYXRpb25zPlJXPC9PcGVyYXRpb25zPgoJCQkJPE11bHRpcGxlSW5zdGFuY2VzPlNpbmdsZTwvTXVsdGlwbGVJbnN0YW5jZXM+CgkJCQk8TWFuZGF0b3J5Pk9wdGlvbmFsPC9NYW5kYXRvcnk+CgkJCQk8VHlwZT5TdHJpbmc8L1R5cGU+CgkJCQk8UmFuZ2VFbnVtZXJhdGlvbj4zMiBieXRlczwvUmFuZ2VFbnVtZXJhdGlvbj4KCQkJCTxVbml0cyAvPgoJCQkJPERlc2NyaXB0aW9uPjwhW0NEQVRBW0luZGljYXRlcyB0aGUgZm9ybWF0IG9mIHRoZSBBcHBsaWNhdGlvbiBEYXRhLgplLmcuIFlHLU1ldGVyLVdhdGVyLVJlYWRpbmcKVVRGOC1zdHJpbmcKXV0+PC9EZXNjcmlwdGlvbj4KCQkJPC9JdGVtPgoJCQk8SXRlbSBJRD0iNSI+PE5hbWU+QXBwIElEPC9OYW1lPgoJCQkJPE9wZXJhdGlvbnM+Ulc8L09wZXJhdGlvbnM+CgkJCQk8TXVsdGlwbGVJbnN0YW5jZXM+U2luZ2xlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQkJCTxNYW5kYXRvcnk+T3B0aW9uYWw8L01hbmRhdG9yeT4KCQkJCTxUeXBlPkludGVnZXI8L1R5cGU+CgkJCQk8UmFuZ2VFbnVtZXJhdGlvbj4yIGJ5dGVzPC9SYW5nZUVudW1lcmF0aW9uPgoJCQkJPFVuaXRzIC8+CgkJCQk8RGVzY3JpcHRpb24+PCFbQ0RBVEFbSW5kaWNhdGVzIHRoZSBkZXN0aW5hdGlvbiBBcHBsaWNhdGlvbiBJRC5dXT48L0Rlc2NyaXB0aW9uPgoJCQk8L0l0ZW0+PC9SZXNvdXJjZXM+CgkJPERlc2NyaXB0aW9uMj48IVtDREFUQVtdXT48L0Rlc2NyaXB0aW9uMj4KCTwvT2JqZWN0Pgo8L0xXTTJNPgo=", accessMode = ApiModelProperty.AccessMode.READ_WRITE) + @JsonGetter("data") + public String getEncodedData() { + return Optional.ofNullable(data) + .map(Base64.getEncoder()::encodeToString) + .orElse(null); + } + + @JsonSetter("data") + public void setEncodedData(String data) { + this.data = Optional.ofNullable(data) + .map(Base64.getDecoder()::decode) + .orElse(null); + } + + @JsonGetter("preview") + public String getEncodedPreview() { + return Optional.ofNullable(preview) + .map(Base64.getEncoder()::encodeToString) + .orElse(null); + } + + @JsonSetter("preview") + public void setEncodedPreview(String preview) { + this.preview = Optional.ofNullable(preview) + .map(Base64.getDecoder()::decode) + .orElse(null); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceInfoDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceInfoDao.java index fc7a29af11..5617aa997c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceInfoDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceInfoDao.java @@ -32,8 +32,10 @@ import org.thingsboard.server.dao.resource.TbResourceInfoDao; import org.thingsboard.server.dao.sql.JpaAbstractDao; import org.thingsboard.server.dao.util.SqlDao; +import java.util.EnumSet; import java.util.List; import java.util.Objects; +import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; @@ -57,22 +59,28 @@ public class JpaTbResourceInfoDao extends JpaAbstractDao findAllTenantResourcesByTenantId(TbResourceInfoFilter filter, PageLink pageLink) { + Set resourceTypes = filter.getResourceTypes(); + if (CollectionsUtil.isEmpty(resourceTypes)) { + resourceTypes = EnumSet.allOf(ResourceType.class); + } return DaoUtil.toPageData(resourceInfoRepository .findAllTenantResourcesByTenantId( filter.getTenantId().getId(), TenantId.NULL_UUID, - CollectionsUtil.isNotEmpty(filter.getResourceTypes()) ? filter.getResourceTypes() - .stream().map(Enum::name).collect(Collectors.toList()) : null, + resourceTypes.stream().map(Enum::name).collect(Collectors.toList()), Objects.toString(pageLink.getTextSearch(), ""), DaoUtil.toPageable(pageLink))); } @Override public PageData findTenantResourcesByTenantId(TbResourceInfoFilter filter, PageLink pageLink) { + Set resourceTypes = filter.getResourceTypes(); + if (CollectionsUtil.isEmpty(resourceTypes)) { + resourceTypes = EnumSet.allOf(ResourceType.class); + } return DaoUtil.toPageData(resourceInfoRepository .findTenantResourcesByTenantId( filter.getTenantId().getId(), - CollectionsUtil.isNotEmpty(filter.getResourceTypes()) ? filter.getResourceTypes() - .stream().map(Enum::name).collect(Collectors.toList()) : null, + resourceTypes.stream().map(Enum::name).collect(Collectors.toList()), pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); } diff --git a/ui-ngx/src/app/modules/home/pages/admin/resource/resources-library.component.html b/ui-ngx/src/app/modules/home/pages/admin/resource/resources-library.component.html index cb7dbf9ddc..f955d26f0d 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/resource/resources-library.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/resource/resources-library.component.html @@ -67,7 +67,7 @@ impleme this.entityForm.get('title').enable({emitEvent: false}); } this.entityForm.patchValue({ - base64Data: null, + data: null, fileName: null }, {emitEvent: false}); }); @@ -93,7 +93,7 @@ export class ResourcesLibraryComponent extends EntityComponent impleme title: [entity ? entity.title : '', [Validators.required, Validators.maxLength(255)]], resourceType: [entity?.resourceType ? entity.resourceType : ResourceType.JS_MODULE, Validators.required], fileName: [entity ? entity.fileName : null, Validators.required], - base64Data: [entity ? entity.base64Data : null, Validators.required] + data: [entity ? entity.data : null, Validators.required] }); } @@ -102,20 +102,20 @@ export class ResourcesLibraryComponent extends EntityComponent impleme this.entityForm.get('resourceType').disable({emitEvent: false}); if (entity.resourceType !== ResourceType.JS_MODULE) { this.entityForm.get('fileName').disable({emitEvent: false}); - this.entityForm.get('base64Data').disable({emitEvent: false}); + this.entityForm.get('data').disable({emitEvent: false}); } } this.entityForm.patchValue({ resourceType: entity.resourceType, fileName: entity.fileName, title: entity.title, - base64Data: entity.base64Data + data: entity.data }); } prepareFormValue(formValue: Resource): Resource { - if (this.isEdit && !isDefinedAndNotNull(formValue.base64Data)) { - delete formValue.base64Data; + if (this.isEdit && !isDefinedAndNotNull(formValue.data)) { + delete formValue.data; } return super.prepareFormValue(formValue); } diff --git a/ui-ngx/src/app/shared/models/resource.models.ts b/ui-ngx/src/app/shared/models/resource.models.ts index 61d413f60e..eb954da35a 100644 --- a/ui-ngx/src/app/shared/models/resource.models.ts +++ b/ui-ngx/src/app/shared/models/resource.models.ts @@ -64,6 +64,6 @@ export interface ResourceInfo extends Omit, 'name' | 'lab } export interface Resource extends ResourceInfo { - base64Data: string; + data: string; name?: string; } From e9c003f76daf68c2c6998921bf74f71dfc670ba7 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Wed, 15 Nov 2023 15:14:41 +0200 Subject: [PATCH 144/252] Subscribe first in testSubscribingToUnreadNotificationsCount (cherry picked from commit edcc507f4b9ec40ee92c70f2c0d8f8490f7ccbe8) --- .../server/service/notification/NotificationApiTest.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java index 26be4e9bd3..ba0b4e4763 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java @@ -113,15 +113,13 @@ public class NotificationApiTest extends AbstractNotificationApiTest { @Test public void testSubscribingToUnreadNotificationsCount() { + wsClient.subscribeForUnreadNotificationsCount().waitForReply(true); NotificationTarget notificationTarget = createNotificationTarget(customerUserId); String notificationText1 = "Notification 1"; submitNotificationRequest(notificationTarget.getId(), notificationText1); String notificationText2 = "Notification 2"; submitNotificationRequest(notificationTarget.getId(), notificationText2); - wsClient.subscribeForUnreadNotificationsCount(); - wsClient.waitForReply(true); - await().atMost(2, TimeUnit.SECONDS) .until(() -> wsClient.getLastCountUpdate().getTotalUnreadCount() == 2); } From 409f45272b3aca996eb15347b1ed23f1371fb514 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Thu, 16 Nov 2023 14:33:36 +0200 Subject: [PATCH 145/252] Improve EdgeProcessor to receive boolean instead of EdgeVersion: it simplifies the code on Edge as well --- .../edge/rpc/processor/alarm/AlarmEdgeProcessor.java | 3 ++- .../edge/rpc/processor/alarm/BaseAlarmProcessor.java | 3 +-- .../edge/rpc/processor/device/BaseDeviceProcessor.java | 6 ++---- .../rpc/processor/device/BaseDeviceProfileProcessor.java | 6 +++--- .../edge/rpc/processor/device/DeviceEdgeProcessor.java | 2 +- .../org/thingsboard/server/common/data/edge/EdgeEvent.java | 2 ++ 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/AlarmEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/AlarmEdgeProcessor.java index b934a33d54..a09d36f656 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/AlarmEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/AlarmEdgeProcessor.java @@ -37,6 +37,7 @@ import org.thingsboard.server.gen.edge.v1.DownlinkMsg; import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; import java.util.ArrayList; import java.util.List; @@ -51,7 +52,7 @@ public class AlarmEdgeProcessor extends BaseAlarmProcessor { log.trace("[{}] processAlarmMsgFromEdge [{}]", tenantId, alarmUpdateMsg); try { edgeSynchronizationManager.getEdgeId().set(edgeId); - return processAlarmMsg(tenantId, alarmUpdateMsg, edgeVersion); + return processAlarmMsg(tenantId, alarmUpdateMsg, EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)); } finally { edgeSynchronizationManager.getEdgeId().remove(); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java index fd53e38f4a..ab30d21e1f 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java @@ -47,10 +47,9 @@ import java.util.UUID; @Slf4j public abstract class BaseAlarmProcessor extends BaseEdgeProcessor { - public ListenableFuture processAlarmMsg(TenantId tenantId, AlarmUpdateMsg alarmUpdateMsg, EdgeVersion edgeVersion) { + public ListenableFuture processAlarmMsg(TenantId tenantId, AlarmUpdateMsg alarmUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2) { log.trace("[{}] processAlarmMsg [{}]", tenantId, alarmUpdateMsg); AlarmId alarmId = new AlarmId(new UUID(alarmUpdateMsg.getIdMSB(), alarmUpdateMsg.getIdLSB())); - boolean isEdgeVersionOlderThan_3_6_2 = EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion); Alarm alarm = isEdgeVersionOlderThan_3_6_2 ? createDeprecatedAlarm(tenantId, alarmUpdateMsg) : JacksonUtil.fromStringIgnoreUnknownProperties(alarmUpdateMsg.getEntity(), Alarm.class); if (alarm == null) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProcessor.java index 27d4b31642..4c5141034d 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProcessor.java @@ -32,10 +32,8 @@ import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentialsType; import org.thingsboard.server.gen.edge.v1.DeviceCredentialsUpdateMsg; import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg; -import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.queue.util.DataDecodingEncodingService; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; -import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; import java.util.Optional; import java.util.UUID; @@ -121,8 +119,8 @@ public abstract class BaseDeviceProcessor extends BaseEdgeProcessor { return device; } - protected void updateDeviceCredentials(TenantId tenantId, DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg, EdgeVersion edgeVersion) { - DeviceCredentials deviceCredentials = EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion) + protected void updateDeviceCredentials(TenantId tenantId, DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2) { + DeviceCredentials deviceCredentials = isEdgeVersionOlderThan_3_6_2 ? createDeviceCredentials(deviceCredentialsUpdateMsg) : JacksonUtil.fromStringIgnoreUnknownProperties(deviceCredentialsUpdateMsg.getEntity(), DeviceCredentials.class); if (deviceCredentials == null) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProfileProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProfileProcessor.java index c9402730f3..97d751dc27 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProfileProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProfileProcessor.java @@ -51,8 +51,8 @@ public abstract class BaseDeviceProfileProcessor extends BaseEdgeProcessor { deviceCreationLock.lock(); try { DeviceProfile deviceProfile = isEdgeVersionOlderThan_3_6_2 - ? createDeviceProfile(tenantId, deviceProfileId, deviceProfileUpdateMsg) - : JacksonUtil.fromStringIgnoreUnknownProperties(deviceProfileUpdateMsg.getEntity(), DeviceProfile.class); + ? createDeviceProfile(tenantId, deviceProfileId, deviceProfileUpdateMsg) + : JacksonUtil.fromStringIgnoreUnknownProperties(deviceProfileUpdateMsg.getEntity(), DeviceProfile.class); if (deviceProfile == null) { throw new RuntimeException("[{" + tenantId + "}] deviceProfileUpdateMsg {" + deviceProfileUpdateMsg + "} cannot be converted to device profile"); } @@ -86,7 +86,7 @@ public abstract class BaseDeviceProfileProcessor extends BaseEdgeProcessor { } catch (Exception e) { log.error("[{}] Failed to process device profile update msg [{}]", tenantId, deviceProfileUpdateMsg, e); throw e; - } finally { + } finally { deviceCreationLock.unlock(); } return Pair.of(created, deviceProfileNameUpdated); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java index 257cee336f..fb8db00890 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java @@ -102,7 +102,7 @@ public class DeviceEdgeProcessor extends BaseDeviceProcessor { try { edgeSynchronizationManager.getEdgeId().set(edgeId); - updateDeviceCredentials(tenantId, deviceCredentialsUpdateMsg, edgeVersion); + updateDeviceCredentials(tenantId, deviceCredentialsUpdateMsg, EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)); } finally { edgeSynchronizationManager.getEdgeId().remove(); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEvent.java b/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEvent.java index 3688f5c6c2..1baf707040 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEvent.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEvent.java @@ -31,6 +31,8 @@ import java.util.UUID; @ToString(callSuper = true) public class EdgeEvent extends BaseData { + private static final long serialVersionUID = 5548866356798094088L; + private long seqId; private TenantId tenantId; private EdgeId edgeId; From 72ae6eb70848646e6afb3c5994afb7c824eba51f Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Thu, 16 Nov 2023 15:13:00 +0200 Subject: [PATCH 146/252] Version control support for resources --- .../main/data/upgrade/3.6.1/schema_update.sql | 1 + .../server/controller/ImageController.java | 10 +-- .../controller/TbResourceController.java | 25 ++++++ .../DefaultExportableEntitiesService.java | 10 +++ .../exporting/ExportableEntitiesService.java | 2 + .../exporting/impl/ResourceExportService.java | 36 ++++++++ .../importing/impl/ResourceImportService.java | 83 +++++++++++++++++++ .../impl/WidgetTypeImportService.java | 4 - .../DefaultEntitiesVersionControlService.java | 6 +- .../server/dao/resource/ResourceService.java | 2 +- .../server/common/data/TbResource.java | 3 - .../server/common/data/TbResourceInfo.java | 28 +++++-- .../server/common/data/sync/JsonTbEntity.java | 4 +- .../server/dao/ExportableEntityDao.java | 6 +- .../dao/model/sql/TbResourceEntity.java | 6 ++ .../dao/model/sql/TbResourceInfoEntity.java | 6 ++ .../server/dao/resource/BaseImageService.java | 2 +- .../dao/resource/BaseResourceService.java | 8 +- .../server/dao/resource/TbResourceDao.java | 3 +- .../dao/sql/resource/JpaTbResourceDao.java | 21 +++++ .../resource/TbResourceInfoRepository.java | 4 +- .../sql/resource/TbResourceRepository.java | 9 +- .../dao/sql/widget/JpaWidgetTypeDao.java | 8 +- .../dao/sql/widget/WidgetTypeRepository.java | 5 +- .../main/resources/sql/schema-entities.sql | 1 + 25 files changed, 259 insertions(+), 34 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/ResourceExportService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/ResourceImportService.java diff --git a/application/src/main/data/upgrade/3.6.1/schema_update.sql b/application/src/main/data/upgrade/3.6.1/schema_update.sql index c19ea2e4e8..27c7adf523 100644 --- a/application/src/main/data/upgrade/3.6.1/schema_update.sql +++ b/application/src/main/data/upgrade/3.6.1/schema_update.sql @@ -30,5 +30,6 @@ $$; ALTER TABLE resource ADD COLUMN IF NOT EXISTS descriptor varchar, ADD COLUMN IF NOT EXISTS preview bytea; +ALTER TABLE resource ADD COLUMN IF NOT EXISTS external_id uuid -- RESOURCES UPDATE END diff --git a/application/src/main/java/org/thingsboard/server/controller/ImageController.java b/application/src/main/java/org/thingsboard/server/controller/ImageController.java index add38ee272..98e7a3a4e8 100644 --- a/application/src/main/java/org/thingsboard/server/controller/ImageController.java +++ b/application/src/main/java/org/thingsboard/server/controller/ImageController.java @@ -35,7 +35,6 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; -import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.ImageDescriptor; import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.TbResource; @@ -88,7 +87,7 @@ public class ImageController extends BaseController { image.setResourceType(ResourceType.IMAGE); ImageDescriptor descriptor = new ImageDescriptor(); descriptor.setMediaType(file.getContentType()); - image.setDescriptor(JacksonUtil.valueToTree(descriptor)); + image.setDescriptorValue(descriptor); image.setData(file.getBytes()); return tbImageService.save(image, user); } @@ -99,11 +98,12 @@ public class ImageController extends BaseController { @PathVariable String key, @RequestPart MultipartFile file) throws Exception { TbResourceInfo imageInfo = checkImageInfo(type, key, Operation.WRITE); - ImageDescriptor descriptor = imageInfo.getDescriptor(ImageDescriptor.class); - TbResource image = new TbResource(imageInfo); image.setData(file.getBytes()); - descriptor.setMediaType(file.getContentType()); + image.updateDescriptor(ImageDescriptor.class, descriptor -> { + descriptor.setMediaType(file.getContentType()); + return descriptor; + }); return tbImageService.save(image, getCurrentUser()); } diff --git a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java index b8abaab0ae..c5445e4465 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java @@ -53,6 +53,7 @@ import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; import java.util.Collections; +import java.util.EnumSet; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -226,6 +227,30 @@ public class TbResourceController extends BaseController { } } + @ApiOperation(value = "Get All Resource Infos (getAllResources)", + notes = "Returns a page of Resource Info objects owned by tenant. " + + PAGE_DATA_PARAMETERS + RESOURCE_INFO_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH, + produces = "application/json") + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") + @GetMapping(value = "/resource/tenant") + public PageData getTenantResources(@ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true) + @RequestParam int pageSize, + @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true) + @RequestParam int page, + @ApiParam(value = RESOURCE_TEXT_SEARCH_DESCRIPTION) + @RequestParam(required = false) String textSearch, + @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = RESOURCE_SORT_PROPERTY_ALLOWABLE_VALUES) + @RequestParam(required = false) String sortProperty, + @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) + @RequestParam(required = false) String sortOrder) throws ThingsboardException { + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + TbResourceInfoFilter filter = TbResourceInfoFilter.builder() + .tenantId(getTenantId()) + .resourceTypes(EnumSet.allOf(ResourceType.class)) + .build(); + return checkNotNull(resourceService.findTenantResourcesByTenantId(filter, pageLink)); + } + @ApiOperation(value = "Get LwM2M Objects (getLwm2mListObjectsPage)", notes = "Returns a page of LwM2M objects parsed from Resources with type 'LWM2M_MODEL' owned by tenant or sysadmin. " + PAGE_DATA_PARAMETERS + LWM2M_OBJECT_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH, diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/DefaultExportableEntitiesService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/DefaultExportableEntitiesService.java index 3358e5182f..d112075a51 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/DefaultExportableEntitiesService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/DefaultExportableEntitiesService.java @@ -118,6 +118,16 @@ public class DefaultExportableEntitiesService implements ExportableEntitiesServi } } + @Override + public PageData findEntitiesIdsByTenantId(TenantId tenantId, EntityType entityType, PageLink pageLink) { + ExportableEntityDao dao = getExportableEntityDao(entityType); + if (dao != null) { + return dao.findIdsByTenantId(tenantId.getId(), pageLink); + } else { + return new PageData<>(); + } + } + @Override public I getExternalIdByInternal(I internalId) { ExportableEntityDao dao = getExportableEntityDao(internalId.getEntityType()); diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/ExportableEntitiesService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/ExportableEntitiesService.java index c5c26c4476..eee102961d 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/ExportableEntitiesService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/ExportableEntitiesService.java @@ -35,6 +35,8 @@ public interface ExportableEntitiesService { , I extends EntityId> PageData findEntitiesByTenantId(TenantId tenantId, EntityType entityType, PageLink pageLink); + PageData findEntitiesIdsByTenantId(TenantId tenantId, EntityType entityType, PageLink pageLink); + I getExternalIdByInternal(I internalId); void removeById(TenantId tenantId, I id); diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/ResourceExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/ResourceExportService.java new file mode 100644 index 0000000000..45e21f5fb4 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/ResourceExportService.java @@ -0,0 +1,36 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.sync.ie.exporting.impl; + +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.common.data.id.TbResourceId; +import org.thingsboard.server.common.data.sync.ie.EntityExportData; +import org.thingsboard.server.queue.util.TbCoreComponent; + +import java.util.Set; + +@Service +@TbCoreComponent +public class ResourceExportService extends BaseEntityExportService> { + + @Override + public Set getSupportedEntityTypes() { + return Set.of(EntityType.TB_RESOURCE); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/ResourceImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/ResourceImportService.java new file mode 100644 index 0000000000..bc7db18e80 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/ResourceImportService.java @@ -0,0 +1,83 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.sync.ie.importing.impl; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.TbResourceId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.sync.ie.EntityExportData; +import org.thingsboard.server.dao.resource.ResourceService; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.sync.vc.data.EntitiesImportCtx; + +@Service +@TbCoreComponent +@RequiredArgsConstructor +public class ResourceImportService extends BaseEntityImportService> { + + private final ResourceService resourceService; + + @Override + protected void setOwner(TenantId tenantId, TbResource resource, IdProvider idProvider) { + resource.setTenantId(tenantId); + } + + @Override + protected TbResource prepare(EntitiesImportCtx ctx, TbResource resource, TbResource oldResource, EntityExportData exportData, IdProvider idProvider) { + return resource; + } + + @Override + protected TbResource findExistingEntity(EntitiesImportCtx ctx, TbResource resource, IdProvider idProvider) { + TbResource existingResource = super.findExistingEntity(ctx, resource, idProvider); + if (existingResource == null && ctx.isFindExistingByName()) { + existingResource = resourceService.findResourceByTenantIdAndKey(ctx.getTenantId(), resource.getResourceType(), resource.getResourceKey()); + } + return existingResource; + } + + @Override + protected boolean compare(EntitiesImportCtx ctx, EntityExportData exportData, TbResource prepared, TbResource existing) { + return true; + } + + @Override + protected TbResource deepCopy(TbResource resource) { + return new TbResource(resource); + } + + @Override + protected TbResource saveOrUpdate(EntitiesImportCtx ctx, TbResource resource, EntityExportData exportData, IdProvider idProvider) { + return resourceService.saveResource(resource); + } + + @Override + protected void onEntitySaved(User user, TbResource savedResource, TbResource oldResource) throws ThingsboardException { + super.onEntitySaved(user, savedResource, oldResource); + clusterService.onResourceChange(savedResource, null); + } + + @Override + public EntityType getEntityType() { + return EntityType.TB_RESOURCE; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/WidgetTypeImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/WidgetTypeImportService.java index 88ebbbcee3..78cd9a66f6 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/WidgetTypeImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/WidgetTypeImportService.java @@ -20,13 +20,9 @@ import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.WidgetTypeId; -import org.thingsboard.server.common.data.id.WidgetsBundleId; import org.thingsboard.server.common.data.sync.ie.WidgetTypeExportData; -import org.thingsboard.server.common.data.sync.ie.WidgetsBundleExportData; import org.thingsboard.server.common.data.widget.WidgetTypeDetails; -import org.thingsboard.server.common.data.widget.WidgetsBundle; import org.thingsboard.server.dao.widget.WidgetTypeService; -import org.thingsboard.server.dao.widget.WidgetsBundleService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.sync.vc.data.EntitiesImportCtx; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java index 89d5153a81..f98a2ad780 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java @@ -208,10 +208,10 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont } if (config.isAllEntities()) { - DaoUtil.processInBatches(pageLink -> exportableEntitiesService.findEntitiesByTenantId(ctx.getTenantId(), entityType, pageLink) - , 100, entity -> { + DaoUtil.processInBatches(pageLink -> exportableEntitiesService.findEntitiesIdsByTenantId(ctx.getTenantId(), entityType, pageLink), + 100, entityId -> { try { - ctx.add(saveEntityData(ctx, entity.getId())); + ctx.add(saveEntityData(ctx, entityId)); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java index 7240b1b742..21b83d17bd 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java @@ -34,7 +34,7 @@ public interface ResourceService extends EntityDaoService { TbResource saveResource(TbResource resource, boolean doValidate); - TbResource findResourceByTenantIdAndKey(TenantId tenantId, ResourceType resourceType, String resourceId); + TbResource findResourceByTenantIdAndKey(TenantId tenantId, ResourceType resourceType, String resourceKey); TbResource findResourceById(TenantId tenantId, TbResourceId resourceId); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/TbResource.java b/common/data/src/main/java/org/thingsboard/server/common/data/TbResource.java index c76d2b5c2d..2324eae1a7 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/TbResource.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/TbResource.java @@ -16,7 +16,6 @@ package org.thingsboard.server.common.data; import com.fasterxml.jackson.annotation.JsonGetter; -import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonSetter; import io.swagger.annotations.ApiModelProperty; import lombok.Data; @@ -34,10 +33,8 @@ public class TbResource extends TbResourceInfo { private static final long serialVersionUID = 7379609705527272306L; - @JsonIgnore private byte[] data; - @JsonIgnore private byte[] preview; public TbResource() { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java index 31ef6b8ea1..11e21c6451 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java @@ -19,7 +19,6 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; @@ -30,11 +29,13 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; +import java.util.function.UnaryOperator; + @ApiModel @Slf4j @Data @EqualsAndHashCode(callSuper = true) -public class TbResourceInfo extends BaseData implements HasName, HasTenantId { +public class TbResourceInfo extends BaseData implements HasName, HasTenantId, ExportableEntity { private static final long serialVersionUID = 7282664529021651736L; @@ -52,15 +53,17 @@ public class TbResourceInfo extends BaseData implements HasName, H private String resourceKey; @ApiModelProperty(position = 7, value = "Resource search text.", example = "19_1.0:binaryappdatacontainer", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private String searchText; + @ApiModelProperty(position = 8, value = "Resource etag.", example = "33a64df551425fcc55e4d42a148795d9f25f89d4", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private String etag; @NoXss @Length(fieldName = "file name") @ApiModelProperty(position = 9, value = "Resource file name.", example = "19.xml", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private String fileName; - @JsonProperty(access = JsonProperty.Access.READ_ONLY) private JsonNode descriptor; + private TbResourceId externalId; + public TbResourceInfo() { super(); } @@ -78,13 +81,14 @@ public class TbResourceInfo extends BaseData implements HasName, H this.searchText = resourceInfo.searchText; this.etag = resourceInfo.etag; this.fileName = resourceInfo.fileName; - this.descriptor = resourceInfo.descriptor; + this.descriptor = resourceInfo.descriptor != null ? resourceInfo.descriptor.deepCopy() : null; + this.externalId = resourceInfo.externalId; } @ApiModelProperty(position = 1, value = "JSON object with the Resource Id. " + "Specify this field to update the Resource. " + "Referencing non-existing Resource Id will cause error. " + - "Omit this field to create new Resource." ) + "Omit this field to create new Resource.") @Override public TbResourceId getId() { return super.getId(); @@ -97,7 +101,7 @@ public class TbResourceInfo extends BaseData implements HasName, H } @Override - @JsonIgnore + @JsonProperty(access = JsonProperty.Access.READ_ONLY) public String getName() { return title; } @@ -107,9 +111,19 @@ public class TbResourceInfo extends BaseData implements HasName, H return searchText != null ? searchText : title; } - @JsonIgnore public T getDescriptor(Class type) throws JsonProcessingException { return descriptor != null ? mapper.treeToValue(descriptor, type) : null; } + public void updateDescriptor(Class type, UnaryOperator updater) throws JsonProcessingException { + T descriptor = getDescriptor(type); + descriptor = updater.apply(descriptor); + setDescriptorValue(descriptor); + } + + @JsonIgnore + public void setDescriptorValue(Object value) { + this.descriptor = value != null ? mapper.valueToTree(value) : null; + } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/sync/JsonTbEntity.java b/common/data/src/main/java/org/thingsboard/server/common/data/sync/JsonTbEntity.java index 92c6a332df..10f20cc973 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/sync/JsonTbEntity.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/sync/JsonTbEntity.java @@ -25,6 +25,7 @@ import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EntityView; +import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.notification.rule.NotificationRule; @@ -56,7 +57,8 @@ import java.lang.annotation.Target; @Type(name = "WIDGET_TYPE", value = WidgetTypeDetails.class), @Type(name = "NOTIFICATION_TEMPLATE", value = NotificationTemplate.class), @Type(name = "NOTIFICATION_TARGET", value = NotificationTarget.class), - @Type(name = "NOTIFICATION_RULE", value = NotificationRule.class) + @Type(name = "NOTIFICATION_RULE", value = NotificationRule.class), + @Type(name = "TB_RESOURCE", value = TbResource.class) }) @JsonIgnoreProperties(value = {"tenantId", "createdTime"}, ignoreUnknown = true) public @interface JsonTbEntity { diff --git a/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityDao.java b/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityDao.java index 169d7c3b88..5ac75f94f7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityDao.java @@ -22,7 +22,7 @@ import org.thingsboard.server.common.data.page.PageLink; import java.util.UUID; -public interface ExportableEntityDao> extends Dao { +public interface ExportableEntityDao> extends Dao { T findByTenantIdAndExternalId(UUID tenantId, UUID externalId); @@ -30,6 +30,10 @@ public interface ExportableEntityDao findByTenantId(UUID tenantId, PageLink pageLink); + default PageData findIdsByTenantId(UUID tenantId, PageLink pageLink) { + return findByTenantId(tenantId, pageLink).mapData(ExportableEntity::getId); + } + I getExternalIdByInternal(I internalId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceEntity.java index 41c5ebc920..ff63994259 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceEntity.java @@ -32,6 +32,7 @@ import javax.persistence.Entity; import javax.persistence.Table; import java.util.UUID; +import static org.thingsboard.server.dao.model.ModelConstants.EXTERNAL_ID_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_DATA_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_DESCRIPTOR_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_ETAG_COLUMN; @@ -82,6 +83,9 @@ public class TbResourceEntity extends BaseSqlEntity { @Column(name = RESOURCE_PREVIEW_COLUMN) private byte[] preview; + @Column(name = EXTERNAL_ID_PROPERTY) + private UUID externalId; + public TbResourceEntity() { } @@ -102,6 +106,7 @@ public class TbResourceEntity extends BaseSqlEntity { this.etag = resource.getEtag(); this.descriptor = resource.getDescriptor(); this.preview = resource.getPreview(); + this.externalId = getUuid(resource.getExternalId()); } @Override @@ -118,6 +123,7 @@ public class TbResourceEntity extends BaseSqlEntity { resource.setEtag(etag); resource.setDescriptor(descriptor); resource.setPreview(preview); + resource.setExternalId(getEntityId(externalId, TbResourceId::new)); return resource; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceInfoEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceInfoEntity.java index 9ab5024b32..1244e25d4c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceInfoEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceInfoEntity.java @@ -33,6 +33,7 @@ import javax.persistence.Entity; import javax.persistence.Table; import java.util.UUID; +import static org.thingsboard.server.dao.model.ModelConstants.EXTERNAL_ID_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_DESCRIPTOR_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_ETAG_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_FILE_NAME_COLUMN; @@ -75,6 +76,9 @@ public class TbResourceInfoEntity extends BaseSqlEntity implemen @Column(name = RESOURCE_DESCRIPTOR_COLUMN) private JsonNode descriptor; + @Column(name = EXTERNAL_ID_PROPERTY) + private UUID externalId; + public TbResourceInfoEntity() { } @@ -91,6 +95,7 @@ public class TbResourceInfoEntity extends BaseSqlEntity implemen this.hashCode = resource.getEtag(); this.fileName = resource.getFileName(); this.descriptor = resource.getDescriptor(); + this.externalId = getUuid(resource.getExternalId()); } @Override @@ -105,6 +110,7 @@ public class TbResourceInfoEntity extends BaseSqlEntity implemen resource.setEtag(hashCode); resource.setFileName(fileName); resource.setDescriptor(descriptor); + resource.setExternalId(getEntityId(externalId, TbResourceId::new)); return resource; } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java index 778192b49c..857296b9b5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java @@ -59,7 +59,7 @@ public class BaseImageService extends BaseResourceService implements ImageServic Pair result = processImage(image.getData(), descriptor); descriptor = result.getLeft(); image.setEtag(descriptor.getEtag()); - image.setDescriptor(JacksonUtil.valueToTree(descriptor)); + image.setDescriptorValue(descriptor); image.setPreview(result.getRight()); return new TbResourceInfo(doSaveResource(image)); diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java index bb881e5dcd..9be95db9e7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java @@ -96,8 +96,7 @@ public class BaseResourceService extends AbstractCachedEntityService, TenantEntityWithDataDao { +public interface TbResourceDao extends Dao, TenantEntityWithDataDao, ExportableEntityDao { TbResource findResourceByTenantIdAndKey(TenantId tenantId, ResourceType resourceType, String resourceId); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceDao.java index 28bebf3e4f..370b21a90e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceDao.java @@ -109,6 +109,27 @@ public class JpaTbResourceDao extends JpaAbstractDao findByTenantId(UUID tenantId, PageLink pageLink) { + return findAllByTenantId(TenantId.fromUUID(tenantId), pageLink); + } + + @Override + public PageData findIdsByTenantId(UUID tenantId, PageLink pageLink) { + return DaoUtil.pageToPageData(resourceRepository.findIdsByTenantId(tenantId, DaoUtil.toPageable(pageLink)) + .map(TbResourceId::new)); + } + + @Override + public TbResourceId getExternalIdByInternal(TbResourceId internalId) { + return DaoUtil.toEntityId(resourceRepository.getExternalIdByInternal(internalId.getId()), TbResourceId::new); + } + @Override public EntityType getEntityType() { return EntityType.TB_RESOURCE; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceInfoRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceInfoRepository.java index 9c526ae321..0a0f7ff127 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceInfoRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceInfoRepository.java @@ -59,8 +59,8 @@ public interface TbResourceInfoRepository extends JpaRepository findKeysByTenantIdAndResourceTypeAndResourceKeyStartingWith(@Param("tenantId") UUID tenantId, - @Param("resourceType") String resourceType, - @Param("resourceKeyStartsWith") String resourceKeyStartsWith); + @Param("resourceType") String resourceType, + @Param("resourceKeyStartsWith") String resourceKeyStartsWith); List findByTenantIdAndHashCodeAndResourceKeyStartingWith(UUID tenantId, String hashCode, String query); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceRepository.java index 6a47e95ed6..b6d52909ff 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceRepository.java @@ -20,12 +20,13 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; +import org.thingsboard.server.dao.ExportableEntityRepository; import org.thingsboard.server.dao.model.sql.TbResourceEntity; import java.util.List; import java.util.UUID; -public interface TbResourceRepository extends JpaRepository { +public interface TbResourceRepository extends JpaRepository , ExportableEntityRepository { TbResourceEntity findByTenantIdAndResourceTypeAndResourceKey(UUID tenantId, String resourceType, String resourceKey); @@ -87,4 +88,10 @@ public interface TbResourceRepository extends JpaRepository findIdsByTenantId(@Param("tenantId") UUID tenantId, Pageable pageable); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDao.java index a821191629..f1878e1de8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDao.java @@ -121,7 +121,7 @@ public class JpaWidgetTypeDao extends JpaAbstractDao findTenantWidgetTypesByTenantId(UUID tenantId, boolean fullSearch, DeprecatedFilter deprecatedFilter, List widgetTypes, PageLink pageLink) { + public PageData findTenantWidgetTypesByTenantId(UUID tenantId, boolean fullSearch, DeprecatedFilter deprecatedFilter, List widgetTypes, PageLink pageLink) { boolean deprecatedFilterEnabled = !DeprecatedFilter.ALL.equals(deprecatedFilter); boolean deprecatedFilterBool = DeprecatedFilter.DEPRECATED.equals(deprecatedFilter); boolean widgetTypesEmpty = widgetTypes == null || widgetTypes.isEmpty(); @@ -204,6 +204,12 @@ public class JpaWidgetTypeDao extends JpaAbstractDao findIdsByTenantId(UUID tenantId, PageLink pageLink) { + return DaoUtil.pageToPageData(widgetTypeRepository.findIdsByTenantId(tenantId, DaoUtil.toPageable(pageLink)) + .map(WidgetTypeId::new)); + } + @Override public WidgetTypeId getExternalIdByInternal(WidgetTypeId internalId) { return Optional.ofNullable(widgetTypeRepository.getExternalIdById(internalId.getId())) diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetTypeRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetTypeRepository.java index d85f486a05..7536133643 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetTypeRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetTypeRepository.java @@ -76,7 +76,10 @@ public interface WidgetTypeRepository extends JpaRepository findAllIds(Pageable pageable); + @Query("SELECT id FROM WidgetTypeDetailsEntity WHERE tenantId = :tenantId") + Page findIdsByTenantId(@Param("tenantId") UUID tenantId, Pageable pageable); + } diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 6b30151536..35d1d49cda 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -718,6 +718,7 @@ CREATE TABLE IF NOT EXISTS resource ( etag varchar, descriptor varchar, preview bytea, + external_id uuid, CONSTRAINT resource_unq_key UNIQUE (tenant_id, resource_type, resource_key) ); From 3d52cc972457e6163c8e9278f59ec35f0c84314b Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Thu, 16 Nov 2023 15:13:36 +0200 Subject: [PATCH 147/252] UI for resources version control --- ui-ngx/src/app/core/http/entity.service.ts | 8 +++++++- ui-ngx/src/app/core/http/resource.service.ts | 4 ++++ ui-ngx/src/app/shared/models/entity-type.models.ts | 2 ++ ui-ngx/src/app/shared/models/resource.models.ts | 4 ++-- ui-ngx/src/app/shared/models/vc.models.ts | 4 +++- ui-ngx/src/assets/locale/locale.constant-en_US.json | 2 ++ 6 files changed, 20 insertions(+), 4 deletions(-) diff --git a/ui-ngx/src/app/core/http/entity.service.ts b/ui-ngx/src/app/core/http/entity.service.ts index 8bc8bfe3f2..968329d36a 100644 --- a/ui-ngx/src/app/core/http/entity.service.ts +++ b/ui-ngx/src/app/core/http/entity.service.ts @@ -95,6 +95,7 @@ import { TenantProfileService } from '@core/http/tenant-profile.service'; import { NotificationType } from '@shared/models/notification.models'; import { UserId } from '@shared/models/id/user-id'; import { AlarmService } from '@core/http/alarm.service'; +import { ResourceService } from '@core/http/resource.service'; @Injectable({ providedIn: 'root' @@ -123,7 +124,8 @@ export class EntityService { private utils: UtilsService, private queueService: QueueService, private notificationService: NotificationService, - private alarmService: AlarmService + private alarmService: AlarmService, + private resourceService: ResourceService ) { } private getEntityObservable(entityType: EntityType, entityId: string, @@ -438,6 +440,10 @@ export class EntityService { pageLink.sortOrder.property = 'name'; entitiesObservable = this.notificationService.getNotificationRules(pageLink, config); break; + case EntityType.TB_RESOURCE: + pageLink.sortOrder.property = 'title'; + entitiesObservable = this.resourceService.getTenantResources(pageLink, config); + break; } return entitiesObservable; } diff --git a/ui-ngx/src/app/core/http/resource.service.ts b/ui-ngx/src/app/core/http/resource.service.ts index f7cb8441d1..d6820f93cb 100644 --- a/ui-ngx/src/app/core/http/resource.service.ts +++ b/ui-ngx/src/app/core/http/resource.service.ts @@ -42,6 +42,10 @@ export class ResourceService { return this.http.get>(url, defaultHttpOptionsFromConfig(config)); } + public getTenantResources(pageLink: PageLink, config?: RequestConfig): Observable> { + return this.http.get>(`/api/resource/tenant${pageLink.toQuery()}`, defaultHttpOptionsFromConfig(config)); + } + public getResource(resourceId: string, config?: RequestConfig): Observable { return this.http.get(`/api/resource/${resourceId}`, defaultHttpOptionsFromConfig(config)); } 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 5c9390f80f..3c3151afb9 100644 --- a/ui-ngx/src/app/shared/models/entity-type.models.ts +++ b/ui-ngx/src/app/shared/models/entity-type.models.ts @@ -326,6 +326,8 @@ export const entityTypeTranslations = new Map( ] ); -export interface ResourceInfo extends Omit, 'name' | 'label'> { +export interface ResourceInfo extends Omit, 'name' | 'label'>, ExportableEntity { tenantId?: TenantId; resourceKey?: string; title?: string; diff --git a/ui-ngx/src/app/shared/models/vc.models.ts b/ui-ngx/src/app/shared/models/vc.models.ts index 5228af05b0..b682b79600 100644 --- a/ui-ngx/src/app/shared/models/vc.models.ts +++ b/ui-ngx/src/app/shared/models/vc.models.ts @@ -32,6 +32,7 @@ export const exportableEntityTypes: Array = [ EntityType.RULE_CHAIN, EntityType.WIDGET_TYPE, EntityType.WIDGETS_BUNDLE, + EntityType.TB_RESOURCE, EntityType.NOTIFICATION_TEMPLATE, EntityType.NOTIFICATION_TARGET, EntityType.NOTIFICATION_RULE @@ -40,7 +41,8 @@ export const exportableEntityTypes: Array = [ export const entityTypesWithoutRelatedData: Set = new Set([ EntityType.NOTIFICATION_TEMPLATE, EntityType.NOTIFICATION_TARGET, - EntityType.NOTIFICATION_RULE + EntityType.NOTIFICATION_RULE, + EntityType.TB_RESOURCE ]); export interface VersionCreateConfig { 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 08a7236400..0eda3333d8 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2198,6 +2198,8 @@ "list-of-edges": "{ count, plural, =1 {One edge} other {List of # edges} }", "edge-name-starts-with": "Edges whose names start with '{{prefix}}'", "type-tb-resource": "Resource", + "type-tb-resources": "Resources", + "list-of-tb-resources": "{ count, plural, =1 {One resource} other {List of # resources} }", "type-ota-package": "OTA package", "type-rpc": "RPC", "type-queue": "Queue", From 80fc2d9b862f09b900fb3e0d2b7ce71ba98759a7 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Thu, 16 Nov 2023 15:06:00 +0100 Subject: [PATCH 148/252] improvements --- .../server/actors/tenant/TenantActor.java | 11 +++++++---- .../server/service/queue/ProtoUtils.java | 16 ++++++++-------- common/cluster-api/src/main/proto/queue.proto | 4 ++-- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java b/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java index f508d967c9..86f338e89d 100644 --- a/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java +++ b/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java @@ -228,7 +228,7 @@ public class TenantActor extends RuleChainManagerActor { log.warn("RECEIVED INVALID MESSAGE: {}", msg); } if (deletedDevices.contains(msg.getDeviceId())) { - log.warn("RECEIVED MESSAGE FOR DELETED DEVICE: {}", msg); + log.debug("RECEIVED MESSAGE FOR DELETED DEVICE: {}", msg); return; } TbActorRef deviceActor = getOrCreateDeviceActor(msg.getDeviceId()); @@ -250,7 +250,8 @@ public class TenantActor extends RuleChainManagerActor { log.info("[{}] Received API state update. Going to ENABLE Rule Engine execution.", tenantId); initRuleChains(); } - } else if (msg.getEntityId().getEntityType() == EntityType.EDGE) { + } + if (msg.getEntityId().getEntityType() == EntityType.EDGE) { EdgeId edgeId = new EdgeId(msg.getEntityId().getId()); EdgeRpcService edgeRpcService = systemContext.getEdgeRpcService(); if (msg.getEvent() == ComponentLifecycleEvent.DELETED) { @@ -259,11 +260,13 @@ public class TenantActor extends RuleChainManagerActor { Edge edge = systemContext.getEdgeService().findEdgeById(tenantId, edgeId); edgeRpcService.updateEdge(tenantId, edge); } - } else if (msg.getEntityId().getEntityType() == EntityType.DEVICE && ComponentLifecycleEvent.DELETED == msg.getEvent()) { + } + if (msg.getEntityId().getEntityType() == EntityType.DEVICE && ComponentLifecycleEvent.DELETED == msg.getEvent()) { DeviceId deviceId = (DeviceId) msg.getEntityId(); onToDeviceActorMsg(new DeviceDeleteMsg(tenantId, deviceId), true); deletedDevices.add(deviceId); - } else if (isRuleEngine) { + } + if (isRuleEngine) { TbActorRef target = getEntityActorRef(msg.getEntityId()); if (target != null) { if (msg.getEntityId().getEntityType() == EntityType.RULE_CHAIN) { diff --git a/application/src/main/java/org/thingsboard/server/service/queue/ProtoUtils.java b/application/src/main/java/org/thingsboard/server/service/queue/ProtoUtils.java index 8986ca8b51..c93af1bb07 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/ProtoUtils.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/ProtoUtils.java @@ -44,9 +44,9 @@ import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponseActorMsg; import org.thingsboard.server.common.msg.rpc.RemoveRpcActorMsg; import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequestActorMsg; -import org.thingsboard.server.common.msg.rule.engine.DeviceDeleteMsg; import org.thingsboard.server.common.msg.rule.engine.DeviceAttributesEventNotificationMsg; import org.thingsboard.server.common.msg.rule.engine.DeviceCredentialsUpdateNotificationMsg; +import org.thingsboard.server.common.msg.rule.engine.DeviceDeleteMsg; import org.thingsboard.server.common.msg.rule.engine.DeviceEdgeUpdateMsg; import org.thingsboard.server.common.msg.rule.engine.DeviceNameOrTypeUpdateMsg; import org.thingsboard.server.gen.transport.TransportProtos; @@ -378,8 +378,8 @@ public class ProtoUtils { ); } - private static TransportProtos.DeviceDeletedMsgProto toProto(DeviceDeleteMsg msg) { - return TransportProtos.DeviceDeletedMsgProto.newBuilder() + private static TransportProtos.DeviceDeleteMsgProto toProto(DeviceDeleteMsg msg) { + return TransportProtos.DeviceDeleteMsgProto.newBuilder() .setTenantIdMSB(msg.getTenantId().getId().getMostSignificantBits()) .setTenantIdLSB(msg.getTenantId().getId().getLeastSignificantBits()) .setDeviceIdMSB(msg.getDeviceId().getId().getMostSignificantBits()) @@ -387,7 +387,7 @@ public class ProtoUtils { .build(); } - private static DeviceDeleteMsg fromProto(TransportProtos.DeviceDeletedMsgProto proto) { + private static DeviceDeleteMsg fromProto(TransportProtos.DeviceDeleteMsgProto proto) { return new DeviceDeleteMsg( TenantId.fromUUID(new UUID(proto.getTenantIdMSB(), proto.getTenantIdLSB())), new DeviceId(new UUID(proto.getDeviceIdMSB(), proto.getDeviceIdLSB()))); @@ -424,8 +424,8 @@ public class ProtoUtils { return TransportProtos.ToDeviceActorNotificationMsgProto.newBuilder().setRemoveRpcActorMsg(proto).build(); } else if (msg instanceof DeviceDeleteMsg) { DeviceDeleteMsg updateMsg = (DeviceDeleteMsg) msg; - TransportProtos.DeviceDeletedMsgProto proto = toProto(updateMsg); - return TransportProtos.ToDeviceActorNotificationMsgProto.newBuilder().setDeviceAssignToTenantMsgProto(proto).build(); + TransportProtos.DeviceDeleteMsgProto proto = toProto(updateMsg); + return TransportProtos.ToDeviceActorNotificationMsgProto.newBuilder().setDeviceDeleteMsg(proto).build(); } return null; } @@ -445,8 +445,8 @@ public class ProtoUtils { return fromProto(proto.getFromDeviceRpcResponseMsg()); } else if (proto.hasRemoveRpcActorMsg()) { return fromProto(proto.getRemoveRpcActorMsg()); - } else if (proto.hasDeviceAssignToTenantMsgProto()) { - return fromProto(proto.getDeviceAssignToTenantMsgProto()); + } else if (proto.hasDeviceDeleteMsg()) { + return fromProto(proto.getDeviceDeleteMsg()); } return null; } diff --git a/common/cluster-api/src/main/proto/queue.proto b/common/cluster-api/src/main/proto/queue.proto index 5000e3025f..c8919cb5c7 100644 --- a/common/cluster-api/src/main/proto/queue.proto +++ b/common/cluster-api/src/main/proto/queue.proto @@ -1004,7 +1004,7 @@ message RemoveRpcActorMsgProto { int64 deviceIdLSB = 6; } -message DeviceDeletedMsgProto { +message DeviceDeleteMsgProto { int64 tenantIdMSB = 1; int64 tenantIdLSB = 2; int64 deviceIdMSB = 3; @@ -1019,7 +1019,7 @@ message ToDeviceActorNotificationMsgProto { ToDeviceRpcRequestActorMsgProto toDeviceRpcRequestMsg = 5; FromDeviceRpcResponseActorMsgProto fromDeviceRpcResponseMsg = 6; RemoveRpcActorMsgProto removeRpcActorMsg = 7; - DeviceDeletedMsgProto deviceAssignToTenantMsgProto = 8; + DeviceDeleteMsgProto deviceDeleteMsg = 8; } /** From 1be39aa9aa74c574432cd4bc5980a837f578ecf7 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Thu, 16 Nov 2023 16:16:38 +0200 Subject: [PATCH 149/252] fixed license, env name refactoring --- .../PubSubRuleNodeExecutorProvider.java | 23 +++++++++++++++++++ .../src/main/resources/thingsboard.yml | 2 +- .../pubsub/TbPubSubQueueExecutorProvider.java | 23 +++++++++++++++++++ .../common/util/ExecutorProvider.java | 15 ++++++++++++ .../src/main/resources/tb-vc-executor.yml | 2 +- .../src/main/resources/tb-coap-transport.yml | 2 +- .../src/main/resources/tb-http-transport.yml | 2 +- .../src/main/resources/tb-lwm2m-transport.yml | 2 +- .../src/main/resources/tb-mqtt-transport.yml | 2 +- .../src/main/resources/tb-snmp-transport.yml | 2 +- 10 files changed, 68 insertions(+), 7 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/executors/PubSubRuleNodeExecutorProvider.java b/application/src/main/java/org/thingsboard/server/service/executors/PubSubRuleNodeExecutorProvider.java index 7db74f6195..f730949f45 100644 --- a/application/src/main/java/org/thingsboard/server/service/executors/PubSubRuleNodeExecutorProvider.java +++ b/application/src/main/java/org/thingsboard/server/service/executors/PubSubRuleNodeExecutorProvider.java @@ -1,3 +1,18 @@ +/** + * Copyright © 2016-2023 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. + */ package org.thingsboard.server.service.executors; import org.springframework.stereotype.Component; @@ -5,6 +20,7 @@ import org.thingsboard.common.util.ExecutorProvider; import org.thingsboard.common.util.ThingsBoardThreadFactory; import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -26,4 +42,11 @@ public class PubSubRuleNodeExecutorProvider implements ExecutorProvider { public ScheduledExecutorService getExecutor() { return executor; } + + @PreDestroy + private void destroy() { + if (executor != null) { + executor.shutdown(); + } + } } diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index ccc4953793..802756c105 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -1412,7 +1412,7 @@ queue: # Number of messages per consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" # Number of threads of pubsub executor provider - executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_SIZE:}" + executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:}" queue-properties: # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consumed again rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubQueueExecutorProvider.java b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubQueueExecutorProvider.java index 2b636b5ee2..4de75faa56 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubQueueExecutorProvider.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubQueueExecutorProvider.java @@ -1,3 +1,18 @@ +/** + * Copyright © 2016-2023 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. + */ package org.thingsboard.server.queue.pubsub; import org.springframework.beans.factory.annotation.Value; @@ -7,6 +22,7 @@ import org.thingsboard.common.util.ExecutorProvider; import org.thingsboard.common.util.ThingsBoardThreadFactory; import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -34,4 +50,11 @@ public class TbPubSubQueueExecutorProvider implements ExecutorProvider { public ScheduledExecutorService getExecutor() { return executor; } + + @PreDestroy + private void destroy() { + if (executor != null) { + executor.shutdown(); + } + } } diff --git a/common/util/src/main/java/org/thingsboard/common/util/ExecutorProvider.java b/common/util/src/main/java/org/thingsboard/common/util/ExecutorProvider.java index 4abc46fb16..61379c8c89 100644 --- a/common/util/src/main/java/org/thingsboard/common/util/ExecutorProvider.java +++ b/common/util/src/main/java/org/thingsboard/common/util/ExecutorProvider.java @@ -1,3 +1,18 @@ +/** + * Copyright © 2016-2023 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. + */ package org.thingsboard.common.util; import java.util.concurrent.ScheduledExecutorService; diff --git a/msa/vc-executor/src/main/resources/tb-vc-executor.yml b/msa/vc-executor/src/main/resources/tb-vc-executor.yml index 2353199c2b..9fc9b884f0 100644 --- a/msa/vc-executor/src/main/resources/tb-vc-executor.yml +++ b/msa/vc-executor/src/main/resources/tb-vc-executor.yml @@ -173,7 +173,7 @@ queue: # Number of messages per consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" # Number of threads of pubsub executor provider - executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_SIZE:}" + executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:}" queue-properties: # Pub/Sub properties for Core subscribers, messages which will commit after ackDeadlineInSec period can be consumed again core: "${TB_QUEUE_PUBSUB_CORE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" diff --git a/transport/coap/src/main/resources/tb-coap-transport.yml b/transport/coap/src/main/resources/tb-coap-transport.yml index 5588384efd..27e5c8b273 100644 --- a/transport/coap/src/main/resources/tb-coap-transport.yml +++ b/transport/coap/src/main/resources/tb-coap-transport.yml @@ -296,7 +296,7 @@ queue: # Number of messages per consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" # Number of threads of pubsub executor provider - executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_SIZE:}" + executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:}" queue-properties: # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consumed again rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" diff --git a/transport/http/src/main/resources/tb-http-transport.yml b/transport/http/src/main/resources/tb-http-transport.yml index 2ca2a04d1f..b8070f6575 100644 --- a/transport/http/src/main/resources/tb-http-transport.yml +++ b/transport/http/src/main/resources/tb-http-transport.yml @@ -279,7 +279,7 @@ queue: # Number of messages per a consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" # Number of threads of pubsub executor provider - executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_SIZE:}" + executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:}" queue-properties: # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consume again rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" diff --git a/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml b/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml index d8ee8d14c4..b1d40c720f 100644 --- a/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml +++ b/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml @@ -375,7 +375,7 @@ queue: # Number of messages per consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" # Number of threads of pubsub executor provider - executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_SIZE:}" + executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:}" queue-properties: # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consumed again rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" diff --git a/transport/mqtt/src/main/resources/tb-mqtt-transport.yml b/transport/mqtt/src/main/resources/tb-mqtt-transport.yml index 6dc1213884..4d130eecd9 100644 --- a/transport/mqtt/src/main/resources/tb-mqtt-transport.yml +++ b/transport/mqtt/src/main/resources/tb-mqtt-transport.yml @@ -312,7 +312,7 @@ queue: # Number of messages per consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" # Number of threads of pubsub executor provider - executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_SIZE:}" + executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:}" queue-properties: # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consumed again rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" diff --git a/transport/snmp/src/main/resources/tb-snmp-transport.yml b/transport/snmp/src/main/resources/tb-snmp-transport.yml index 928c3c0642..2b89065a55 100644 --- a/transport/snmp/src/main/resources/tb-snmp-transport.yml +++ b/transport/snmp/src/main/resources/tb-snmp-transport.yml @@ -265,7 +265,7 @@ queue: # Number of messages per consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" # Number of threads of pubsub executor provider - executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_SIZE:}" + executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:}" queue-properties: # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consumed again rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" From 28254d651ac00e9de493a365080c622c207fea8c Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 16 Nov 2023 17:42:02 +0200 Subject: [PATCH 150/252] UI: Range chart widget. --- .../json/system/widget_bundles/charts.json | 1 + .../json/system/widget_types/doughnut.json | 2 +- .../widget_types/horizontal_doughnut.json | 2 +- .../json/system/widget_types/range_chart.json | 31 ++ .../basic/basic-widget-config.module.ts | 12 +- .../value-card-basic-config.component.html | 1 + .../doughnut-basic-config.component.html | 1 + .../range-chart-basic-config.component.html | 222 ++++++++ .../range-chart-basic-config.component.ts | 269 +++++++++ .../chart/range-chart-widget.component.html | 34 ++ .../chart/range-chart-widget.component.scss | 105 ++++ .../lib/chart/range-chart-widget.component.ts | 527 ++++++++++++++++++ .../lib/chart/range-chart-widget.models.ts | 103 ++++ ...range-chart-widget-settings.component.html | 140 +++++ .../range-chart-widget-settings.component.ts | 147 +++++ .../common/color-range-settings.component.ts | 4 +- .../common/date-format-select.component.ts | 15 +- .../lib/settings/widget-settings.module.ts | 12 +- .../widget/widget-components.module.ts | 7 +- .../shared/models/widget-settings.models.ts | 77 ++- ui-ngx/src/app/shared/models/widget.models.ts | 2 + .../assets/locale/locale.constant-en_US.json | 14 + 22 files changed, 1709 insertions(+), 19 deletions(-) create mode 100644 application/src/main/data/json/system/widget_types/range_chart.json create mode 100644 ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.ts diff --git a/application/src/main/data/json/system/widget_bundles/charts.json b/application/src/main/data/json/system/widget_bundles/charts.json index 0829c612d0..14c09d3fc4 100644 --- a/application/src/main/data/json/system/widget_bundles/charts.json +++ b/application/src/main/data/json/system/widget_bundles/charts.json @@ -11,6 +11,7 @@ "widgetTypeFqns": [ "charts.basic_timeseries", "charts.state_chart", + "range_chart", "charts.timeseries_bars_flot", "cards.aggregated_value_card", "charts.bars", diff --git a/application/src/main/data/json/system/widget_types/doughnut.json b/application/src/main/data/json/system/widget_types/doughnut.json index f5a890ad3a..8764bbcace 100644 --- a/application/src/main/data/json/system/widget_types/doughnut.json +++ b/application/src/main/data/json/system/widget_types/doughnut.json @@ -2,7 +2,7 @@ "fqn": "doughnut", "name": "Doughnut", "deprecated": false, - "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMkAAACgCAMAAACR47ilAAABrVBMVEUNDQ0AAAD+/v7+/v7u7u7bycvX19f///8Ihyv/X2r//v7z8/P/kJjn5+f5+fkhISHs7Oz9/f3w8PD19fX39/fu7u7CwsL7+/v8/PyDw5XOzs62trb/2dvV1dUnlkX6+vrg8OWrq6v/9vfB4coYjjj09PTr6+uenp7o6OjJyMjv9/Ki0q/y8vLq6uq8vLzc29tltHrh4eGTy6L/6+3/2t3Qz8//8vPq6emQkJD/+vrj4+PHx8f/h4//c31FpWA9PT3//Pza2tr/19qy2b3/r7RYWFj/4uRGpWD/m6L/gopVrG1tbW3/6er/5ef/3+H/3d//aXNVVVX/w8dktHv/TVovLy/w9/KxsLD/paz/3N50u4iGhobR6dfQ6Nf/ur6SkpJ0vId5eXk2nlNKSkr/7e7/sbf/jJX/fYY2nVL/hI50dHRmZmbh4OD109W7urqCgoL/zdHMsLLOwcLcv8HDp6ikpKS8o6T/g4xhYWEXjjj/zdDnxcfBr7A8PDzp3d7b09T27e7f7+Pn0tThx8nSt7r4pat/v5GRkZFutoK728TCuruOxZ22m5ykl5hWrG17FlNEAAAAB3RSTlMGANDBIv4mEkvyGAAADi1JREFUeNrEmolT00AUxj3naZZcmzRNG0tpsQeltWrRKgURlYr3NXgrKgoq4n3ft+P1P/uSSBMbMFnd6jeDBmdk+pvvfe/tvrBkxXLouATTTOuaIz0dT5uqqgrAqt/+j2Urli5ZBh2Xasb1HKUxW7ScQxwpbZqqIAA/LV+6BDouVU1rRMnHKFUqYrGu4APRdDTHdoab/gGJIKR1mi8L4EjSqCJW8rY3uh7HOgMu6ihJ6Vx/4tix/h7B1EmeQPb8+Piup02wpZeRRomVscxMgUuRdYykZ+Ox7lWuut+ktRiF2vBqVzPjR22cuKaI6ExOl+I8AtMRktLOY2tX+dQtEcUEBPE0PP40izCkXlcoQRaTA8sSviSIsdvB8OtzWYHbq9vVdRQAtHxRcXwxQ1BCujB3kt4LiBHQRxqDXR6Cz5kaGhNzWNLxP+9j/El6d69aUF9jMTi/ekENPXVYYlST/gyFG0k4B+oczcOO1YtoGItMKlZiRE+bzCz8SMI5UMeknCJB1+pFWXYAaJW8XWKsXYw/SY/HEdC2EnZhBbLjqxfVeM2Ji23LfyZJrF2EYu3VY70gqGgKBbg9PnR4MZZdALl6nmo6Qz/mT3L20oIQezf2luYPkDjT8yagsjvOdy2IM4y25CsxTY8cfP4kGxeqqEQv+KTGdRLDgU50FVDN80ML20KLMeJH4T9P2BJyKVFq/wyIopVpLF8XRaWsA0Dt6HCwI9dAKyoOyn8gOdsdsMPvhv9gL+k5QmlMqRSLMQ0AdowHKqwJ8YpS1tKhBcafZGN7OBI9sLAEAa9bkq5riPPhUZer6enpoaFrj/bM69G1oaHp6a5oOsmTJNEGsr4Ei0tAY0wznpY+XAFX27dmN9f2Nw+dGljj6tTBA5c3b90ezZErV/iRnG6rq7MQJgGldrW+s1Eu728enEcZONTcHx3lGjeS9b8WVj9EVBe0oRw4eGhgwCE5dbC5f3N263aWn+OR8AHZWwImEkkkLkp18+XageY8yqGDB2qIspAr4iYAqIr2FzeSYGmt3QjAREKsqjHqZMVwbHHD4qEEJ731+Mw+eDx5xLIK+8DiQxIMe3cPMJIoBkgbpGTfBjC2b2p82b/y+btTA3fuP5wbuHPnfe1uNqkro8QwiGikwFWVwBTJgDWZsS4WRC4kQZBtJWAlgU3rRmF0A1iSIdzYuunV3bcYlodr5h7ee3/oztvnm0+kUqJFiDFa9ZkyCUCOiGAkC/yqa6cf5AIAM4kkQSppEEiKBiQbN1/1vawdOHh/YM23O88G7t27e/fVTUM4YRjJUdEXlCMEHtt1dXFyMsOJpMd/9k3AH5CIfZJoVJMSegIWaYwmX26uHXg49+z+s/tzD+fmvu+/29i+TpE2eCSTnzYdIWcubqqCZZwhZ/iQlLoDIMzVtaEvJUG1T4Eb+DiqiK9xSD6/d2duzb37zwYOvdv/sr4VY0QUAj9FrMkqFAoF2xRxUuRDcjoEhG0OePP+uT1YnFnvNLCQAcmBpD8AwoFEQJTXmBWnGQ94x5ZOkvR0h4Q98ikj6ArOlZ9jxZn1vEjCZ/slCIjl6Bd05bJjineW/O2POfmXJP2Bgcisk12LaHq6dcZ/dC3kfH/tCvwlia+2eoGfBDOt0SL178WGsyreydIqRBE7ScKfdp4SVFwmiTqc9N/saaWsmwLf228w7t3AWSYukyoCzLRIDjOZgiR/GPce4CzBlGhFgWa7KXEBwsRO0sOztoIocY3WNTjpN0V0TOFfXZ4l3SXgLXdZWVezh/2m5O2k8CMJnhz7oQMSTJ1WKBxtS0qcP0l/IO6c5fSvOMz4TVFyksqdpJu7JUFTyvkY7PBMAb1I7fIKExtJL39LgqFvM2UHKEqURsxGsp6DJeGdmOT97/K6gOTLksq3uo533BKUaqIpArYvfyOOVF5Iwp739dA5qVK5Qn2vio9CPpZLq1w92Rsy3vlISGuKCE1/eVWIxJekNUx2A1cFZ0pRhyGvvOJ29+JJ0suU9w1JR9AuUgifKUTxZ34H1FvDkQ/JaabiEqvVB5uq1fnvLAYSbMRFtdYiOQlUIWmBoyfb5kGuQjRlRACQqikCJPkAmcTUhigk4JbXcOulHWgV7MMCP0+8NQQDCcn0JTOiYjxIQjKTMiwkiThSvBNxNl7HoPAj8WKyk4HEMABSFogZtIQAFAgpRJnzShFu+4JSpFqcH4n3SrHEQGI5BeWQkL4TVjQSU7eHY4vkPCgKknDLyWkvJgwkuJEH5YFDYqWiemIHRfOCMm6fh/l54gV+LwtJ0sIvA0mcb8WIJBgUCq2X3DOg2bORlyfeXEywkEBf5oRFQMpkIFXIWA/ECCTYh3OK4h1YDtvNS1d5eXKcLfCeJOL8RVpPEDHyRwPNiw/JOY77unASnQ76jl41wL2ECSGKStLra138FYy8GK/52jCSxAVOOfHex0HHJajYvCTwDvZRTl5Iwng5eQKdF97m6zoMeyQVjiQJjvfFcE/wtpXzSM5D3t7fcaquBPNgxNC6amQyDXA1iI8UQtVGsgsUnp4wXrMa1ye2uB9+TEaN2FjUfSxEyAlBkqFfSNKC8B9IBrdM4Ed2SUbkiVtbXsgjFGDKfpyS5Ua4JwGS/5STjCyPTbgkDVm+6fzZ+PkIY/L1CCT1HMz4SBR+XXgjC0mjQGHEJbkpy4CS5Qyktjj/cl0ei5ITLZATTiT9jL1rniTlFNOgr6TQk2hd+LBvUcR/nqDYSGBWHrl5c0qe+tnPUrfkicEok1H3JuNTKHIk6fX2EWwk6AFqis4HCEGinVayv5xWdG7nLu9t1jk2ki3yxOysLN/62ZtnsY2FotgbVd/Cvgki1bl5cpx1uz3S6l0pzMkEJt7V4JQ8FmFhX/Fd5LNSkYZ5wvumFSS5JY+5DWsWfqogT4QuVNtuWnqd8rtpwdV5kmNMJNfdrM8iUGFkzCWRw0icJXeXt/AieSLxI1nvtWEWkoJbXXZQGvZQAToSWl2qs5GY8S8hc+mQ3QrvLVGQhL7AeT87Ib/AmI+5j/JgaOCpqLZviTq8uQsngcHrMmrMOUHemrAfUxGu8ZVfN3f25ZebJ8f//Je6UoO09ZiiECa1bZsKkoiB57nh5nnXCn+BMuMF/gcvZ8zaOBBE4fIFcRjZOiGpEQHZLrQpjK645u6KuLBRmQNzhBgOVfkLLq85UuU3Z1eESGEIzoyf/f2AxM+zM2tpZt7CdxojSp7IrgMZ2f7FuOuQ9gl/BFMnqME5CSMfMe7GaULuBO0u1p27+Z6MunPI/b2Y4QjGjukeXORPleFwfUES7kVmTOCME1Hq0ZU4xXbcxY79QzylYyqP16EGDalksZyMtun71m/GnSxAe4Gcj8JtMno22fY1eEpSIqvXI4jI9yr5oOS2f2sXsaei5sYX9soR1eUwWPCzr1w5XUlhmN621K707VfXXX8tTtnzXdjJ6UE+YVgte33O2g4hOYpXogsKv3zJMjyLc3z782N7iygNg0RMJTIo92Ahq1eSxgkCkzhd9CWYkCciKOe/6P81q6d17I1YbtLlzAuZciefZVA68JBf1lNwlvJGTJP8c0KMGwL88yX/wf/kq/eTUrjjaJXsTFsbhtv3MMuyoCM626Z/pd+kse1S7COFi5RFCdpBSleDTGerKFKJLun5Q6r3o4BDgU0J1rYtQOU2sYMKxmYmj8K4TGxXsis5UqQQ+3rLxTaYlULm6qpo3ypnSpEeCGto4Wz6tzVOpd6c+N1w3RfsXJcqITQl0trj0OAUmrnwQNDCcylZ2cNSP7//S7BAdI4pHWy4uUoIX4m0JWotYXnwqU44WmA7LGm11CGulIrOcL2ya7kW/ngNzPCdyFYV3qHwXSwfYIbgDvfrSnwgdzwwdfEozfEU8aQokcki2TR7fExVDOEwniy+EnlBSxfFMXXVtD45JJ3yZFGVyLBIOZu2KJyrqrVzzd/f3YcOmAVOhOY2urpSwMsQvhKgKu06KtjhKwHcS3t3tNo2DIVxPGvHPsbZEbo6UAmFmRWCGpDngne3gNu7ujBCChmkW5eVsg7yAHuDvfbsNHWvttTGDlmbPwSELyR+WLkwie3+5h3dSIDT0eYdrUuq70udE3P4rR1HJWm34dkjGaPTI7RZr4unvK89M4eTlhmVpPUuh+9v/6LoT47fov1KSWddnh5MRqP+4R2gf3s2ORheHqGbSskTaSfZvnaS7Wsn2b52ku1rJ9m+dpLt61+So+PVyw0yDUPw5HAXCVrp/OYrlrEEQplh1G+95Pv9v2wWDlOHAK1IE6BZAKV5OQQpaJBSTNBaoUbnry8ufqDMQkVQTKVEM5TWABEDy1mhCATFgOZGknfVXVlKtBPtSXiaWOVTL0AxnCqTOrfIOEBgk6CitJbk50XRJxSFjEA2sWTYeec4SgCTxqGclb0yRglJEmiaNJJ8eXi6inVasoSEDYQCqJQYuCQAVokXErbGskWt3pSSE5TFYhYOC2dYuSBs7jZa0B4knHlZJF5MRNJwd42qn/h9gJuqlcRWErZAQBAOifZEqqbkpIB8RJHKgCk7cGw4kK4kljy0V8G5oHxM1FiC4f0VdyagKVYSJ6GUWG/hxCdwMSIFMaGuBOefbz6gzItPIWa5uySsJFZSeAkES2RB1qfrJLVTKGKjqnF1tHlq9XmYx3B1bM0ClaRZlKDbEo0aPVaSDhgynzMG+QBdNMvzuFgFjXukJJ5dM+ZsZ3aGXzE6aMxMV/McjVsvqVYqPRagMaH9aDz/DaSbkkTzCJhF6CAyyM3GJFG50mCALsoEM9mY5Cofi1znuaD9aDwfc+eSKkL9Njb5c7k++b/q9fbxJNrr9V7u4Qm0/+rFH3ITHoXZTmIYAAAAAElFTkSuQmCC", + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMkAAACgCAMAAACR47ilAAAA9lBMVEX////39/f5+fkAAAD////////////9/f36+vr///8Ihyv/X2r8/Pzy8vL39/f5+fnu7u4hISHs7Oz09PTq6urm5ubg8OXo6OgXjjjw9/Ki0q8nlkWrq6uDw5VktHr/6+3/c32Ty6LB4cqenp7/9fZ0dHQnlkbj4+N0vIf/19r/r7RFpWA2nlP/h4/R6dc9PT3b29v/aXNGpWBYWFgvLy//w8eQkJCy2r1VrG3IyMj/pavV1dXOzs7/m6LCwsL/ub7/fYb/4eP/zdG6urqCgoK2trb/kZhmZmY8PDykpKRKSkr/9fW728TO4tSDwpWi0rCFw5ZWrG24QEw9AAAACXRSTlOAgIAAcN+QOHDzcMjxAAALEklEQVR42tyae3PSQBTFq/V122ySbrIJYIBSUB4KgooKpWBRO75n/P5fxt2FugkLIZvNMuqZ0Zb/+M05596b6NHx8b378K/r/r3j46PjB/A/6MHx0UP4P/Tw6Aj+Dz06HIl1KzCiuwch4RTOWkZwDkji2J6HmDybyike5s5BSBgHcjH2Mcau6yLPK5rGNIkAQa7vehQH45CEPsXh5jiFsZgmESAYO7CWY7t4RVMgjFGS4G2lU2sEtCO2hzFAa3E1XXSvgYuykRVMMSymSIJK52XpZKWqxUywYDw6Xak/HXMcy/NJiDmLo81iYnaVK7XHJ3E1qCU2dE/jOr8Z1zlMSJ0pwpfCPSk3Xp5sqmQj34L3p5t6v2AwjstShmxbY5Jpk8huMAxZPxCG1uk29ZkzFgpDjJCGLwWnK6iWTrbru+tC/XSHrpgxHmPxbPW6GPDkImaHTOILT2T1uwCAQp93PyeKPonMIesx7QlA/3S3zscAFiaURQHFBElAOVJUsRFGfHal+HJNWXytiOn3pNw5SdVPy/FovGCcisL74rGI5UTRJ2mU0jBKtQu24xFDqV+dp6GMeixiIVYaYurpUg9W6VmnMijfXvQI+/zLXS97/dHOulBbEMEuTdhhSYQhMkVjsOUWtmGl6/HNDhpmi3rC9NNV/rUjUGVIirtCbSF8PAFTd3vU3tdZwtRQ9D0ZPN3C8awiMJIolMV1se+Ttr+iWd5sS9iSJ0wBRZ+kscWOzgB2yXIoDHv+5TQhtmHHDOipoeiTdGSOahl2y6JiMBzHZUlzLYBvY5ll2gInVEDRIynX1DgEDedZw/AZ0DvfVhYFFC2S8vNNkHcBZJQFaxjX9Ylvs4xtGccWe2xxjJMEm11/fAEq+uMMY7Eoy7mEouyKINEB6YhgKcFwFt6X3hYUqfbaJDKIliFCtywhYrZIKLz25kjkjryTDFFkcXFImC0Sik3SUPR3/AZIA3TEVyaLGAJYjJIoLUDE9WQUBU8U9kgpV7LkiGHals2E9QHYi0vHEEk1WZEA9CRu5ZBIKFMA3095jNQieVs4iLiVQ4qSfJe0ACutKjokwVMTIMBfuNKy2NBKolyDTVxkGyAxAiISRjyGkmy9G7pp+cpJUjUDwrUd5Yo9ee3Ll/oUHhgEEa4kaz9m+fIcBRLlbA2gSAkUB+rxvTKi+fL35ksxXVX1hageMJ9YyVdjN6tbch+JiidBHKQKxUugLOIoXbDD/flS8eR5vCRgRBZ7yRf6ADfJ+eXvN0XBk0r8RlFou/peCV1oxVvfy1J6BU+emi2JCBjCxIZusvT+3knMSZQtqYEBCVcQu1umCVMsZoo2iWyJmWyJ1ot8xSYxsnVJZEuqYFSs9cSGpVpTMpM8NTq35HwRC/pSU/RIZEsqYFIiX90NU9IXPZ/Cf5clK1OwbApOjVdGTy7MWiKz2LThCVOWgNKfHjOS1MxbIi8ViJvSB4ukmpItXWXzlsj5CpOm1HnndT2pmLdEnsQbTemBl/rwmI3kmXFLZFP4+FrG7ki+5zVJAmm9mxe/WaA1Eijd9HjxniiE6x0cSqtFP03GS5fkXbHhIplNSWzHvhQvdZKSIClDRk0+cbVBEmpmH8QWxOLFl2OKKft7cpEnXJiQaEKIc+tDU5nE2ozXmB3EWiSNnOGKhpxhNkSAZ82hA87lrC1IlOM1BTvMSSLXJFAmmUWzeYTIvDlDKJrPoktKorBSxPQSczg/SUl5LQoS1EQAnyY8XZh+HkaMJPP0Si7HFoTYc3I0Xt4mNWWSdgT86/OekMn8kwLJ6mJZxDcKxsjO78lb9ZoIkqEgoVRtdKlG4uN4URaA+IuJvCRVQTJQJsFfHID5nJNM5gAKJLwoIbQEyRU7vfJ4IhcelElgHg1nXxCg5iUafrmcfeEkSkU5F/91glXedpR7Ip+Pz0FJl5j9PZxPEP+B6J8ZmjnOTOF5izix15EjMbxyeVLSP7o07mEPponhpUMiwtUB05KHlwvj+NNWiPOTBHrvUDVJMIblxhhWJ5GvrrdgWjKJD9fJy6sQkgswL/leqcdI+ELJS1Ix8O9xSgsFBEmvKJIAlNT+80sUTWAt9CFiUznzYd/eIAkPTtJ+8/rJ+rdXZ1QvIs7x8TX78ARl9EQi8Q7sCWqyb78meXF29pV9ZLbQn69ffT47e5V9yUPssXF6cBLuwpok+t3e2fYkDgRx/DT3ZhpBil7dglSBQJtrrQnWqjHmHjBivMfv/2muHU4HqZDtTLca4j8xAaOmP3dmOjudHWw7BfCT/Oqn+BpuEUvHuookBzWTnA0bp0iCl53b0si2AYanp35ub7Y9LEFSKK/U7PFLJDM7oYSsDMmre/wTyRVetp+/fVRq277+/UQehYsbrQGDBBdlloWse3++HmmaeVHKuzN+WUOCWb1u102HRdJ4sDMl14A6xFjAzFa+Y52ozgySSNCukts0i7zThTUZcba/WBoWZJAuI6snEgxaPgbmpAEoXJdDXZJnJYlveS7M3zMqKq1wSB7sGWSa5ovyqMQeaRdXnu+0PrX4awIRr9yFJHTVVzlJejb7/70z3d1vE04Wdr+Q736BS+KReXFI0KzQyK7x5j6/M97qdhgsnnw+gd0DSUWiTyQxg2SYZY/DwwzkHnOW0/QwTezE1wzCeMp2sTDcqrtyRySQ2qgHP/eWe3ydTEGL5KXKHYNEWE0dpo+X69/OZqPp41ZldjYaNko8//3MrqbKK9xykZvsLlW414QuxlMHnuR736/ipw79V6kT7RTu8Dfo8BU9nZsAT9yGoibc6GZdRKLlKMqFWkQp/TGRHGGLKqyQ1rNfz0CLBPsptmhNOgLzErgJGpd2Z4EWiWuR6jIvbIGeGxclwvtSEghl3Zy8Zu589sQ5owNHNw4rMC1yk124W+qKEvd3uYpdsJeQUEaf6Sg3Lnn3YMD2eQFJuwl3zyNXU04ysLjPHiS3k72F9PEcC6ny3lQI+YvCj10t6vU4BnwwVwFJR7AoEpc/f8pU9sQ93MUer7ELJkVnanDazMl8BNNOPu+vEpKe6IQAt5G7nU+aOPqRvcM9ViUkrhKfCOKcoG3P16GhMUUKSfQWpV6nd39HSnm/2vvtVv6116zsdJPbFZ9u4sxF+Nto5UMwm80Kz85dsE+ccUFQP5uZMo4qT2aGFikCs6ItUbizm2FUe4I5JhDTPSw9RspaggQCqyZXGVgMEtr9lnR6FYMpOWNGnMQ1Yf2vrLEhr0cQRuWgFAkERlFonAujgFuSBCJjKATCGydQlsRR5lAIhFG+JRKGqxhAQRBugNxmTI4xhILOzr1pyaf5VFmhiBGEkUjwSDBrMbJb+aOsRXVdKCX51CtPamE0fpURtiQk4HSt6p3FiRggfBJCqdrC+ooBIicpooxljh9PLAaIkIRsQe4t5CFyEP5ky0trWT0ei1scRB4x/pJglm2vgDJmsBAHyXOBIeEEWIGNEQc7fMhJyO+LmnRATzRQnTsCUG5dhOK9PClZJ5ANAmW9oJAbOAxNr7Ym/RhWy+lfFn5PWh0Qz0Z3QmuF1CS4iN1lx3A6AVEsK4qBJSIRqdO11khFE8/r5fK8y2j9j/IzBbl1kbfIdcnwEMmamGIJB8AXrYlccfjKHHISYvFq5zBDgjbWZWCoQMBhhATVCcsuR98FmUyQoJwSMCFmnGIZIkEYDTNTHq2GXIZIkOYiCNUqiCjoOFC5kMSQ4kG/54VRV80But3QC/oXDhjS9sZ8+ufWO8mb0zvJ29PmkGx/+Aiboa0N+jTpzfmE74351PV/l9Yow3BWYVkAAAAASUVORK5CYII=", "description": "Displays the latest values of the attributes or time-series data in a doughnut chart. Supports numeric values only.", "descriptor": { "type": "latest", diff --git a/application/src/main/data/json/system/widget_types/horizontal_doughnut.json b/application/src/main/data/json/system/widget_types/horizontal_doughnut.json index 1eeffcae6e..f76bf50e48 100644 --- a/application/src/main/data/json/system/widget_types/horizontal_doughnut.json +++ b/application/src/main/data/json/system/widget_types/horizontal_doughnut.json @@ -2,7 +2,7 @@ "fqn": "horizontal_doughnut", "name": "Horizontal doughnut", "deprecated": false, - "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMkAAACgCAMAAACR47ilAAABp1BMVEX5+fn9/f3///8AAAD29vby8vL29vb///////8Ihyv/X2r+/f3z8/P4+Pjn5+f6+vr/kJjs7OwhISHx8fHr6uru7u78/PzPzs7CwsK2trbIyMiDw5X/2dva2tq8vLyrq6v19fUnlkUYjjj29vbg8OWRkZH39/d0vIfv9/KTy6L/7O3B4crb29v/h49XV1eenp6i0q//9fXg8OQ9PT3w8PDp6en/govW1dX/+/wnlkbj4+P/293/w8f/4eP/2tzR6dhFpWDi4eGEhISy2b1GpWBks3r/r7T/paz/+Pn/5uj/3uDU1NT/ub4vLy//sbf/c33/TVr/6ev/m6IXjjj/9vf/8vOwsLBtbW1hYWFKSkr/3N//aXP/7/H/4+XCqKp0dHRmZmb/zdHYysv/jZb/ipM1nVL/4OLh4OCkpKR5eXlVrG3w9/LnyMrLurudnZ3/fYb23+H/sLbNsLGi0rC8oaM8PDzj0tPz0NPfwsPZvL6/tbZVrW03nVLn2dr51tjLycnPxMXDvr//aXTz6OjZ6d272sPOv8Ch0a/ymZ+Pxp6knp62nJ1+vpCq9c7pAAAACHRSTlODge8AU0N0cBMSxpIAAA11SURBVHja7JqHjxJBFMat8dOdGRx3ly1U5eAQF/EU7O1sqJwae9fYezf2rrH/0c4C50NR987M2uIv4TLkkgu/fO/tmxluwuRpk/D3M2ni5AlT8G8wdcJE/BtM/G/yxzFxwgT8G0z4Z0z+Z/Ln8d/kz+O/yZ/Hf5M/j/8mhGHAULQXvxD9JoaRTLRJGr/QRb+JYSQ4LxXy+UKBc2WTNBSIHf0mKo8SM21b2nbdNFm+FNoolZhl9JsYBi+Y0qozW1pCWFLpsEJJycQXjGaTgfVzBzcODqjSKtStAjpwZluiIW0lo5LR7aLfpLx+cMOc6R3eJEqmTKLS3NUcObEKCqNgN9oyJa4zGP0m81Ibpvew22aSYeWLGR2qzRMVANy2QpmCLhf9JgOp2dO/4p1poaJEiGvNNABeF5Zt5tsu0IBWk3mUBrGxXsfIjK/YsasCJG3LkqGLBhWdJuVU2Br9fLRN7JrRT3UlkMg3LJuVNLjoMiGPfl7VTcrky2A2VZAwhVVnGkpMl8kC8viKFA/7pF+DXGwhzYKGWDSYUH/0MWeuwZnk2DTjey4jQFKEJaZiQaxEm5QHvykxe2NqwRpADcZwnqzc8V2XE4AZxhJzhUWbzFv2DYvB9WV0MJI8bysVVNIjzeo3XdRzLNmwzHysKtEmqT6NtakBEIaRUCqWyQ2EpEfIhmJZCcNWFcZjU4k2KW/4ujEG56GXroppSyFknfHti2fOPHX6dLVaXdxLtXr69KlTM8dKU7vJwLKvPFJl9GGELgVWt6Vlvd0OGEs3Dx2pbDm6auuKFavTIasPnd1S2btn6ZgD2X4DhA6TeXN+7EEqyUR41GLmKSiWKpW9R3ZuWbX1UEdl9YpDq8anUgWhwWRutAe5KJkE5zPRVdkztLeiXDqxKJWt41OZSUsNJqkv+3whiG/LqNdMKLhgqsJUid3ZefSsimV1pIpwAAQifOk1IRGagIiAPgHzAjcDQ+Xy/EilW2JK5Qe94h1Ytx8HivM9r7YfnkaTfpEzCxEFmUgXfJg7/rmlz/ecvPLg+r1j91anbx67fffslSvP9jw1HC4zzHWZcJegQ8BwkOXgFXPehZrQbHKrV2QQ0ZAJnGwGmWF43F0aDJ08eeWySuV2+u7te/e3XHn8YKnXagmPMTcTgEIpAmy+gOvUoNdkTa/ImCqLTDjHEsdlcIQLx39w8vUd1fjHVqc/3Lx89uTJk08zWRf7XNfJCFCjzGc44AG4UCzm9Jj0z5E58zAWyET4XLiBw1Um8Ex/ybPHO49uvX338rHLN58cu379fd73kZV8mEyKRWc+W3fBCeC569g6jSblHpHZazAmyATD/hKOwJc4p5YZa/jlkZ1Hj92/+SR9/9jlVVvuDFkWVBsxydCFecUAtVrNAyCKQqPJYI9IVK9Hz4Fw5O88trU9WNSI3HlkaLOBCDSZLIgQGfsnIJXrR5VKe64cpWdxbCb9TTJnDcbHYnxHRQ2WQ51pr8YKhRKvSc/2dz0iiNj6kcreigolrWiHsnlpxJ9pYvyQSX9tpTBumjMJor3Rpx1+1Aa/uh3jh0z6a2sjtGAYPG8LEyfo6LUJpYbJtR7s+0029na7HsIDshSJ3muLVZCS6VTpNxmg2poHXahUWKhCB+MquKiXEnGaUCSD0IeR4KZloXL4s0oadhyhkAlFMrsMfRhGomQ2GEboFhyJMBRohkwokgXQiVIp2F/U18owlERsmVAkG6AX1fVMqvrq7ZSGqbtTyIQiWQjNqPqqiwKa1CmQksVlMqB3lBDd55fdMIYO94RiqZkCjZDJgrgi6daX2TCxiUKBsPNaQyGTZXF1Sbe+mC16QmnCtJjmLyO6Jmto5wjNUCjUKYeREGYpGYMJHbBmIxZUKFJgVW/Pq/KKwYSKK4VYCMejKNFM2RWWF4/BZGA8/T7stMHXsBq+i6FCsSyMxFNeZEJPrkeIRgTBeScIRt95PzahUL7o+TQadiGh3+ThOO+3cgIAD1oMzDkfAKI1HGECg6t6ovJqoi5ZDCZzRk3WIAIyYTnfyQnpnnfg5Ja4XpSJ6nmJlTQc8xbjuk3oGbwbUZCJ6wItDyIHCAbUWIRJMm8vN4ZmjDKU0Hd0JJP1oyYXEQWZeJ2CCk2Yv8+LNlEjpYAdvWO+oN1kcOxtQibZDCDPt028JdGZwODMsmk4jkDKfAJtynPnDiDEAVzAFxl0yGTwA5yrQG7/OibW7Xe7Jmvp1Dt2E8dTL1eZtN+KaBPVKBY1yi7YVtjy3ZuQZWUofMnOS+lzxphg4MJRJmrJAckgwds/BCAlgNy6+QjUXbK3LicOdk1mj5qUx2ECP7fPY+C5HJbUct55EWlSsHvG/DXkR7//nvu5HM61Mk4rM5zJOG6QVfUb5u74S7LIDrvCZy0fWaZ+w/YFUIj5ANwDAhAHuibU8OOCM4Sw9ioS1Sh1kUDPbBzd2ZMJd11kXaZMBDzmt6tLLV2RBXdFy8+KwHGdfczFZ5Pa/owSER2TNbQPjgmaKNTyQ+0pD8Wl2aP3UkoDfhZdE3fUJMuyYC73/NY+1goYIxPholZjBxx0TOZFPLq0msyke6/Pl0UL587t1rUfION2TeC77era5/twXI8hK/g+MM/xyYTNv3BAFA/u3x+0TdZrux2KfHhJ+s+2E9H7FQ6FI3h32YWBoHdtkwUxb4R7pzwdHFeO8dw4LDEmQpOUnuuhaJO8tDBCJhaZaOCnTMzl6ODncj46LFdLE9+EHsOyQQNlEyzJfq+Jf3xbESHL589SLAq1zM6yhh+hTMSXJr8zk+WfiDmb3qaBIAxf38DuBq+T+APiiIaASlqj1EIWFlLaILiAA1UlIKeGA0KABEKIG1f+N17bdBQlLhh2xCu13aa9PNr52JlZe5JrrSuSROeLyTeduMDKLFdah9ghsq4Nkv9sXSOt47wiCbWOyu9hvUSsz7GlZhLH+p7styAJP7lIKpJIa/ND6xHSSfnJuY6xU+TxnNbVPgrXJGlpTBmZ1G/3ZFzGLjoMM5C0yIxEgrVOomilV3U8Sxc6z7BDtCeb+aTXH1smmbZqCRMJyoC1cn85UCMIZcY+5fgbBUl17sJsuTyFkTDnEyM1xJ9p9OpVAG9SkxxRZ6UVyUTn67XWizo2r4swdhnKQIqei8ONc1dZNJ5eefHiygyFzOFKwhGGxKnrEOkoAfObkKjqEwHhSFR65SnhTRL83ak+uYhdKZDleoRK2epyjy/L3890FlZ1fbJ8UehL6XqhAPwT/0QN34UHEUYpoKKDQIaeqVtUgEgEJ4Ecpb9IXr9fCCDBX1RaRLLQcRWw1qj1SeeXgAyKKYoE1Sfy/hYJlB/KCDJSQzwNXyKoDM1TEVDg+L7jvwu8wPGpAhaTe0RC7a5pG5LzytfXBdC9JK5INHaIencZ1YyHVDPOjHWd1nV8qCKIgmR4gIBIAiCAH6pAvfOEkBck5XU9ImnVkSCST5V1GUcJTVKBm2xbV2M6uQZq3Z0ulzMYpX4YlbWIGqrAr/ckCqPiD9EJvAOMIAMvIhJM3seKSCgMf2hD4n7TOl7n+lsGxNVS7/Z46q1Q6Hq2OzHK+osqEjUsP5Mb/9JQn1DwOmtDguxcF4ozQ7XIzTJFswbk8NTvwu8lFP5AW93UKdopzdyLZeqiQdTipgHwbKBsjhqpw81bNW63uw4x7rn2+8LYZ++uXDXZROAa9er7HL16cpTOLbDIZJO+6s6ubLhJF/ZUk+Amq3nR0IF9poXrFL14ZGLwxpxR2HWTC5Kp7atdJJo5gCLXmzKbcJDgmMvnqSns0CXC5xioPs88HteZN6UrXXIThiE2kXzcY90UY109Oj7exX22eyu4w7gp9R0viUOutAgiIZ/nuoMz7vdx93npJXfLux5cJDjm3xQBPLx27ckMwnFlF1ZFJLQpZuZnX1eNz7uDcun23DHXPUjaFK45ytGds87b/V5fCNfpm1uQjCTzDqN93dqr39dg3jEjGECIhMIXg33Rlf0ft8dj2WUAIRLKKQwTR7r5+rjL9YocIkFZpvCciekEcXzVCAzaIMExoXyFPT3q8AWTBpIjsq+zOWxpvmf5cRBSE8mGfZ3ZKR/J21lnspc+BfjABgqB8NhWM8nH75ZR5gaE4XGQ35Lg6GbHpq9M9zq8TtJMYvyTtI9/kvE70hx8anqq3E5eIa+juM6lxif9rTjLrZ/t3TGKwzAQheFAdpfRGVRJCGShxqTbygIlJ9gzqMnev1/MFoMjJ/IEJhmMvhPkj2zUPZfqP2HUXl/4CfCcYF8Ysm0R4+yAzp3VK0Mer5QgTWupN78CsMKSWl7+ku8RNqs7bAQm7RK8V7DFwRbYwXOP0EvglNSSvUzQFr1VS2kAftTVqxIcPOJwMJLxXaeXzE9YpeAS2dKQvVEVE4GEqQR+vVpTrjpHPB03jfpi1Bo/ABFTCR7LGmtmVt2VIjAglqARW2jsCJzoJeQW3PxiRi+ZW6R3YEnLmNQtIe9HVdJ08kZtYbQDVvSS2qRT6zR0hLd4ar06WbXGJrwzebRLyKYcfErmv8iYcvUhO3ir/l0HiXqJPL1Enl4iTy+Rp5fI00vk6SXy9BJ59lTyCfvwcTgedvHV9a/jH7TmXldOIe3fAAAAAElFTkSuQmCC", + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMkAAACgCAMAAACR47ilAAABDlBMVEX///8AAAD///////////////////////////8Ihyv/X2r9/f36+vrz8/MhISHt7e339/f19fXn5+fr6+vv7++Dw5Xp6unx8fE9PT3y8vLB4cro6OhFpWDg8OW2traenp7j4+ORkZEnlkXw9/I2nlMYjjj/cnzNzc2qqqrCwsLb29t0vIf/h4//6+3/r7RktHrIyMj/w8eTy6JWVlb/aXMnlkai0q//9fbV1dVJSUnR6de7u7v/19ovLy//4eOwsLD/m6L/TVqi0rD/ub6kpKSEhISx2b1kZGTPz890dHT/pav/fYZUrG3/zdH/zdD/m6GsrKxtbW3/6er/kZn/eYNVrW08PDz/pazf7+MnlUVxCrBzAAAACHRSTlOAAHDfkI9AMGXS8IIAAAzaSURBVHja3NdBb9MwGAbgFgS8rW3ZsR1baVoJrayXLp1abas0VVO1STswBgcO8P9/CXFaGImzAHFTBO8l50fv99lOr99/8RL/el6+6Pd7/Vf4H/Kq33uDA4YQkDz4C3nd6+FQIdXgmHlxMImDUEqFEFTkX0KPjOkdTOIcImIsSRhjUZSD8nTbTSeSvUNzrpTinGubOA7tsJnuJIJppZK8FK5SmRrFtc41rppuMN1ICghXjGAXKqySMs019iiYcMn0+io+n987Sd4IBbLlcnODXSjjUhpeNNPdlIVLpqPVh5PBLidfXQkR1u+HRR4+Lz5mcBE8lcph8mKCKV1IRvF48HPeuUqA7Q6y52ydxmGkUdpGImzGupCM5qeDau4Zj/A4rObszmFolKa5hQVYOpAUDD/nTNGiEh+zdBihUsVtuxnrQjK9Gg/qM7aG4GxYn+0GANF5L0kkwinhkmns6qjPB6sIPg2fy2wJgCipNIuCagmXlB1+rtye3HmCioXl+2JZSC3hEt9RPbs4x3o2/JXFSsNdLQGUMMn95aAR8pVGVlFkJYpvuQEol0q7zW9BCZdMxk2M03G8f3URZF+aLdsMYMZoFrWYsHDJ6tnBOjlf3U+AH+8uAmC9eVycXTSNGFUpT1pMWICksZCT+WiK7yE5JUq40QS73Cy3s+drsVLpFudxmKS+kPFqglLIfsBMqngiCFw272fP1SKM0qwFpb1kOq9bjPiJUaYUv1rGuPc8o0U1tc0sAJqaFpT2ksm7mjpGqA3ZW1iiNc+7kUYXmGWN5SEDUcVxfCTJ9eUvHVUKpUJEOcc6jUwT6ixn/oRlgDa8ppVOJLenv+HwMITuOCxxmLdcAHj0ern4CFjpjrAjSGLvtPIcXshPnB3GSMlqZ+wOsGlBCbb4kmZIPMVv5wljuSos64W/90UrgnYq8SHvrvFHId+PgCjJLYoA2ayGknIWSPElzZA52sRZcozl5q0GsPApiQmmNEtib0PahewszPJUCmAz8yjcaBa0Ks2S28pkTdAilStTSQtkD2XKEsQYHdGuJNdlyDnCsh8xlUoKfClTbkBTZcMp9ZJJ+UKMEZzigcm4SUV1WS6ynBJwrTRLDgbxH5hSo/KPPFsjSd0TrAPJvAS5Rev422ILyrJE2QI8dFXqJasuIE+/MDKpUu5AUmVFIMWXTE7DR6uZwnNKZVWE5EyQIIovuTwwxN97qStr/wDo0PnyJbF3sQfG33subeUwXrj5+sas+TWlDURR/PXMNGtwwRAENkSdkmS0YvEPFKbpAxRqndpOp9//q9TdrNms60PMsrTnweHR39x77jkkWN0vk2SoBSJ2KIXSE1M511JF7NdOSR6qFWWIHUuhHOpP+c6Btq3pdZLb6kiO4UCF7dvUw2f9fnkjS9PrJDdO3G6idIL+Oy0hr+/Q5aa3JzHtfgJXEq7vC6vopuf93prE7FtDuJI8YIe4uDaGsiOSI+e7pVC6bSr3Sw3FJh4liTmSE7hUYRUfODeHYkdiFq5bOJFC4fvVwtJwyk5Ibhxmoul63otfDsUPLIJekdw6jhJzv/o9XGhDEUFvT3KmQAZwLh713PRfFMl7HvSkZU3ySRuJe4mhdLShLNELGlcWRXLpeCRm1D8NZaS9w38Uh9ia5Mb94TKdQgmWmufb7V7LsyM53keWmPHY1s7XFQiPFAsSfbku4VYKhWdKNejPm6+XIrkxGpdz8aEEPu7M9bIhOd6r39VQ2lTz/BIk6NqRHDXzOy3kwRBdAKjn+StFcg+PNupeimTQbLkYY1HO2DOJn+FZWYIaJK1eEOBOC0fqExuS0+aVKyIolTGd5O3rVRjFguS40ZMhReKzKB9hsooSYLyKUq8Oich5fr0+KJIfze6wIjlq0lQUCVllGEW+mMks98DGtUk6QQfLqlFa/ddJKCoihP/ZsqzYhJSUJH8UyWkDkhkDkE7kdhEySeuRgNfIALg2EgUvNYpoicF+hWOATufTMAaicBpO6TPJWRObKJI0BTBOBQlheZrXJeHhqHWvayB41fKLnEFqPRck8ZwiDikNI9AwliSnWsDbzWSRcqj6JB3awr1arwv4ZbMfDsr3giTyV165Z4yTEAqk4Xi8psB8LUg0w39sQuJHPkhCkeWCxEvqksjvW1dVy3eCriQ5UzmdLbj5pAoSIGXTDQAOxCTJ10aGVyTIkmQ1A0iSED9ZrVhek4RX+8DHZ71EdqXlxT8EoSQDzV+SrOecgDtmDUlyZF+6iCccLG4KhOqStPvVbPyAw/IMD0rf0ijLZhHVSLjiwvobebu0IgzXMvtwv3q8HuGJMyx/kzUYgivN0zTNJxoJY0AWMpApv1ySZFD7dLkgGQHf1Rnmzcs4wysKgJaeZ8XtSmkcUmzmbLvdGiQDOJeZ8h5+V99vmYEiTZfMqiRkHYbzBNtQSJKclCQP2K9EHfbwWO2QI79mXSEUpQySSziXeYZbksSMxvqSJI4ebNclOax2SEHSejPIf0ESEI3kH82kGy1QaLIJ55uf8vNTZk3jEWrO5AUJfyO0Z5LuYnpwEEPo24HQN5FXxedw1pBk7zOJ/1Jztk1pA0Ecf7sd4JCHmthaEsSEIB3nxlgbjEGZVqmdseMDZabf/4t0kwM2ydkxzYVz3BeIvpEfm//u3f8eIsbWJPeM+SGfM4ZZ4YxNQu5FzCz4dMk6qSvopMSM0WTm9ZrEZ5ENYERsnqTHFpkJitaun8q1S6kKWxxsIpkAxoT5APw+0U7IGC/WT3akfqKJhIJIbllkAADmhEAxNYWmWtgZP2d6fLc8yWHJ0QqRcBR7YF+nPjyPUP6FSGwQJDTuapXWye+S7jaRQLhkGJEHsKle10Yhkk47PRZ+wrGwnJMG2oNNyMXRD4zGyq2QRvVOCRL67NnffLvI/KSBfv3XjLki20ToPPV6npezOh9MjCE0vAcTQHGmRST40S3DsPAVRBh8yUwDipjcnfSc8duzzvCwhy+9XCn0LQHkz4nkjEhuypGYQhRzIfy1dO4LtZM9uEt73DSPf1zM3EciCXmuC3grg8IikvMyjgSR0M9+LPmAB/S3l6cnHzMm5DRlp7qz2WwhSC7b9tBrZLWDPtckTosgkV2iUiQG6ns1ZjHAFL0lYMwq5Xd11o1xFocg8Uaj3mWOpD86wnl8lgQOShYvO9Xjw6TyLpPE9BFugvl5UfCSB5k26xcI4tLTNQrluZbZJ5KSbqrcT1hk4gtHhuXqfR+KrWpNM6WL2smjO1uQToQ/SDGM/SmJ5KSk4UVasP2k8PLk/W2E700LCskkc/D5F9ld+dqFfmD2b6bVtswRkSiuOlAYPKCqGwQ2FHRWavCUMlNJ8Nnv37K+j3L9xJIVj5LXvYZN69goEwJx/28k3KYaoLI6px5IgtOsdDe5gnq72apqxfQENAQ9XHW4ym8teHur2PVk0AWnWZkormKD80o7C2gRO9VNlEjG2p2iWlyD8/tWWjikr24HjgN6IpZJU2zwogUt9b1E4GjcqEaCN+BCfrgUScZ6NU8k1VQuIsHHS3tSUCdG6ulyQTR4VRLY15cU2oCTVvwV1NrdN7fLdtNP6jA93Rz+78ZnNNV3Pg+cV0lKDabuCqS2261mD/d7zUmhO1rujo8vAEFUjm0IEjkpXwaw/RCnAhEliZ2Oyvlyzac2ZKXEKEjQamFyuripXpWEkkJxDtuPD/t/nP2TvS7CNBUvKZNPN+lcmR+v/tNuoyGuwFQAyZEMDrZ2LJNC/t7G9R3lyy9zJHDzTkf9kgYVA/VbfImEGj3Vry1NVOSzVGc1QIpqT5V/cnRJ5bDis1REQoe1dOywR7VXXCeJhJ4vHV0F1V7t2EjjSf9/gzhV6FHj7QvSdS7V2lK6bsSQQCrvW8+SDP62dy8pEsJAAIbtoZmpOkA2IdmUceUiIJiFB3Dldu5/lWFgoKBjTyeW1Yj4ncCfxMfGSkDVDWYQNf6U5hLmSS0lD/EgxiWZqdNYf/5HT+FDVXnCUs4H1LkLS6dejRZ2YUdU2rjlk8gM7KBHtZCK6XDy78kpKIbUTewDidagZkjdFMUxwWZ2VAgpLOEnGAsWNrEOH3SCp1Z9CUy0Nm20FncwmqCOsAQ8YcYlKMcdgheUpIT1mBujh0LedJjrW9jb9knJqSDGDw5XdAPUk5fwDnsUeHr1itb2hKuchyryEmbwmRAHm12Yn4fIFfIFkZcw/43/CW6J5lcfo6MOmXBB5CW5RLgDmkFAVsKMuKUzLcjJS8AbOnAHl5TwAx22g0sKpYAbuKTawSVVpkiVy9FbUCAoYTbS0TJAdArVS85YeBfZyWBmCfgELYNtQZu8hLV2NtE5or8ACks0aXpvBJecw1VyPFfJ8Vwlx9M0H3AOTfMJ5/DV3O5wBvfbeU5d/wHsiOrJpqVt0wAAAABJRU5ErkJggg==", "description": "Displays the latest values of the attributes or time-series data in a doughnut chart using horizontal layout. Supports numeric values only.", "descriptor": { "type": "latest", diff --git a/application/src/main/data/json/system/widget_types/range_chart.json b/application/src/main/data/json/system/widget_types/range_chart.json new file mode 100644 index 0000000000..981e3582c5 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/range_chart.json @@ -0,0 +1,31 @@ +{ + "fqn": "range_chart", + "name": "Range chart", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAC/VBMVEX////////8/PwAAAD///////+bsfP+wEzu7u73lkzz8/P1eWTT09PN1emRpeOcsvSxsbGQpeLc3NyXl5epqanjXHPLy8tti+OgoKD/wU26urr29vbts0fnkUf39/fIrYP4l02oqKjJroPl5eWUrPPDw8PCwsLJroSQqfKLpfKDn/HU1NSnu/X/tzD/pwNyku//vD//sB3m5ub2jj98mfB2lfD2hTD/rRPu8v3/3qD1fCD3+f7K1vnnjEf1eh30awSWrvOBnfFkhOLld1n1dRX0cxH/9N/1cVvld1jmjEf2izn/qQry9f2Yr/P/+u/+9u+RpuP82r//yGDdM1D3kEPzWD32gSixwfBWeN//2JDzY0v+vkf/siT+5+RBaNr7x6DbYWXxTC96mPD+7N+VqN35tID2emX3oWD3k0fm6/zc5PvV3vq2xveWrfL+8/Hd3d09ZNr/6b/6vZCPj4/2h3TfQl3bKkjaIUD/ujnxQCL0bwrC0PiWqN5Kbt3/79D6zMqGhob/0n/iUWn0aVH+vUPZGjr/tSuwwvZpiOLR0dH+2I97e3v1dWDkcV3fPVnxSCr/qw+Jo/F/m/GLoum3xOj64uZef+H41NoyW9hdfNbJycnxqLT7x5/4no/nb4P/zXDiVm3hS2TlcV3zXEPyUzj+uTTwRCbkbRB+mOaareN5k+FyjuBHbNw4YNn7xr3/47D70LD5t6zumqjngJH/2I+Ojo7lYnf4oWC+mF7utEj2iDTmfSrxRyrunw/kZwLL1vWWrfS/zPORqfP98fKjt/GGofHv7+/+7erK0umHnuKDm+Lf39/t5d7829bm2dT949Dzt8HqwLFse6r4raDsjJz3lIPopHzGpnT4q3DZVmXSNkvnj0LnjD7mgjbjWzHiSzHwRyrunQXv8/3+9POquOWot+Tu4s3u3b3rzrfUxrHu166boa6coK76uKzTvJvoppv6vpDuyYfdboDmkXH1fGffR2H3oV/kbVnnjkHtrTbOIDTiUiLhUCD/rhTuoROM0RHwAAAABXRSTlMg77MAvxFwlo8AAA5CSURBVHja1JtJaBNRGMfj8scXk8MwSYZxoiFhaC9FEQoRA7YHE6K9iMZ4KIoWcTkpqK3oRVH05HISwX1Db66guF48eHHFBfcFQUUQ933BL6PxZZyZRE2+of4aMhPyWvKb73vzf3nQQL9A/z743+lLGoE+Qlea0JLShVA0mFHFwP9Hn0CgL7QwdERgKnGlmY7QBYgNGwY2lleHBjLSNwAIpdlAFPRIQERBsIjcvDGQEUEimqLq0FFysUR4WLKiG5yQiKGiJGIqIJYCKQEGDhV6wAmJCD3VBDWqCxCmrjeBg+7tK06DkQD8Yd3C2ML1YMM/kY2FWM9GsOGfyLLuWOEg2PBPZMW22JotYMM3kYs9sdjbw2DDN5Eta2KxbQWw4ZtIYRmJrAAbfomsXxgjFq4DF36JXCmQB2eQ+CWybHuMKPAFiU8i61bESqy5Ai58EtnYY4l08wWJTyKHuy2R7d3gwieRnmWkwRok/ogsoc6y4FzI84tYsU4wBwm/iBXrP+i5CC74RcqxTnAu5C0RMypgppQ4lCaIlN6CxnKwUBZZcwhsWN/ZE4CeUA3TiCIiEImjoVCs/6T7MNggkXAk1YIo4lGoCaEDiQQIRRGN4fynaWVu3BRskIjRBJ0sbPtaDRR5fm/1jtWrV+/YsXrH+3uCDRJpDpNI5Me+VlwnMYFG8qYz9IvdZ8AFicQjioKmlC4A0JFeNJSuvBRZuxdcBEAIlGcGkUBDWdIVkuy6DCb4c+TqzpmhED0sipvBBL9IMU8WZToXgAd+kSknQhUs7wQP/CKXd1WK5E88BAv8IvnllSKruO6/7CJzFq0KVbJ2ClhgF9nbFbLRdQ4ssIss6LSLFJ+BBXaRYinWZ9LPT3YyBQmviIx19iCpS2QOanK1GCJ4g6R+kQVLUIvO5SE7+SLqgkWkmEcNptwP/c4i1AeDyIGu3VNqDSn+DyL5zp3VZ66M9Zm9WeTcfVpwVJvvtliXInNQBwwiFHU1UkHGOvcaRYqYKoSiAc0mEs2KwB8wZRFF3apdVa8vuRIzfRRZaiAiVMPUDOiq+KN9rQO7an/jo1h3cIJTxCgJIB5FWMR1wBSoTdGax/ku75LIWJ/pU0WEoRr4y32tS1/GjB4zevSYu9eEJ6/vjh5DY8pPNJ6ePl8SPARIIBqZr0ZKFQHiEUATtUXGHh87dtSosaPu7DkrvDh+ZxQNGVt6EHQsne15IngIgKCKGIaigaCDjppMXxz8SW6G55j2oAu58eDBEkGCZExYmCZqc6Qt+JOOHDzYRGOcZPeDgX/OkczEYLJ8iafCnWQ6SNA4G21T8Y9wiGzKyk/WkYE77bN/OPRmkcyx2s2yn2R7u8h4muqybTpa4casScEKkr1SZFbaPn/Hw4VMa9CNzAz8Exwi09sn2q71MbeSXKCquTGpF4lMbfutXxa7lORFeZAcm+xtInTvtZN2KUlrR4VE76zI1Jw9G4h2R0lkrCftKpNmwZ2qic8i0pp2XOtJrQ7brDSoHJ5MH/H6s+NQBQYRucySzHaUJFlhaxPyFMnMgycsIu9ss9irYXJyHtmF0h6tNa69FZ5wiIyjj+hWkun2hs86FX5WJAlXxufavXuLQ2Rqm3s8XIdExrq9raqsAzAvk/XuLQ6RTIcVCcnfS5IdB4kj1muLJI+lPXuLQ4SWWbUTm2I9+bci6dbZXr3FIkLLLHdm58ZV9Elb0FMk7T71KHfq6i1LZG6LQEJrAehovZA4cm62vffleZssiYx15xJlYsZjrlsLhDoo/9dbSjU0s8mAoqlV9rWut5UFHCoTqSQy1r1wisgaUm/VJ6LGof/Y12pW4zoQ9hJxLLNsRtlfJdmU/WsRus3V2VsBEIZm29fyZFMu6E0HlUTG+l+KpDvKi896RDQDKO9r6YDqua91+/iQKuwpb9btue096NYp4capD9Z7Z+va1zJ1VY0rYSoLoWtzI/AQefRgsIOj8vTjSWHx9KRzkDx7IFx4/ONXvr2sT0TTNBEPt8AirCXgwYSVA4hBA0rIM8mIkSixdfKAKgyDC/u+Wu8NHQ4XGp4j32kvYxWFgSiKVsOkVlnyVrARbJYJM1XID6QJWKQT/Qm7VAFBLLbKH1hZ2/iBq6s4WW/MDle8oCHeIeT47ry85MlW96pKyvy8zlntWcNA9rtfb5nMwXoDSOZ0W4Ik211hs7zoxhS5guQKZatbTRdgvQHEVfp/mSZp8yJ60QXSLC82kS0GZF30Acj9u7K9pF0gdaE1kS0SRIyWW0AQA+Mm8OtzkGOqNZEtDgSiT6qoFWhz63OQrXeAlM5v2HBh1dK1AtmDENkiQdwWk0SoE6SxmsgWB5KlQAAnLMg80ZrIFgdiDlw9EOSI/TD1NpmtOg8EqQstDAaudhlcfLHytnDZsqdAkNIE3St6ISBy8Gu5bM2TDEBCxiwuZfIMxNiz+VK21imAhIxZLBOAwF5n+9Z+FQiysp0DCfMkWW169jqbLVN9B4FkTeBtC5yBjyAL11oqTLbyRIeBWMO2KRSClDv9YrYy1wKZDM6f6HKcdIxZ0Hx7qiD35Pn/ua8iprpafLZK40GieBaf39PH0fBjOIUxC6YsXrtSPShZepfrW27rQT4HaqhmIxVHavoF67oKIFz/NY8gdeNn/tBs4evM6Q4SqdHlOAzovcKOKQACl6eytXB/QeLLMR7/kGoFK27DQDQXixzKTgVljxswKo6NwVBsCDgH5wMM/QbfNt18QU495Zz8RdkfyLEsLXT30OveSy+99Bs6ltWVlfHKMnoklhTNyPM0T5ITEtx8uNh7tYbt841vJyL0h4ep2qLfAj7/J/Lm7Y1Mxnv869a18bvWz3O4CBFRtJCIojAKQyyxjkVb7fqlTYTG2IVX7A/bZgc0wzq2n6O5icVza412yhvvc/4zn4Izuv+dBQrX7+aqlDw0kS9PV/xK4yO+OOftZ5xjo6tiwVWdKztdtl0vHY98buK+HUXZ8M7h6XEKjx/36PZ79BxZZRWTAEZBesaxFIGB24KOU2dlYMHWbB4SdLkbJXK3kfewRu1D5NNpYNR8H1ggSrO5dCKSVO7Bu1iK5GKivjOKWFg1su43y4y5EFnn03IBY927JCDzSbzq3KKtQ3YwUtpGCKNEkmU/CphIhHrAJZFGSDtpqCv52hJSbAyRHhkinY0mhAJ8clKZRH5lQwOARVvbhmWrPi/hQiSJjbmSF2dIB5KRJiDC0IBxbaUxO/XytSqYA5FtQe7hvXtlZO8kQ5naosfBMe1pZuNCRMSvSgd0050hECLVctiRaEsHbsoTYkLEkhAfgNG4JHISxMGureSBsf4iaXZAiVA5GsGAKvxOeOPUvs3IGFRbdG43697SH8/IqvCJWGvQbBcC8VVd4vw131hY5lYvkn3SOXybWRNCFzjVip0INVkqPMjrbsAEtLbo3NZyD2/656nEzJaQyvvJBEhiRmHVVir3KL1IyqymRKgTjdV/HwbHzwe1VSa7rnOzV0skZ5QI3bAnhEfU502tLsrBvRcky1QdQ0dKxJ4Qd4CfF32Wp09+epH8q+56WqSG4Wj98yCHlQ1JiCGBQhk6KONpQBD04pxH8DZ7moN4WfbgwF5d8Kh4c0XWb+Dfo2cRVARvfgdP+iH8pdlprKGtioudRztN8vq68zYv/ZPD9OKF1Ej+S4dUvbj9j68jsd4zqmhNs7V3uT7C5SshNVSMRgouHTOqBOe05ZKFB6qOR0KiThDbjWylNxpxkFx7VGeZjLAJrFjPaxUMogwPVPGovbeNrcZI8IeR206ylV7XfHdtN67Y62hJKAZZleKw+pP4n8BASrN16UPkbtwK/+1ohCuAjxpG8nCX34u/vW1Mm1IyZqvlNHp5dz1xEXvEiurXwiRCeaxpe/3LlzA/4xcqERqzNjdpJaZiq6Jv99TxZ2B9MWkKCgJRtTBUaLlZ0YF/+Zj9hMcviKlnmF48Di3rb+DHiFRqyykuQHBcWRD2vn8799/x/Ond27dp8bh79+mnJkctX2M9w4AxP7h/vsbOrMkdnT9/NI/1QRvBbCcaWS2a3MGTJ9Ha0I0slrWPJwdo4nDn4LCuDN3IfFln69UCTbxbrt7WlaEbidmarXZ/NXm0ipXBG6mz9epZwv08+gdvpM7W692EO3wfy4M3ss7WzrOUercXy8M3sliFDnmbUnNEDN8IZSs996YYvpGQreUC3dgAIz5bs94O2QAjPluvDtGDDTBC2Zod7aIHm2BksaJzbx82wch8+XoPfdgEI5jN0IuNMLJYoB/BCJNghpcYG+RGFQ6Dwhz9ODZSSIwFJLh1xTTMa93Z+m1o3cWWJyR98KBZ90a0Xs9r5fTxx/j4uYt9cELSq3mznnHFbDJBlx5y8MgAYe29CUXLgMAFhE72yjF4ZPBQcMcvdGSqMHmgcm/ojqAPR5RgDV1OznNrJlTUI6BEA3csMOGFI6kvpVJq1O1SoZRulzI+SqRdp1/B3wC2sN7dwwmsQM6Q534hWK6hRZVEte/8dsowBVglHVN9jK3SS8tKGtRuLUUuVYdUAO1SmSORdhkJ40VURsy+s4IZWzizfqWo1utdlOa0LTTXtI+K0ikXJFWGWTEqtGxKuVDtUlsY1ioV+7xMpL9rRAluBZ+CT41TiEYs84dkXPi/5YzjWkTpFh95KTPHUh6lYJapdinLmWmVagvjEunvGgGXgjNYIZSORuwEBMWclE6CGZgiSicY8Upa3hNcoJgKE6Vjy/dHHVLIVqkoyVkqzU6hFYrW0dgb4XBvxMgKRVtWH3LrnlJUo1VIioNkGPP4bbhvAFNUEUxyYh66WloRrdKy4JN2qeEGqTQ7i3+EPGnoZLvJvItlSMnTWbb57wJH9RLtM9lpbDpOnc3O/ACb/i1NA1ABvwAAAABJRU5ErkJggg==", + "description": "Displays changes to time-series data over time visualized with color ranges — for example, temperature or humidity readings.", + "descriptor": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "resources": [], + "templateHtml": "\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.rangeChartWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.rangeChartWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '80%',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: false,\n defaultDataKeysFunction: function() {\n return [{ name: 'temperature', label: 'Temperature', type: 'timeseries' }];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-range-chart-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-range-chart-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":0,\"realtime\":{\"realtimeType\":0,\"timewindowMs\":60000,\"quickInterval\":\"CURRENT_DAY\",\"interval\":1000},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000},\"timezone\":null},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"dataZoom\":true,\"rangeColors\":[{\"to\":-20,\"color\":\"#234CC7\"},{\"from\":-20,\"to\":0,\"color\":\"#305AD7\"},{\"from\":0,\"to\":10,\"color\":\"#7191EF\"},{\"from\":10,\"to\":20,\"color\":\"#FFA600\"},{\"from\":20,\"to\":30,\"color\":\"#F36900\"},{\"from\":30,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"color\":\"#D81838\"}],\"outOfRangeColor\":\"#ccc\",\"fillArea\":true,\"showLegend\":true,\"legendPosition\":\"top\",\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"showTooltip\":true,\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipShowDate\":true,\"tooltipDateFormat\":{\"format\":\"dd MMM yyyy HH:mm\",\"lastUpdateAgo\":false,\"custom\":false},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":4,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Range chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"thermostat\",\"iconColor\":\"#1F6BDD\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"units\":\"°C\",\"decimals\":0,\"noDataDisplayMessage\":\"\",\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true},\"margin\":\"0px\",\"borderRadius\":\"0px\",\"iconSize\":\"0px\"}" + }, + "externalId": null, + "tags": [ + "range", + "color range", + "line chart" + ] +} \ No newline at end of file diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts index 5bb6d9ea52..5e95d162cd 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts @@ -85,6 +85,9 @@ import { import { DoughnutBasicConfigComponent } from '@home/components/widget/config/basic/chart/doughnut-basic-config.component'; +import { + RangeChartBasicConfigComponent +} from '@home/components/widget/config/basic/chart/range-chart-basic-config.component'; @NgModule({ declarations: [ @@ -111,7 +114,8 @@ import { ThermometerScaleGaugeBasicConfigComponent, CompassGaugeBasicConfigComponent, LiquidLevelCardBasicConfigComponent, - DoughnutBasicConfigComponent + DoughnutBasicConfigComponent, + RangeChartBasicConfigComponent ], imports: [ CommonModule, @@ -142,7 +146,8 @@ import { ThermometerScaleGaugeBasicConfigComponent, CompassGaugeBasicConfigComponent, LiquidLevelCardBasicConfigComponent, - DoughnutBasicConfigComponent + DoughnutBasicConfigComponent, + RangeChartBasicConfigComponent ] }) export class BasicWidgetConfigModule { @@ -167,5 +172,6 @@ export const basicWidgetConfigComponentsMap: {[key: string]: Type diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.html new file mode 100644 index 0000000000..66b4054df1 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.html @@ -0,0 +1,222 @@ + + + + + + +
+
widget-config.appearance
+
+ + {{ 'widget-config.title' | translate }} + +
+ + + + + + + +
+
+
+ + {{ 'widgets.value-chart-card.icon' | translate }} + +
+ + + + + + + + +
+
+
+
+
widgets.range-chart.chart
+
+ + {{ 'widgets.range-chart.data-zoom' | translate }} + +
+
+
{{ 'widgets.range-chart.range-colors' | translate }}
+ + +
+
+
{{ 'widgets.range-chart.out-of-range-color' | translate }}
+ + +
+
+ + {{ 'widgets.range-chart.fill-area' | translate }} + +
+
+
widget-config.units-short
+ + +
+
+
widget-config.decimals-short
+ + + +
+
+
+ + + + + {{ 'widget-config.legend' | translate }} + + + + +
+
{{ 'legend.position' | translate }}
+ + + + {{ legendPositionTranslationMap.get(pos) | translate }} + + + +
+
+
{{ 'widgets.range-chart.legend-label' | translate }}
+
+ + + + +
+
+
+
+
+
+ + + + + {{ 'widgets.range-chart.tooltip' | translate }} + + + + +
+
{{ 'widgets.range-chart.tooltip-value' | translate }}
+
+ + + + +
+
+
+ + {{ 'widgets.range-chart.tooltip-date' | translate }} + +
+ + + + + +
+
+
+
{{ 'widgets.range-chart.tooltip-background-color' | translate }}
+ + +
+
+
{{ 'widgets.range-chart.tooltip-background-blur' | translate }}
+ + +
px
+
+
+
+
+
+
+
widget-config.card-appearance
+
+
{{ 'widgets.background.background' | translate }}
+ + +
+
+
widget-config.show-card-buttons
+ + {{ 'fullscreen.fullscreen' | translate }} + +
+
+
{{ 'widget-config.card-border-radius' | translate }}
+ + + +
+
+ + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.ts new file mode 100644 index 0000000000..5b22637296 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.ts @@ -0,0 +1,269 @@ +/// +/// Copyright © 2016-2023 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, Injector } from '@angular/core'; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { BasicWidgetConfigComponent } from '@home/components/widget/config/widget-config.component.models'; +import { WidgetConfigComponentData } from '@home/models/widget-component.models'; +import { DataKey, legendPositions, legendPositionTranslationMap, WidgetConfig, } from '@shared/models/widget.models'; +import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { + getTimewindowConfig, + setTimewindowConfig +} from '@home/components/widget/config/timewindow-config-panel.component'; +import { formatValue, isUndefined } from '@core/utils'; +import { + cssSizeToStrSize, + DateFormatProcessor, + DateFormatSettings, + resolveCssSize +} from '@shared/models/widget-settings.models'; +import { + rangeChartDefaultSettings, + RangeChartWidgetSettings +} from '@home/components/widget/lib/chart/range-chart-widget.models'; + +@Component({ + selector: 'tb-range-chart-basic-config', + templateUrl: './range-chart-basic-config.component.html', + styleUrls: ['../basic-config.scss'] +}) +export class RangeChartBasicConfigComponent extends BasicWidgetConfigComponent { + + legendPositions = legendPositions; + + legendPositionTranslationMap = legendPositionTranslationMap; + + rangeChartWidgetConfigForm: UntypedFormGroup; + + tooltipValuePreviewFn = this._tooltipValuePreviewFn.bind(this); + + tooltipDatePreviewFn = this._tooltipDatePreviewFn.bind(this); + + constructor(protected store: Store, + protected widgetConfigComponent: WidgetConfigComponent, + private $injector: Injector, + private fb: UntypedFormBuilder) { + super(store, widgetConfigComponent); + } + + protected configForm(): UntypedFormGroup { + return this.rangeChartWidgetConfigForm; + } + + protected defaultDataKeys(configData: WidgetConfigComponentData): DataKey[] { + return [{ name: 'temperature', label: 'Temperature', type: DataKeyType.timeseries }]; + } + + protected onConfigSet(configData: WidgetConfigComponentData) { + const settings: RangeChartWidgetSettings = {...rangeChartDefaultSettings, ...(configData.config.settings || {})}; + const iconSize = resolveCssSize(configData.config.iconSize); + this.rangeChartWidgetConfigForm = this.fb.group({ + timewindowConfig: [getTimewindowConfig(configData.config), []], + datasources: [configData.config.datasources, []], + + showTitle: [configData.config.showTitle, []], + title: [configData.config.title, []], + titleFont: [configData.config.titleFont, []], + titleColor: [configData.config.titleColor, []], + + showIcon: [configData.config.showTitleIcon, []], + iconSize: [iconSize[0], [Validators.min(0)]], + iconSizeUnit: [iconSize[1], []], + icon: [configData.config.titleIcon, []], + iconColor: [configData.config.iconColor, []], + + dataZoom: [settings.dataZoom, []], + rangeColors: [settings.rangeColors, []], + outOfRangeColor: [settings.outOfRangeColor, []], + fillArea: [settings.fillArea, []], + units: [configData.config.units, []], + decimals: [configData.config.decimals, []], + + showLegend: [settings.showLegend, []], + legendPosition: [settings.legendPosition, []], + legendLabelFont: [settings.legendLabelFont, []], + legendLabelColor: [settings.legendLabelColor, []], + + showTooltip: [settings.showTooltip, []], + tooltipValueFont: [settings.tooltipValueFont, []], + tooltipValueColor: [settings.tooltipValueColor, []], + tooltipShowDate: [settings.tooltipShowDate, []], + tooltipDateFormat: [settings.tooltipDateFormat, []], + tooltipDateFont: [settings.tooltipDateFont, []], + tooltipDateColor: [settings.tooltipDateColor, []], + tooltipBackgroundColor: [settings.tooltipBackgroundColor, []], + tooltipBackgroundBlur: [settings.tooltipBackgroundBlur, []], + + background: [settings.background, []], + + cardButtons: [this.getCardButtons(configData.config), []], + borderRadius: [configData.config.borderRadius, []], + + actions: [configData.config.actions || {}, []] + }); + } + + protected prepareOutputConfig(config: any): WidgetConfigComponentData { + setTimewindowConfig(this.widgetConfig.config, config.timewindowConfig); + this.widgetConfig.config.datasources = config.datasources; + + this.widgetConfig.config.showTitle = config.showTitle; + this.widgetConfig.config.title = config.title; + this.widgetConfig.config.titleFont = config.titleFont; + this.widgetConfig.config.titleColor = config.titleColor; + + this.widgetConfig.config.showTitleIcon = config.showIcon; + this.widgetConfig.config.iconSize = cssSizeToStrSize(config.iconSize, config.iconSizeUnit); + this.widgetConfig.config.titleIcon = config.icon; + this.widgetConfig.config.iconColor = config.iconColor; + + this.widgetConfig.config.settings = this.widgetConfig.config.settings || {}; + + this.widgetConfig.config.settings.dataZoom = config.dataZoom; + this.widgetConfig.config.settings.rangeColors = config.rangeColors; + this.widgetConfig.config.settings.outOfRangeColor = config.outOfRangeColor; + this.widgetConfig.config.settings.fillArea = config.fillArea; + this.widgetConfig.config.units = config.units; + this.widgetConfig.config.decimals = config.decimals; + + this.widgetConfig.config.settings.showLegend = config.showLegend; + this.widgetConfig.config.settings.legendPosition = config.legendPosition; + this.widgetConfig.config.settings.legendLabelFont = config.legendLabelFont; + this.widgetConfig.config.settings.legendLabelColor = config.legendLabelColor; + + this.widgetConfig.config.settings.showTooltip = config.showTooltip; + this.widgetConfig.config.settings.tooltipValueFont = config.tooltipValueFont; + this.widgetConfig.config.settings.tooltipValueColor = config.tooltipValueColor; + this.widgetConfig.config.settings.tooltipShowDate = config.tooltipShowDate; + this.widgetConfig.config.settings.tooltipDateFormat = config.tooltipDateFormat; + this.widgetConfig.config.settings.tooltipDateFont = config.tooltipDateFont; + this.widgetConfig.config.settings.tooltipDateColor = config.tooltipDateColor; + this.widgetConfig.config.settings.tooltipBackgroundColor = config.tooltipBackgroundColor; + this.widgetConfig.config.settings.tooltipBackgroundBlur = config.tooltipBackgroundBlur; + + this.widgetConfig.config.settings.background = config.background; + + this.setCardButtons(config.cardButtons, this.widgetConfig.config); + this.widgetConfig.config.borderRadius = config.borderRadius; + + this.widgetConfig.config.actions = config.actions; + return this.widgetConfig; + } + + protected validatorTriggers(): string[] { + return ['showTitle', 'showIcon', 'showLegend', 'showTooltip', 'tooltipShowDate']; + } + + protected updateValidators(emitEvent: boolean, trigger?: string) { + const showTitle: boolean = this.rangeChartWidgetConfigForm.get('showTitle').value; + const showIcon: boolean = this.rangeChartWidgetConfigForm.get('showIcon').value; + const showLegend: boolean = this.rangeChartWidgetConfigForm.get('showLegend').value; + const showTooltip: boolean = this.rangeChartWidgetConfigForm.get('showTooltip').value; + const tooltipShowDate: boolean = this.rangeChartWidgetConfigForm.get('tooltipShowDate').value; + + if (showTitle) { + this.rangeChartWidgetConfigForm.get('title').enable(); + this.rangeChartWidgetConfigForm.get('titleFont').enable(); + this.rangeChartWidgetConfigForm.get('titleColor').enable(); + this.rangeChartWidgetConfigForm.get('showIcon').enable({emitEvent: false}); + if (showIcon) { + this.rangeChartWidgetConfigForm.get('iconSize').enable(); + this.rangeChartWidgetConfigForm.get('iconSizeUnit').enable(); + this.rangeChartWidgetConfigForm.get('icon').enable(); + this.rangeChartWidgetConfigForm.get('iconColor').enable(); + } else { + this.rangeChartWidgetConfigForm.get('iconSize').disable(); + this.rangeChartWidgetConfigForm.get('iconSizeUnit').disable(); + this.rangeChartWidgetConfigForm.get('icon').disable(); + this.rangeChartWidgetConfigForm.get('iconColor').disable(); + } + } else { + this.rangeChartWidgetConfigForm.get('title').disable(); + this.rangeChartWidgetConfigForm.get('titleFont').disable(); + this.rangeChartWidgetConfigForm.get('titleColor').disable(); + this.rangeChartWidgetConfigForm.get('showIcon').disable({emitEvent: false}); + this.rangeChartWidgetConfigForm.get('iconSize').disable(); + this.rangeChartWidgetConfigForm.get('iconSizeUnit').disable(); + this.rangeChartWidgetConfigForm.get('icon').disable(); + this.rangeChartWidgetConfigForm.get('iconColor').disable(); + } + + if (showLegend) { + this.rangeChartWidgetConfigForm.get('legendPosition').enable(); + this.rangeChartWidgetConfigForm.get('legendLabelFont').enable(); + this.rangeChartWidgetConfigForm.get('legendLabelColor').enable(); + } else { + this.rangeChartWidgetConfigForm.get('legendPosition').disable(); + this.rangeChartWidgetConfigForm.get('legendLabelFont').disable(); + this.rangeChartWidgetConfigForm.get('legendLabelColor').disable(); + } + + if (showTooltip) { + this.rangeChartWidgetConfigForm.get('tooltipValueFont').enable(); + this.rangeChartWidgetConfigForm.get('tooltipValueColor').enable(); + this.rangeChartWidgetConfigForm.get('tooltipShowDate').enable({emitEvent: false}); + this.rangeChartWidgetConfigForm.get('tooltipBackgroundColor').enable(); + this.rangeChartWidgetConfigForm.get('tooltipBackgroundBlur').enable(); + if (tooltipShowDate) { + this.rangeChartWidgetConfigForm.get('tooltipDateFormat').enable(); + this.rangeChartWidgetConfigForm.get('tooltipDateFont').enable(); + this.rangeChartWidgetConfigForm.get('tooltipDateColor').enable(); + } else { + this.rangeChartWidgetConfigForm.get('tooltipDateFormat').disable(); + this.rangeChartWidgetConfigForm.get('tooltipDateFont').disable(); + this.rangeChartWidgetConfigForm.get('tooltipDateColor').disable(); + } + } else { + this.rangeChartWidgetConfigForm.get('tooltipValueFont').disable(); + this.rangeChartWidgetConfigForm.get('tooltipValueColor').disable(); + this.rangeChartWidgetConfigForm.get('tooltipShowDate').disable({emitEvent: false}); + this.rangeChartWidgetConfigForm.get('tooltipDateFormat').disable(); + this.rangeChartWidgetConfigForm.get('tooltipDateFont').disable(); + this.rangeChartWidgetConfigForm.get('tooltipDateColor').disable(); + this.rangeChartWidgetConfigForm.get('tooltipBackgroundColor').disable(); + this.rangeChartWidgetConfigForm.get('tooltipBackgroundBlur').disable(); + } + } + + private getCardButtons(config: WidgetConfig): string[] { + const buttons: string[] = []; + if (isUndefined(config.enableFullscreen) || config.enableFullscreen) { + buttons.push('fullscreen'); + } + return buttons; + } + + private setCardButtons(buttons: string[], config: WidgetConfig) { + config.enableFullscreen = buttons.includes('fullscreen'); + } + + private _tooltipValuePreviewFn(): string { + const units: string = this.rangeChartWidgetConfigForm.get('units').value; + const decimals: number = this.rangeChartWidgetConfigForm.get('decimals').value; + return formatValue(22, decimals, units, false); + } + + private _tooltipDatePreviewFn(): string { + const dateFormat: DateFormatSettings = this.rangeChartWidgetConfigForm.get('tooltipDateFormat').value; + const processor = DateFormatProcessor.fromSettings(this.$injector, dateFormat); + processor.update(Date.now()); + return processor.formatted; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.html new file mode 100644 index 0000000000..db9e828a0c --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.html @@ -0,0 +1,34 @@ + +
+
+ +
+
+
+
+
+
+
+
{{ rangeItem.label }}
+
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.scss new file mode 100644 index 0000000000..d9b42c6acb --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.scss @@ -0,0 +1,105 @@ +/** + * Copyright © 2016-2023 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. + */ + +.tb-range-chart-panel { + width: 100%; + height: 100%; + position: relative; + display: flex; + flex-direction: column; + gap: 16px; + padding: 20px 24px 24px 24px; + > div:not(.tb-range-chart-overlay) { + z-index: 1; + } + .tb-range-chart-overlay { + position: absolute; + top: 12px; + left: 12px; + bottom: 12px; + right: 12px; + } + div.tb-widget-title { + padding: 0; + } + .tb-range-chart-content { + flex: 1; + min-width: 0; + min-height: 0; + display: flex; + flex-direction: column; + gap: 16px; + &.legend-top { + flex-direction: column-reverse; + } + &.legend-right { + flex-direction: row; + } + &.legend-left { + flex-direction: row-reverse; + } + .tb-range-chart-shape { + flex: 1; + min-width: 0; + min-height: 0; + display: flex; + align-items: center; + justify-content: center; + } + .tb-range-chart-legend { + display: flex; + justify-content: center; + align-items: center; + align-self: stretch; + flex-wrap: wrap; + column-gap: 24px; + row-gap: 8px; + .tb-range-chart-legend-item { + display: flex; + flex-direction: column; + justify-content: center; + align-items: flex-start; + user-select: none; + cursor: pointer; + .tb-range-chart-legend-item-label { + display: flex; + align-items: center; + gap: 4px; + color: #ccc; + .tb-range-chart-legend-item-label-circle { + width: 8px; + height: 8px; + border-radius: 50%; + background-color: #ccc; + } + } + } + } + &.legend-right, &.legend-left { + gap: 24px; + .tb-range-chart-legend { + flex-direction: column-reverse; + justify-content: flex-end; + align-items: stretch; + .tb-range-chart-legend-item { + flex-direction: row; + justify-content: space-between; + align-items: center; + } + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.ts new file mode 100644 index 0000000000..80e59c3512 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.ts @@ -0,0 +1,527 @@ +/// +/// Copyright © 2016-2023 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 { + AfterViewInit, + ChangeDetectorRef, + Component, + ElementRef, + Input, + OnDestroy, + OnInit, + Renderer2, + TemplateRef, + ViewChild, + ViewEncapsulation +} from '@angular/core'; +import { WidgetContext } from '@home/models/widget-component.models'; +import { + backgroundStyle, + ColorRange, + ComponentStyle, + DateFormatProcessor, filterIncludingColorRanges, + getDataKey, + overlayStyle, + sortedColorRange, + textStyle +} from '@shared/models/widget-settings.models'; +import { ResizeObserver } from '@juggle/resize-observer'; +import * as echarts from 'echarts/core'; +import { formatValue, isDefinedAndNotNull, isNumber } from '@core/utils'; +import { + DataZoomComponent, + DataZoomComponentOption, + GridComponent, + GridComponentOption, + MarkLineComponent, + MarkLineComponentOption, + TooltipComponent, + TooltipComponentOption, + VisualMapComponent, + VisualMapComponentOption +} from 'echarts/components'; +import { LineChart, LineSeriesOption, } from 'echarts/charts'; +import { CanvasRenderer } from 'echarts/renderers'; +import { rangeChartDefaultSettings, RangeChartWidgetSettings } from './range-chart-widget.models'; +import { DataSet } from '@shared/models/widget.models'; + +echarts.use([ + TooltipComponent, + GridComponent, + VisualMapComponent, + DataZoomComponent, + MarkLineComponent, + LineChart, + CanvasRenderer +]); + +type EChartsOption = echarts.ComposeOption< + | TooltipComponentOption + | GridComponentOption + | VisualMapComponentOption + | DataZoomComponentOption + | MarkLineComponentOption + | LineSeriesOption +>; + +type ECharts = echarts.ECharts; + +interface VisualPiece { + lt?: number; + gt?: number; + lte?: number; + gte?: number; + value?: number; + color?: string; +} + +interface RangeItem { + index: number; + from?: number; + to?: number; + piece: VisualPiece; + color: string; + label: string; + visible: boolean; + enabled: boolean; +} + +const rangeItemLabel = (from?: number, to?: number): string => { + if (isNumber(from) && isNumber(to)) { + if (from === to) { + return `${from}`; + } else { + return `${from} - ${to}`; + } + } else if (isNumber(from)) { + return `≥ ${from}`; + } else if (isNumber(to)) { + return `< ${to}`; + } else { + return null; + } +}; + +const toVisualPiece = (color: string, from?: number, to?: number): VisualPiece => { + const piece: VisualPiece = { + color + }; + if (isNumber(from) && isNumber(to)) { + if (from === to) { + piece.value = from; + } else { + piece.gte = from; + piece.lt = to; + } + } else if (isNumber(from)) { + piece.gte = from; + } else if (isNumber(to)) { + piece.lt = to; + } + return piece; +}; + +const toRangeItems = (colorRanges: Array): RangeItem[] => { + const rangeItems: RangeItem[] = []; + let counter = 0; + const ranges = sortedColorRange(filterIncludingColorRanges(colorRanges)).filter(r => isNumber(r.from) || isNumber(r.to)); + for (let i = 0; i < ranges.length; i++) { + const range = ranges[i]; + let from = range.from; + const to = range.to; + if (i > 0) { + const prevRange = ranges[i - 1]; + if (isNumber(prevRange.to) && isNumber(from) && from < prevRange.to) { + from = prevRange.to; + } + } + rangeItems.push( + { + index: counter++, + color: range.color, + enabled: true, + visible: true, + from, + to, + label: rangeItemLabel(from, to), + piece: toVisualPiece(range.color, from, to) + } + ); + if (!isNumber(from) || !isNumber(to)) { + const value = !isNumber(from) ? to : from; + rangeItems.push( + { + index: counter++, + color: 'transparent', + enabled: true, + visible: false, + label: '', + piece: { gt: value - 0.000000001, lt: value + 0.000000001, color: 'transparent'} + } + ); + } + } + return rangeItems; +}; + +const toNamedData = (data: DataSet): {name: string; value: [number, any]}[] => { + if (!data?.length) { + return []; + } else { + return data.map(d => ({ + name: d[0] + '', + value: d + })); + } +}; + +const getMarkPoints = (ranges: Array): number[] => { + const points = new Set(); + for (const range of ranges) { + if (range.visible) { + if (isNumber(range.from)) { + points.add(range.from); + } + if (isNumber(range.to)) { + points.add(range.to); + } + } + } + return Array.from(points).sort(); +}; + +@Component({ + selector: 'tb-range-chart-widget', + templateUrl: './range-chart-widget.component.html', + styleUrls: ['./range-chart-widget.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class RangeChartWidgetComponent implements OnInit, OnDestroy, AfterViewInit { + + @ViewChild('chartShape', {static: false}) + chartShape: ElementRef; + + settings: RangeChartWidgetSettings; + + @Input() + ctx: WidgetContext; + + @Input() + widgetTitlePanel: TemplateRef; + + showLegend: boolean; + legendClass: string; + + backgroundStyle: ComponentStyle = {}; + overlayStyle: ComponentStyle = {}; + + legendLabelStyle: ComponentStyle; + disabledLegendLabelStyle: ComponentStyle; + visibleRangeItems: RangeItem[]; + + private rangeItems: RangeItem[]; + + private shapeResize$: ResizeObserver; + + private decimals = 0; + private units = ''; + + private drawChartPending = false; + private rangeChart: ECharts; + private rangeChartOptions: EChartsOption; + private selectedRanges: {[key: number]: boolean} = {}; + + private tooltipDateFormat: DateFormatProcessor; + + constructor(private renderer: Renderer2, + private cd: ChangeDetectorRef) { + } + + ngOnInit(): void { + this.ctx.$scope.rangeChartWidget = this; + this.settings = {...rangeChartDefaultSettings, ...this.ctx.settings}; + + this.decimals = this.ctx.decimals; + this.units = this.ctx.units; + const dataKey = getDataKey(this.ctx.datasources); + if (isDefinedAndNotNull(dataKey?.decimals)) { + this.decimals = dataKey.decimals; + } + if (dataKey?.units) { + this.units = dataKey.units; + } + + + this.backgroundStyle = backgroundStyle(this.settings.background); + this.overlayStyle = overlayStyle(this.settings.background.overlay); + + this.rangeItems = toRangeItems(this.settings.rangeColors); + this.visibleRangeItems = this.rangeItems.filter(item => item.visible); + for (const range of this.rangeItems) { + this.selectedRanges[range.index] = true; + } + + this.showLegend = this.settings.showLegend && !!this.rangeItems.length; + + if (this.showLegend) { + this.legendClass = `legend-${this.settings.legendPosition}`; + this.legendLabelStyle = textStyle(this.settings.legendLabelFont); + this.disabledLegendLabelStyle = textStyle(this.settings.legendLabelFont); + this.legendLabelStyle.color = this.settings.legendLabelColor; + } + + if (this.settings.showTooltip && this.settings.tooltipShowDate) { + this.tooltipDateFormat = DateFormatProcessor.fromSettings(this.ctx.$injector, this.settings.tooltipDateFormat); + } + } + + ngAfterViewInit() { + if (this.drawChartPending) { + this.drawChart(); + } + } + + ngOnDestroy() { + if (this.shapeResize$) { + this.shapeResize$.disconnect(); + } + if (this.rangeChart) { + this.rangeChart.dispose(); + } + } + + public onInit() { + const borderRadius = this.ctx.$widgetElement.css('borderRadius'); + this.overlayStyle = {...this.overlayStyle, ...{borderRadius}}; + if (this.chartShape) { + this.drawChart(); + } else { + this.drawChartPending = true; + } + this.cd.detectChanges(); + } + + public onDataUpdated() { + if (this.rangeChart) { + this.rangeChart.setOption({ + xAxis: { + min: this.ctx.defaultSubscription.timeWindow.minTime, + max: this.ctx.defaultSubscription.timeWindow.maxTime + }, + series: [ + {data: this.ctx.data?.length ? toNamedData(this.ctx.data[0].data) : []} + ], + visualMap: { + selected: this.selectedRanges + } + }); + } + } + + public toggleRangeItem(item: RangeItem) { + item.enabled = !item.enabled; + this.selectedRanges[item.index] = item.enabled; + this.rangeChart.dispatchAction({ + type: 'selectDataRange', + selected: this.selectedRanges + }); + } + + private drawChart() { + const dataKey = getDataKey(this.ctx.datasources); + this.rangeChart = echarts.init(this.chartShape.nativeElement, null, { + renderer: 'canvas', + }); + this.rangeChartOptions = { + tooltip: { + trigger: 'none' + }, + grid: { + containLabel: true, + top: '30', + left: 0, + right: 0, + bottom: this.settings.dataZoom ? 60 : 0 + }, + xAxis: { + type: 'time', + axisTick: { + show: true + }, + axisLabel: { + hideOverlap: true, + fontSize: 10 + }, + axisLine: { + onZero: false + }, + min: this.ctx.defaultSubscription.timeWindow.minTime, + max: this.ctx.defaultSubscription.timeWindow.maxTime + }, + yAxis: { + type: 'value', + axisLabel: { + formatter: value => formatValue(value, this.decimals, this.units, false) + } + }, + series: [{ + type: 'line', + name: dataKey?.label, + smooth: false, + showSymbol: false, + animation: true, + areaStyle: this.settings.fillArea ? {} : undefined, + data: this.ctx.data?.length ? toNamedData(this.ctx.data[0].data) : [], + markLine: this.rangeItems.length ? { + animation: true, + symbol: ['circle', 'arrow'], + symbolSize: [5, 7], + lineStyle: { + width: 1, + type: [3, 3], + color: '#37383b' + }, + label: { + position: 'insideEndTop', + color: '#37383b', + backgroundColor: 'rgba(255,255,255,0.56)', + padding: [4, 5], + borderRadius: 4, + formatter: params => formatValue(params.value, this.decimals, this.units, false) + }, + emphasis: { + disabled: true + }, + data: getMarkPoints(this.rangeItems).map(point => ({ yAxis: point })) + } : undefined + }], + dataZoom: [ + { + type: 'inside', + disabled: !this.settings.dataZoom + }, + { + type: 'slider', + show: this.settings.dataZoom, + showDetail: false, + right: 10 + } + ], + visualMap: { + show: false, + type: 'piecewise', + selected: this.selectedRanges, + pieces: this.rangeItems.map(item => item.piece), + outOfRange: { + color: this.settings.outOfRangeColor + }, + inRange: !this.rangeItems.length ? { + color: this.settings.outOfRangeColor + } : undefined + } + }; + + if (this.settings.showTooltip) { + this.rangeChartOptions.tooltip = { + trigger: 'axis', + formatter: (params) => { + if (!params.length || !params[0]) { + return null; + } + const seriesParams = params[0]; + const value = formatValue(seriesParams.value[1], this.decimals, this.units, false); + const tooltipElement: HTMLElement = this.renderer.createElement('div'); + this.renderer.setStyle(tooltipElement, 'display', 'flex'); + this.renderer.setStyle(tooltipElement, 'flex-direction', 'column'); + this.renderer.setStyle(tooltipElement, 'align-items', 'flex-start'); + this.renderer.setStyle(tooltipElement, 'gap', '4px'); + if (this.settings.tooltipShowDate) { + const dateElement: HTMLElement = this.renderer.createElement('div'); + const ts = seriesParams.value[0]; + this.tooltipDateFormat.update(ts); + this.renderer.appendChild(dateElement, this.renderer.createText(this.tooltipDateFormat.formatted)); + this.renderer.setStyle(dateElement, 'font-family', this.settings.tooltipDateFont.family); + this.renderer.setStyle(dateElement, 'font-size', this.settings.tooltipDateFont.size + this.settings.tooltipDateFont.sizeUnit); + this.renderer.setStyle(dateElement, 'font-style', this.settings.tooltipDateFont.style); + this.renderer.setStyle(dateElement, 'font-weight', this.settings.tooltipDateFont.weight); + this.renderer.setStyle(dateElement, 'line-height', this.settings.tooltipDateFont.lineHeight); + this.renderer.setStyle(dateElement, 'color', this.settings.tooltipDateColor); + this.renderer.appendChild(tooltipElement, dateElement); + } + const labelValueElement: HTMLElement = this.renderer.createElement('div'); + this.renderer.setStyle(labelValueElement, 'display', 'flex'); + this.renderer.setStyle(labelValueElement, 'flex-direction', 'row'); + this.renderer.setStyle(labelValueElement, 'align-items', 'center'); + this.renderer.setStyle(labelValueElement, 'align-self', 'stretch'); + this.renderer.setStyle(labelValueElement, 'gap', '12px'); + this.renderer.appendChild(tooltipElement, labelValueElement); + const labelElement: HTMLElement = this.renderer.createElement('div'); + this.renderer.setStyle(labelElement, 'display', 'flex'); + this.renderer.setStyle(labelElement, 'align-items', 'center'); + this.renderer.setStyle(labelElement, 'gap', '8px'); + this.renderer.appendChild(labelValueElement, labelElement); + const circleElement: HTMLElement = this.renderer.createElement('div'); + this.renderer.setStyle(circleElement, 'width', '8px'); + this.renderer.setStyle(circleElement, 'height', '8px'); + this.renderer.setStyle(circleElement, 'border-radius', '50%'); + this.renderer.setStyle(circleElement, 'background', seriesParams.color); + this.renderer.appendChild(labelElement, circleElement); + const labelTextElement: HTMLElement = this.renderer.createElement('div'); + this.renderer.appendChild(labelTextElement, this.renderer.createText(seriesParams.seriesName)); + this.renderer.setStyle(labelTextElement, 'font-family', 'Roboto'); + this.renderer.setStyle(labelTextElement, 'font-size', '12px'); + this.renderer.setStyle(labelTextElement, 'font-style', 'normal'); + this.renderer.setStyle(labelTextElement, 'font-weight', '400'); + this.renderer.setStyle(labelTextElement, 'line-height', '16px'); + this.renderer.setStyle(labelTextElement, 'letter-spacing', '0.4px'); + this.renderer.setStyle(labelTextElement, 'color', 'rgba(0, 0, 0, 0.76)'); + this.renderer.appendChild(labelElement, labelTextElement); + const valueElement: HTMLElement = this.renderer.createElement('div'); + this.renderer.appendChild(valueElement, this.renderer.createText(value)); + this.renderer.setStyle(valueElement, 'font-family', this.settings.tooltipValueFont.family); + this.renderer.setStyle(valueElement, 'font-size', this.settings.tooltipValueFont.size + this.settings.tooltipValueFont.sizeUnit); + this.renderer.setStyle(valueElement, 'font-style', this.settings.tooltipValueFont.style); + this.renderer.setStyle(valueElement, 'font-weight', this.settings.tooltipValueFont.weight); + this.renderer.setStyle(valueElement, 'line-height', this.settings.tooltipValueFont.lineHeight); + this.renderer.setStyle(valueElement, 'color', this.settings.tooltipValueColor); + this.renderer.appendChild(labelValueElement, valueElement); + return tooltipElement; + }, + padding: [8, 12], + backgroundColor: this.settings.tooltipBackgroundColor, + extraCssText: `line-height: 1; backdrop-filter: blur(${this.settings.tooltipBackgroundBlur}px);` + }; + } + + this.rangeChart.setOption(this.rangeChartOptions); + + this.shapeResize$ = new ResizeObserver(() => { + this.onResize(); + }); + this.shapeResize$.observe(this.chartShape.nativeElement); + this.onResize(); + } + + private onResize() { + const width = this.rangeChart.getWidth(); + const height = this.rangeChart.getHeight(); + const shapeWidth = this.chartShape.nativeElement.offsetWidth; + const shapeHeight = this.chartShape.nativeElement.offsetHeight; + if (width !== shapeWidth || height !== shapeHeight) { + this.rangeChart.resize(); + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts new file mode 100644 index 0000000000..6bcd0c1497 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts @@ -0,0 +1,103 @@ +/// +/// Copyright © 2016-2023 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 { + BackgroundSettings, + BackgroundType, + ColorRange, + DateFormatSettings, + Font, simpleDateFormat +} from '@shared/models/widget-settings.models'; +import { LegendPosition } from '@shared/models/widget.models'; + +export interface RangeChartWidgetSettings { + dataZoom: boolean; + rangeColors: Array; + outOfRangeColor: string; + fillArea: boolean; + showLegend: boolean; + legendPosition: LegendPosition; + legendLabelFont: Font; + legendLabelColor: string; + showTooltip: boolean; + tooltipValueFont: Font; + tooltipValueColor: string; + tooltipShowDate: boolean; + tooltipDateFormat: DateFormatSettings; + tooltipDateFont: Font; + tooltipDateColor: string; + tooltipBackgroundColor: string; + tooltipBackgroundBlur: number; + background: BackgroundSettings; +} + +export const rangeChartDefaultSettings: RangeChartWidgetSettings = { + dataZoom: true, + rangeColors: [ + {to: -20, color: '#234CC7'}, + {from: -20, to: 0, color: '#305AD7'}, + {from: 0, to: 10, color: '#7191EF'}, + {from: 10, to: 20, color: '#FFA600'}, + {from: 20, to: 30, color: '#F36900'}, + {from: 30, to: 40, color: '#F04022'}, + {from: 40, color: '#D81838'} + ], + outOfRangeColor: '#ccc', + fillArea: true, + showLegend: true, + legendPosition: LegendPosition.top, + legendLabelFont: { + family: 'Roboto', + size: 12, + sizeUnit: 'px', + style: 'normal', + weight: '400', + lineHeight: '16px' + }, + legendLabelColor: 'rgba(0, 0, 0, 0.76)', + showTooltip: true, + tooltipValueFont: { + family: 'Roboto', + size: 12, + sizeUnit: 'px', + style: 'normal', + weight: '500', + lineHeight: '16px' + }, + tooltipValueColor: 'rgba(0, 0, 0, 0.76)', + tooltipShowDate: true, + tooltipDateFormat: simpleDateFormat('dd MMM yyyy HH:mm'), + tooltipDateFont: { + family: 'Roboto', + size: 11, + sizeUnit: 'px', + style: 'normal', + weight: '400', + lineHeight: '16px' + }, + tooltipDateColor: 'rgba(0, 0, 0, 0.76)', + tooltipBackgroundColor: 'rgba(255, 255, 255, 0.76)', + tooltipBackgroundBlur: 4, + background: { + type: BackgroundType.color, + color: '#fff', + overlay: { + enabled: false, + color: 'rgba(255,255,255,0.72)', + blur: 3 + } + } +}; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.html new file mode 100644 index 0000000000..155d09f53f --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.html @@ -0,0 +1,140 @@ + + +
+
widgets.range-chart.range-chart-card-style
+
+ + {{ 'widgets.range-chart.data-zoom' | translate }} + +
+
+
{{ 'widgets.range-chart.range-colors' | translate }}
+ + +
+
+
{{ 'widgets.range-chart.out-of-range-color' | translate }}
+ + +
+
+ + {{ 'widgets.range-chart.fill-area' | translate }} + +
+
+ + + + + {{ 'widget-config.legend' | translate }} + + + + +
+
{{ 'legend.position' | translate }}
+ + + + {{ legendPositionTranslationMap.get(pos) | translate }} + + + +
+
+
{{ 'widgets.range-chart.legend-label' | translate }}
+
+ + + + +
+
+
+
+
+
+ + + + + {{ 'widgets.range-chart.tooltip' | translate }} + + + + +
+
{{ 'widgets.range-chart.tooltip-value' | translate }}
+
+ + + + +
+
+
+ + {{ 'widgets.range-chart.tooltip-date' | translate }} + +
+ + + + + +
+
+
+
{{ 'widgets.range-chart.tooltip-background-color' | translate }}
+ + +
+
+
{{ 'widgets.range-chart.tooltip-background-blur' | translate }}
+ + +
px
+
+
+
+
+
+
+
{{ 'widgets.background.background' | translate }}
+ + +
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.ts new file mode 100644 index 0000000000..f5fa02065a --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.ts @@ -0,0 +1,147 @@ +/// +/// Copyright © 2016-2023 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, Injector } from '@angular/core'; +import { + legendPositions, + legendPositionTranslationMap, + WidgetSettings, + WidgetSettingsComponent +} from '@shared/models/widget.models'; +import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { formatValue } from '@core/utils'; +import { rangeChartDefaultSettings } from '@home/components/widget/lib/chart/range-chart-widget.models'; +import { DateFormatProcessor, DateFormatSettings } from '@shared/models/widget-settings.models'; + +@Component({ + selector: 'tb-range-chart-widget-settings', + templateUrl: './range-chart-widget-settings.component.html', + styleUrls: [] +}) +export class RangeChartWidgetSettingsComponent extends WidgetSettingsComponent { + + legendPositions = legendPositions; + + legendPositionTranslationMap = legendPositionTranslationMap; + + rangeChartWidgetSettingsForm: UntypedFormGroup; + + tooltipValuePreviewFn = this._tooltipValuePreviewFn.bind(this); + + tooltipDatePreviewFn = this._tooltipDatePreviewFn.bind(this); + + constructor(protected store: Store, + private $injector: Injector, + private fb: UntypedFormBuilder) { + super(store); + } + + protected settingsForm(): UntypedFormGroup { + return this.rangeChartWidgetSettingsForm; + } + + protected defaultSettings(): WidgetSettings { + return {...rangeChartDefaultSettings}; + } + + protected onSettingsSet(settings: WidgetSettings) { + this.rangeChartWidgetSettingsForm = this.fb.group({ + dataZoom: [settings.dataZoom, []], + rangeColors: [settings.rangeColors, []], + outOfRangeColor: [settings.outOfRangeColor, []], + fillArea: [settings.fillArea, []], + + showLegend: [settings.showLegend, []], + legendPosition: [settings.legendPosition, []], + legendLabelFont: [settings.legendLabelFont, []], + legendLabelColor: [settings.legendLabelColor, []], + + showTooltip: [settings.showTooltip, []], + tooltipValueFont: [settings.tooltipValueFont, []], + tooltipValueColor: [settings.tooltipValueColor, []], + tooltipShowDate: [settings.tooltipShowDate, []], + tooltipDateFormat: [settings.tooltipDateFormat, []], + tooltipDateFont: [settings.tooltipDateFont, []], + tooltipDateColor: [settings.tooltipDateColor, []], + tooltipBackgroundColor: [settings.tooltipBackgroundColor, []], + tooltipBackgroundBlur: [settings.tooltipBackgroundBlur, []], + + background: [settings.background, []] + }); + } + + protected validatorTriggers(): string[] { + return ['showLegend', 'showTooltip', 'tooltipShowDate']; + } + + protected updateValidators(emitEvent: boolean) { + const showLegend: boolean = this.rangeChartWidgetSettingsForm.get('showLegend').value; + const showTooltip: boolean = this.rangeChartWidgetSettingsForm.get('showTooltip').value; + const tooltipShowDate: boolean = this.rangeChartWidgetSettingsForm.get('tooltipShowDate').value; + + if (showLegend) { + this.rangeChartWidgetSettingsForm.get('legendPosition').enable(); + this.rangeChartWidgetSettingsForm.get('legendLabelFont').enable(); + this.rangeChartWidgetSettingsForm.get('legendLabelColor').enable(); + } else { + this.rangeChartWidgetSettingsForm.get('legendPosition').disable(); + this.rangeChartWidgetSettingsForm.get('legendLabelFont').disable(); + this.rangeChartWidgetSettingsForm.get('legendLabelColor').disable(); + } + + if (showTooltip) { + this.rangeChartWidgetSettingsForm.get('tooltipValueFont').enable(); + this.rangeChartWidgetSettingsForm.get('tooltipValueColor').enable(); + this.rangeChartWidgetSettingsForm.get('tooltipShowDate').enable({emitEvent: false}); + this.rangeChartWidgetSettingsForm.get('tooltipBackgroundColor').enable(); + this.rangeChartWidgetSettingsForm.get('tooltipBackgroundBlur').enable(); + if (tooltipShowDate) { + this.rangeChartWidgetSettingsForm.get('tooltipDateFormat').enable(); + this.rangeChartWidgetSettingsForm.get('tooltipDateFont').enable(); + this.rangeChartWidgetSettingsForm.get('tooltipDateColor').enable(); + } else { + this.rangeChartWidgetSettingsForm.get('tooltipDateFormat').disable(); + this.rangeChartWidgetSettingsForm.get('tooltipDateFont').disable(); + this.rangeChartWidgetSettingsForm.get('tooltipDateColor').disable(); + } + } else { + this.rangeChartWidgetSettingsForm.get('tooltipValueFont').disable(); + this.rangeChartWidgetSettingsForm.get('tooltipValueColor').disable(); + this.rangeChartWidgetSettingsForm.get('tooltipShowDate').disable({emitEvent: false}); + this.rangeChartWidgetSettingsForm.get('tooltipDateFormat').disable(); + this.rangeChartWidgetSettingsForm.get('tooltipDateFont').disable(); + this.rangeChartWidgetSettingsForm.get('tooltipDateColor').disable(); + this.rangeChartWidgetSettingsForm.get('tooltipBackgroundColor').disable(); + this.rangeChartWidgetSettingsForm.get('tooltipBackgroundBlur').disable(); + } + } + + private _tooltipValuePreviewFn(): string { + const units: string = this.widgetConfig.config.units; + const decimals: number = this.widgetConfig.config.decimals; + return formatValue(22, decimals, units, false); + } + + private _tooltipDatePreviewFn(): string { + const dateFormat: DateFormatSettings = this.rangeChartWidgetSettingsForm.get('tooltipDateFormat').value; + const processor = DateFormatProcessor.fromSettings(this.$injector, dateFormat); + processor.update(Date.now()); + return processor.formatted; + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-settings.component.ts index 989bdb2844..31cafc6d96 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-settings.component.ts @@ -147,7 +147,9 @@ export class ColorRangeSettingsComponent implements OnInit, ControlValueAccessor const rangeColors = this.modelValue.slice(0, Math.min(3, this.modelValue.length)).map(r => r.color); colors = colors.concat(rangeColors); } - if (colors.length === 1) { + if (!colors.length) { + this.colorStyle = {}; + } else if (colors.length === 1) { this.colorStyle = {backgroundColor: colors[0]}; } else { const gradientValues: string[] = []; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/date-format-select.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/date-format-select.component.ts index e4f0b47946..2aba8866ed 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/date-format-select.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/date-format-select.component.ts @@ -16,11 +16,7 @@ import { Component, forwardRef, Input, OnInit, Renderer2, ViewChild, ViewContainerRef } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormControl } from '@angular/forms'; -import { - compareDateFormats, - dateFormats, - DateFormatSettings -} from '@shared/models/widget-settings.models'; +import { compareDateFormats, dateFormats, DateFormatSettings } from '@shared/models/widget-settings.models'; import { TranslateService } from '@ngx-translate/core'; import { DatePipe } from '@angular/common'; import { MatButton } from '@angular/material/button'; @@ -29,6 +25,7 @@ import { deepClone } from '@core/utils'; import { DateFormatSettingsPanelComponent } from '@home/components/widget/lib/settings/common/date-format-settings-panel.component'; +import { coerceBoolean } from '@shared/decorators/coercion'; @Component({ selector: 'tb-date-format-select', @@ -50,7 +47,11 @@ export class DateFormatSelectComponent implements OnInit, ControlValueAccessor { @Input() disabled: boolean; - dateFormatList = dateFormats; + @Input() + @coerceBoolean() + excludeLastUpdateAgo = false; + + dateFormatList: DateFormatSettings[]; dateFormatsCompare = compareDateFormats; @@ -69,6 +70,8 @@ export class DateFormatSelectComponent implements OnInit, ControlValueAccessor { private viewContainerRef: ViewContainerRef) {} ngOnInit(): void { + this.dateFormatList = this.excludeLastUpdateAgo ? + dateFormats.filter(format => !format.lastUpdateAgo) : dateFormats; this.dateFormatFormControl = new UntypedFormControl(); this.dateFormatFormControl.valueChanges.subscribe((value: DateFormatSettings) => { this.updateModel(value); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts index 0f545366c2..5d1bce1745 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts @@ -305,6 +305,9 @@ import { import { DoughnutWidgetSettingsComponent } from '@home/components/widget/lib/settings/chart/doughnut-widget-settings.component'; +import { + RangeChartWidgetSettingsComponent +} from '@home/components/widget/lib/settings/chart/range-chart-widget-settings.component'; @NgModule({ declarations: [ @@ -416,7 +419,8 @@ import { ValueChartCardWidgetSettingsComponent, ProgressBarWidgetSettingsComponent, LiquidLevelCardWidgetSettingsComponent, - DoughnutWidgetSettingsComponent + DoughnutWidgetSettingsComponent, + RangeChartWidgetSettingsComponent ], imports: [ CommonModule, @@ -533,7 +537,8 @@ import { ValueChartCardWidgetSettingsComponent, ProgressBarWidgetSettingsComponent, LiquidLevelCardWidgetSettingsComponent, - DoughnutWidgetSettingsComponent + DoughnutWidgetSettingsComponent, + RangeChartWidgetSettingsComponent ] }) export class WidgetSettingsModule { @@ -616,5 +621,6 @@ export const widgetSettingsComponentsMap: {[key: string]: Type { + if (isNumber(range.from) && isNumber(range.to)) { + if (isNumber(toCheck.from) && isNumber(toCheck.to)) { + return toCheck.from >= range.from && toCheck.to < range.to; + } else { + return false; + } + } else if (isNumber(range.from)) { + if (isNumber(toCheck.from)) { + return toCheck.from >= range.from; + } else { + return false; + } + } else if (isNumber(range.to)) { + if (isNumber(toCheck.to)) { + return toCheck.to < range.to; + } else { + return false; + } + } else { + return false; + } +}; + +export const filterIncludingColorRanges = (ranges: Array): Array => { + const result = [...ranges]; + let includes = true; + while (includes) { + let index = -1; + for (let i = 0; i < result.length; i++) { + const range = result[i]; + if (result.some((value, i1) => i1 !== i && colorRangeIncludes(value, range))) { + index = i; + break; + } + } + if (index > -1) { + result.splice(index, 1); + } else { + includes = false; + } + } + return result; +}; + +export const sortedColorRange = (ranges: Array): Array => ranges ? [...ranges].sort( + (a, b) => { + if (isNumber(a.from) && isNumber(a.to) && isNumber(b.from) && isNumber(b.to)) { + if (b.from >= a.from && b.to < a.to) { + return 1; + } else if (a.from >= b.from && a.to < b.to) { + return -1; + } else { + return a.from - b.from; + } + } else if (isNumber(a.from) && isNumber(b.from)) { + return a.from - b.from; + } else if (isNumber(a.to) && isNumber(b.to)) { + return a.to - b.to; + } else if (isNumber(a.from) && isUndefinedOrNull(b.from)) { + return 1; + } else if (isUndefinedOrNull(a.from) && isNumber(b.from)) { + return -1; + } else if (isNumber(a.to) && isUndefinedOrNull(b.to)) { + return 1; + } else if (isUndefinedOrNull(a.to) && isNumber(b.to)) { + return -1; + } else { + return 0; + } + } + ) : []; + export interface ColorSettings { type: ColorType; color: string; @@ -208,7 +281,7 @@ class RangeColorProcessor extends ColorProcessor { return this.settings.color; } - private static constantRange(range: ColorRange): boolean { + public static constantRange(range: ColorRange): boolean { return isNumber(range.from) && isNumber(range.to) && range.from === range.to; } } diff --git a/ui-ngx/src/app/shared/models/widget.models.ts b/ui-ngx/src/app/shared/models/widget.models.ts index 0e676138ce..da0919e8a4 100644 --- a/ui-ngx/src/app/shared/models/widget.models.ts +++ b/ui-ngx/src/app/shared/models/widget.models.ts @@ -269,6 +269,8 @@ export enum LegendPosition { right = 'right' } +export const legendPositions = Object.keys(LegendPosition) as LegendPosition[]; + export const legendPositionTranslationMap = new Map( [ [ LegendPosition.top, 'position.top' ], 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 ef660731bf..37bc5d449f 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -5834,6 +5834,20 @@ "no-columns-found": "No columns found", "no-columns-matching": "'{{column}}' not found." }, + "range-chart": { + "chart": "Chart", + "data-zoom": "Data zoom", + "range-colors": "Range colors", + "out-of-range-color": "Out of range color", + "fill-area": "Fill area", + "legend-label": "Label", + "tooltip": "Tooltip", + "tooltip-value": "Value", + "tooltip-date": "Date", + "tooltip-background-color": "Background color", + "tooltip-background-blur": "Background blur", + "range-chart-card-style": "Range chart card style" + }, "rpc": { "value-settings": "Value settings", "initial-value": "Initial value", From f5cf8408386f362f67eced1f990558550adac84c Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 16 Nov 2023 17:46:03 +0200 Subject: [PATCH 151/252] Add upgrade from 3.6.1 tp 3.6.2 --- .../thingsboard/server/install/ThingsboardInstallService.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java index 0730bca8f8..9218a170d5 100644 --- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java +++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java @@ -266,6 +266,8 @@ public class ThingsboardInstallService { log.info("Upgrading ThingsBoard from version 3.6.0 to 3.6.1 ..."); databaseEntitiesUpgradeService.upgradeDatabase("3.6.0"); dataUpdateService.updateData("3.6.0"); + case "3.6.1": + log.info("Upgrading ThingsBoard from version 3.6.1 to 3.6.2 ..."); //TODO DON'T FORGET to update switch statement in the CacheCleanupService if you need to clear the cache break; default: From 7401ece634712da3f983f7e2ca54fdb6d8e806d0 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Fri, 17 Nov 2023 11:56:29 +0200 Subject: [PATCH 152/252] testSyncEdge - improved edge activation. added loggin in case test failure --- .../server/controller/EdgeControllerTest.java | 43 ++++++++++++++++--- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java index f0c47bb53a..0a25d76dd9 100644 --- a/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java @@ -23,6 +23,8 @@ import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; import com.google.protobuf.AbstractMessage; +import lombok.extern.slf4j.Slf4j; +import org.awaitility.Awaitility; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -78,6 +80,8 @@ import org.thingsboard.server.gen.edge.v1.UserUpdateMsg; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.Optional; import java.util.UUID; import java.util.concurrent.TimeUnit; @@ -92,6 +96,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; }) @ContextConfiguration(classes = {EdgeControllerTest.Config.class}) @DaoSqlTest +@Slf4j public class EdgeControllerTest extends AbstractControllerTest { public static final String EDGE_HOST = "localhost"; @@ -863,10 +868,7 @@ public class EdgeControllerTest extends AbstractControllerTest { Edge edge = doPost("/api/edge", constructEdge("Test Sync Edge", "test"), Edge.class); - // simulate edge activation - ObjectNode attributes = JacksonUtil.newObjectNode(); - attributes.put("active", true); - doPost("/api/plugins/telemetry/EDGE/" + edge.getId() + "/attributes/" + DataConstants.SERVER_SCOPE, attributes); + simulateEdgeActivation(edge); doPost("/api/edge/" + edge.getId().getId().toString() + "/device/" + savedDevice.getId().getId().toString(), Device.class); @@ -878,7 +880,7 @@ public class EdgeControllerTest extends AbstractControllerTest { edgeImitator.expectMessageAmount(24); edgeImitator.connect(); - assertThat(edgeImitator.waitForMessages()).as("await for messages on first connect").isTrue(); + waitForMessages(edgeImitator); verifyFetchersMsgs(edgeImitator); // verify queue msgs @@ -890,7 +892,7 @@ public class EdgeControllerTest extends AbstractControllerTest { edgeImitator.expectMessageAmount(20); doPost("/api/edge/sync/" + edge.getId()); - assertThat(edgeImitator.waitForMessages()).as("await for messages after edge sync rest api call").isTrue(); + waitForMessages(edgeImitator); verifyFetchersMsgs(edgeImitator); Assert.assertTrue(edgeImitator.getDownlinkMsgs().isEmpty()); @@ -909,6 +911,35 @@ public class EdgeControllerTest extends AbstractControllerTest { .andExpect(status().isOk()); } + private void simulateEdgeActivation(Edge edge) throws Exception { + ObjectNode attributes = JacksonUtil.newObjectNode(); + attributes.put("active", true); + doPost("/api/plugins/telemetry/EDGE/" + edge.getId() + "/attributes/" + DataConstants.SERVER_SCOPE, attributes); + Awaitility.await() + .atMost(30, TimeUnit.SECONDS) + .until(() -> { + List> values = doGetAsyncTyped("/api/plugins/telemetry/EDGE/" + edge.getId() + + "/values/attributes/SERVER_SCOPE", new TypeReference<>() {}); + Optional> activeAttrOpt = values.stream().filter(att -> att.get("key").equals("active")).findFirst(); + if (activeAttrOpt.isEmpty()) { + return false; + } + Map activeAttr = activeAttrOpt.get(); + return "true".equals(activeAttr.get("value").toString()); + }); + } + + private void waitForMessages(EdgeImitator edgeImitator) throws Exception { + boolean success = edgeImitator.waitForMessages(); + if (!success) { + List downlinkMsgs = edgeImitator.getDownlinkMsgs(); + for (AbstractMessage downlinkMsg : downlinkMsgs) { + log.error("{}\n{}", downlinkMsg.getClass(), downlinkMsg); + } + Assert.fail("Await for messages was not successful!"); + } + } + private void verifyFetchersMsgs(EdgeImitator edgeImitator) { Assert.assertTrue(popQueueMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Main")); Assert.assertTrue(popRuleChainMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Edge Root Rule Chain")); From 0eaa0d44e66ff23b13d2c7a5d38c293c3ec9763d Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Fri, 17 Nov 2023 11:59:00 +0200 Subject: [PATCH 153/252] Organize imports --- .../org/thingsboard/server/controller/EdgeControllerTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java index 0a25d76dd9..379dafef94 100644 --- a/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java @@ -85,7 +85,6 @@ import java.util.Optional; import java.util.UUID; import java.util.concurrent.TimeUnit; -import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.containsString; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; From 0e7722b9fc813ebd248539dd596609cd2f576a23 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Fri, 17 Nov 2023 12:02:05 +0200 Subject: [PATCH 154/252] Fix compile error --- .../test/java/org/thingsboard/server/edge/ResourceEdgeTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/test/java/org/thingsboard/server/edge/ResourceEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/ResourceEdgeTest.java index 4d1acf9afb..14f6e2994b 100644 --- a/application/src/test/java/org/thingsboard/server/edge/ResourceEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/ResourceEdgeTest.java @@ -129,7 +129,7 @@ public class ResourceEdgeTest extends AbstractEdgeTest { resource.setResourceType(ResourceType.JKS); resource.setTitle("Edge Test Resource"); resource.setFileName(FILE_NAME); - resource.setData(TEST_DATA); + resource.setEncodedData(TEST_DATA); edgeImitator.expectMessageAmount(1); TbResource savedResource = doPost("/api/resource", resource, TbResource.class); From fbec7aab25b670b89a97b72fa252fa625247f039 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Fri, 17 Nov 2023 12:18:03 +0200 Subject: [PATCH 155/252] moved pubsub executors to pubsubSettings --- .../PubSubRuleNodeExecutorProvider.java | 9 +++- .../src/main/resources/thingsboard.yml | 5 +- .../provider/PubSubMonolithQueueFactory.java | 50 ++++++++----------- .../provider/PubSubTbCoreQueueFactory.java | 38 ++++++-------- .../PubSubTbRuleEngineQueueFactory.java | 28 +++++------ .../PubSubTbVersionControlQueueFactory.java | 13 ++--- .../provider/PubSubTransportQueueFactory.java | 20 +++----- .../pubsub/TbPubSubConsumerTemplate.java | 4 +- .../pubsub/TbPubSubProducerTemplate.java | 10 +--- .../pubsub/TbPubSubQueueExecutorProvider.java | 2 +- .../server/queue/pubsub/TbPubSubSettings.java | 2 + .../src/main/resources/tb-vc-executor.yml | 2 +- .../rule/engine/gcp/pubsub/TbPubSubNode.java | 6 +-- .../src/main/resources/tb-coap-transport.yml | 2 +- .../src/main/resources/tb-http-transport.yml | 2 +- .../src/main/resources/tb-lwm2m-transport.yml | 2 +- .../src/main/resources/tb-mqtt-transport.yml | 2 +- .../src/main/resources/tb-snmp-transport.yml | 2 +- 18 files changed, 88 insertions(+), 111 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/executors/PubSubRuleNodeExecutorProvider.java b/application/src/main/java/org/thingsboard/server/service/executors/PubSubRuleNodeExecutorProvider.java index f730949f45..520b8f855c 100644 --- a/application/src/main/java/org/thingsboard/server/service/executors/PubSubRuleNodeExecutorProvider.java +++ b/application/src/main/java/org/thingsboard/server/service/executors/PubSubRuleNodeExecutorProvider.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.service.executors; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.thingsboard.common.util.ExecutorProvider; import org.thingsboard.common.util.ThingsBoardThreadFactory; @@ -27,6 +28,9 @@ import java.util.concurrent.ScheduledExecutorService; @Component public class PubSubRuleNodeExecutorProvider implements ExecutorProvider { + @Value("${service.rule_engine.pubsub.executor_thread_pool_size}") + private Integer threadPoolSize; + /** * Refers to com.google.cloud.pubsub.v1.Publisher default executor configuration */ @@ -35,7 +39,10 @@ public class PubSubRuleNodeExecutorProvider implements ExecutorProvider { @PostConstruct public void init() { - executor = Executors.newScheduledThreadPool(THREADS_PER_CPU * Runtime.getRuntime().availableProcessors(), ThingsBoardThreadFactory.forName("pubsub-rule-nodes"));; + if (threadPoolSize == null) { + threadPoolSize = THREADS_PER_CPU * Runtime.getRuntime().availableProcessors(); + } + executor = Executors.newScheduledThreadPool(threadPoolSize, ThingsBoardThreadFactory.forName("pubsub-rule-nodes")); } @Override diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 802756c105..304ad96a34 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -1411,7 +1411,7 @@ queue: max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" # Number of messages per consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" - # Number of threads of pubsub executor provider + # Thread pool size for pubsub queue executor provider. If not set - default pubsub executor provider value will be used (5 * number of available processors) executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:}" queue-properties: # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consumed again @@ -1641,6 +1641,9 @@ service: # Comma-separated list of tenant profile ids assigned to this Rule Engine. # This Rule Engine will only be responsible for tenants with these profiles (in case 'isolation' option is enabled in the profile). assigned_tenant_profiles: "${TB_RULE_ENGINE_ASSIGNED_TENANT_PROFILES:}" + pubsub: + # Thread pool size for pubsub rule node executor provider. If not set - default pubsub executor provider value will be used (5 * number of available processors) + executor_thread_pool_size: "${TB_RULE_ENGINE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:}" # Metrics parameters metrics: diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubMonolithQueueFactory.java index 8ecd6564c8..554d452556 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubMonolithQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubMonolithQueueFactory.java @@ -44,7 +44,6 @@ import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.pubsub.TbPubSubAdmin; import org.thingsboard.server.queue.pubsub.TbPubSubConsumerTemplate; -import org.thingsboard.server.queue.pubsub.TbPubSubQueueExecutorProvider; import org.thingsboard.server.queue.pubsub.TbPubSubProducerTemplate; import org.thingsboard.server.queue.pubsub.TbPubSubSettings; import org.thingsboard.server.queue.pubsub.TbPubSubSubscriptionSettings; @@ -78,7 +77,6 @@ public class PubSubMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng private final TbQueueAdmin transportApiAdmin; private final TbQueueAdmin notificationAdmin; private final TbQueueAdmin vcAdmin; - private final TbPubSubQueueExecutorProvider tbPubSubQueueExecutorProvider; public PubSubMonolithQueueFactory(TbPubSubSettings pubSubSettings, TbQueueCoreSettings coreSettings, @@ -89,8 +87,7 @@ public class PubSubMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng TbServiceInfoProvider serviceInfoProvider, TbPubSubSubscriptionSettings pubSubSubscriptionSettings, TbQueueRemoteJsInvokeSettings jsInvokeSettings, - TbQueueVersionControlSettings vcSettings, - TbPubSubQueueExecutorProvider tbPubSubQueueExecutorProvider) { + TbQueueVersionControlSettings vcSettings) { this.pubSubSettings = pubSubSettings; this.coreSettings = coreSettings; this.ruleEngineSettings = ruleEngineSettings; @@ -99,7 +96,6 @@ public class PubSubMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng this.topicService = topicService; this.serviceInfoProvider = serviceInfoProvider; this.vcSettings = vcSettings; - this.tbPubSubQueueExecutorProvider = tbPubSubQueueExecutorProvider; this.coreAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getCoreSettings()); this.ruleEngineAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getRuleEngineSettings()); @@ -113,91 +109,85 @@ public class PubSubMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng @Override public TbQueueProducer> createTransportNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, transportNotificationSettings.getNotificationsTopic(), tbPubSubQueueExecutorProvider); + return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, transportNotificationSettings.getNotificationsTopic()); } @Override public TbQueueProducer> createRuleEngineMsgProducer() { - return new TbPubSubProducerTemplate<>(ruleEngineAdmin, pubSubSettings, ruleEngineSettings.getTopic(), tbPubSubQueueExecutorProvider); + return new TbPubSubProducerTemplate<>(ruleEngineAdmin, pubSubSettings, ruleEngineSettings.getTopic()); } @Override public TbQueueProducer> createRuleEngineNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, ruleEngineSettings.getTopic(), tbPubSubQueueExecutorProvider); + return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, ruleEngineSettings.getTopic()); } @Override public TbQueueProducer> createTbCoreMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getTopic(), tbPubSubQueueExecutorProvider); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getTopic()); } @Override public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, coreSettings.getTopic(), tbPubSubQueueExecutorProvider); + return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, coreSettings.getTopic()); } @Override public TbQueueConsumer> createToVersionControlMsgConsumer() { return new TbPubSubConsumerTemplate<>(vcAdmin, pubSubSettings, vcSettings.getTopic(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportProtos.ToVersionControlServiceMsg.parseFrom(msg.getData()), msg.getHeaders()), - tbPubSubQueueExecutorProvider + msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportProtos.ToVersionControlServiceMsg.parseFrom(msg.getData()), msg.getHeaders()) ); } @Override public TbQueueConsumer> createToRuleEngineMsgConsumer(Queue configuration) { return new TbPubSubConsumerTemplate<>(ruleEngineAdmin, pubSubSettings, configuration.getTopic(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineMsg.parseFrom(msg.getData()), msg.getHeaders()), - tbPubSubQueueExecutorProvider); + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueConsumer> createToRuleEngineNotificationsMsgConsumer() { return new TbPubSubConsumerTemplate<>(notificationAdmin, pubSubSettings, topicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceInfoProvider.getServiceId()).getFullTopicName(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineNotificationMsg.parseFrom(msg.getData()), msg.getHeaders()), - tbPubSubQueueExecutorProvider); + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueConsumer> createToCoreMsgConsumer() { return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getTopic(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreMsg.parseFrom(msg.getData()), msg.getHeaders()), - tbPubSubQueueExecutorProvider); + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueConsumer> createToCoreNotificationsMsgConsumer() { return new TbPubSubConsumerTemplate<>(notificationAdmin, pubSubSettings, topicService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreNotificationMsg.parseFrom(msg.getData()), msg.getHeaders()), - tbPubSubQueueExecutorProvider); + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueConsumer> createTransportApiRequestConsumer() { return new TbPubSubConsumerTemplate<>(transportApiAdmin, pubSubSettings, transportApiSettings.getRequestsTopic(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiRequestMsg.parseFrom(msg.getData()), msg.getHeaders()), - tbPubSubQueueExecutorProvider); + msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiRequestMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueProducer> createTransportApiResponseProducer() { - return new TbPubSubProducerTemplate<>(transportApiAdmin, pubSubSettings, transportApiSettings.getResponsesTopic(), tbPubSubQueueExecutorProvider); + return new TbPubSubProducerTemplate<>(transportApiAdmin, pubSubSettings, transportApiSettings.getResponsesTopic()); } @Override @Bean public TbQueueRequestTemplate, TbProtoQueueMsg> createRemoteJsRequestTemplate() { - TbQueueProducer> producer = new TbPubSubProducerTemplate<>(jsExecutorAdmin, pubSubSettings, jsInvokeSettings.getRequestTopic(), tbPubSubQueueExecutorProvider); + TbQueueProducer> producer = new TbPubSubProducerTemplate<>(jsExecutorAdmin, pubSubSettings, jsInvokeSettings.getRequestTopic()); TbQueueConsumer> consumer = new TbPubSubConsumerTemplate<>(jsExecutorAdmin, pubSubSettings, jsInvokeSettings.getResponseTopic() + "." + serviceInfoProvider.getServiceId(), msg -> { RemoteJsResponse.Builder builder = RemoteJsResponse.newBuilder(); JsonFormat.parser().ignoringUnknownFields().merge(new String(msg.getData(), StandardCharsets.UTF_8), builder); return new TbProtoQueueMsg<>(msg.getKey(), builder.build(), msg.getHeaders()); - }, tbPubSubQueueExecutorProvider); + }); DefaultTbQueueRequestTemplate.DefaultTbQueueRequestTemplateBuilder , TbProtoQueueMsg> builder = DefaultTbQueueRequestTemplate.builder(); @@ -213,28 +203,28 @@ public class PubSubMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng @Override public TbQueueConsumer> createToUsageStatsServiceMsgConsumer() { return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getUsageStatsTopic(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToUsageStatsServiceMsg.parseFrom(msg.getData()), msg.getHeaders()), tbPubSubQueueExecutorProvider); + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToUsageStatsServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getOtaPackageTopic(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders()), tbPubSubQueueExecutorProvider); + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getOtaPackageTopic(), tbPubSubQueueExecutorProvider); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getOtaPackageTopic()); } @Override public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getUsageStatsTopic(), tbPubSubQueueExecutorProvider); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getUsageStatsTopic()); } @Override public TbQueueProducer> createVersionControlMsgProducer() { - return new TbPubSubProducerTemplate<>(vcAdmin, pubSubSettings, vcSettings.getTopic(), tbPubSubQueueExecutorProvider); + return new TbPubSubProducerTemplate<>(vcAdmin, pubSubSettings, vcSettings.getTopic()); } @PreDestroy diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbCoreQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbCoreQueueFactory.java index 5443339d2a..3b45992e25 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbCoreQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbCoreQueueFactory.java @@ -42,7 +42,6 @@ import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.pubsub.TbPubSubAdmin; import org.thingsboard.server.queue.pubsub.TbPubSubConsumerTemplate; -import org.thingsboard.server.queue.pubsub.TbPubSubQueueExecutorProvider; import org.thingsboard.server.queue.pubsub.TbPubSubProducerTemplate; import org.thingsboard.server.queue.pubsub.TbPubSubSettings; import org.thingsboard.server.queue.pubsub.TbPubSubSubscriptionSettings; @@ -73,7 +72,6 @@ public class PubSubTbCoreQueueFactory implements TbCoreQueueFactory { private final TbQueueAdmin transportApiAdmin; private final TbQueueAdmin notificationAdmin; private final TbQueueAdmin ruleEngineAdmin; - private final TbPubSubQueueExecutorProvider tbPubSubQueueExecutorProvider; public PubSubTbCoreQueueFactory(TbPubSubSettings pubSubSettings, TbQueueCoreSettings coreSettings, @@ -83,8 +81,7 @@ public class PubSubTbCoreQueueFactory implements TbCoreQueueFactory { TbQueueRemoteJsInvokeSettings jsInvokeSettings, TbQueueTransportNotificationSettings transportNotificationSettings, TbQueueRuleEngineSettings ruleEngineSettings, - TbPubSubSubscriptionSettings pubSubSubscriptionSettings, - TbPubSubQueueExecutorProvider tbPubSubQueueExecutorProvider) { + TbPubSubSubscriptionSettings pubSubSubscriptionSettings) { this.pubSubSettings = pubSubSettings; this.coreSettings = coreSettings; this.transportApiSettings = transportApiSettings; @@ -93,7 +90,6 @@ public class PubSubTbCoreQueueFactory implements TbCoreQueueFactory { this.jsInvokeSettings = jsInvokeSettings; this.transportNotificationSettings = transportNotificationSettings; this.ruleEngineSettings = ruleEngineSettings; - this.tbPubSubQueueExecutorProvider = tbPubSubQueueExecutorProvider; this.coreAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getCoreSettings()); this.jsExecutorAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getJsExecutorSettings()); @@ -104,64 +100,64 @@ public class PubSubTbCoreQueueFactory implements TbCoreQueueFactory { @Override public TbQueueProducer> createTransportNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, transportNotificationSettings.getNotificationsTopic(), tbPubSubQueueExecutorProvider); + return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, transportNotificationSettings.getNotificationsTopic()); } @Override public TbQueueProducer> createRuleEngineMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getTopic(), tbPubSubQueueExecutorProvider); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getTopic()); } @Override public TbQueueProducer> createRuleEngineNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, ruleEngineSettings.getTopic(), tbPubSubQueueExecutorProvider); + return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, ruleEngineSettings.getTopic()); } @Override public TbQueueProducer> createTbCoreMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getTopic(), tbPubSubQueueExecutorProvider); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getTopic()); } @Override public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, coreSettings.getTopic(), tbPubSubQueueExecutorProvider); + return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, coreSettings.getTopic()); } @Override public TbQueueConsumer> createToCoreMsgConsumer() { return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getTopic(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreMsg.parseFrom(msg.getData()), msg.getHeaders()), tbPubSubQueueExecutorProvider); + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueConsumer> createToCoreNotificationsMsgConsumer() { return new TbPubSubConsumerTemplate<>(notificationAdmin, pubSubSettings, topicService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreNotificationMsg.parseFrom(msg.getData()), msg.getHeaders()), tbPubSubQueueExecutorProvider); + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueConsumer> createTransportApiRequestConsumer() { return new TbPubSubConsumerTemplate<>(transportApiAdmin, pubSubSettings, transportApiSettings.getRequestsTopic(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiRequestMsg.parseFrom(msg.getData()), msg.getHeaders()), tbPubSubQueueExecutorProvider); + msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiRequestMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueProducer> createTransportApiResponseProducer() { - return new TbPubSubProducerTemplate<>(transportApiAdmin, pubSubSettings, transportApiSettings.getResponsesTopic(), tbPubSubQueueExecutorProvider); + return new TbPubSubProducerTemplate<>(transportApiAdmin, pubSubSettings, transportApiSettings.getResponsesTopic()); } @Override @Bean public TbQueueRequestTemplate, TbProtoQueueMsg> createRemoteJsRequestTemplate() { - TbQueueProducer> producer = new TbPubSubProducerTemplate<>(jsExecutorAdmin, pubSubSettings, jsInvokeSettings.getRequestTopic(), tbPubSubQueueExecutorProvider); + TbQueueProducer> producer = new TbPubSubProducerTemplate<>(jsExecutorAdmin, pubSubSettings, jsInvokeSettings.getRequestTopic()); TbQueueConsumer> consumer = new TbPubSubConsumerTemplate<>(jsExecutorAdmin, pubSubSettings, jsInvokeSettings.getResponseTopic() + "." + serviceInfoProvider.getServiceId(), msg -> { JsInvokeProtos.RemoteJsResponse.Builder builder = JsInvokeProtos.RemoteJsResponse.newBuilder(); JsonFormat.parser().ignoringUnknownFields().merge(new String(msg.getData(), StandardCharsets.UTF_8), builder); return new TbProtoQueueMsg<>(msg.getKey(), builder.build(), msg.getHeaders()); - }, tbPubSubQueueExecutorProvider); + }); DefaultTbQueueRequestTemplate.DefaultTbQueueRequestTemplateBuilder , TbProtoQueueMsg> builder = DefaultTbQueueRequestTemplate.builder(); @@ -177,25 +173,23 @@ public class PubSubTbCoreQueueFactory implements TbCoreQueueFactory { @Override public TbQueueConsumer> createToUsageStatsServiceMsgConsumer() { return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getUsageStatsTopic(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToUsageStatsServiceMsg.parseFrom(msg.getData()), msg.getHeaders()), - tbPubSubQueueExecutorProvider); + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToUsageStatsServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getOtaPackageTopic(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders()), - tbPubSubQueueExecutorProvider); + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getOtaPackageTopic(), tbPubSubQueueExecutorProvider); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getOtaPackageTopic()); } @Override public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getUsageStatsTopic(), tbPubSubQueueExecutorProvider); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getUsageStatsTopic()); } @Override diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbRuleEngineQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbRuleEngineQueueFactory.java index b0fde82e7d..a60f77852f 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbRuleEngineQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbRuleEngineQueueFactory.java @@ -40,7 +40,6 @@ import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.pubsub.TbPubSubAdmin; import org.thingsboard.server.queue.pubsub.TbPubSubConsumerTemplate; -import org.thingsboard.server.queue.pubsub.TbPubSubQueueExecutorProvider; import org.thingsboard.server.queue.pubsub.TbPubSubProducerTemplate; import org.thingsboard.server.queue.pubsub.TbPubSubSettings; import org.thingsboard.server.queue.pubsub.TbPubSubSubscriptionSettings; @@ -63,7 +62,6 @@ public class PubSubTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory private final TbServiceInfoProvider serviceInfoProvider; private final TbQueueRemoteJsInvokeSettings jsInvokeSettings; private final TbQueueTransportNotificationSettings transportNotificationSettings; - private final TbPubSubQueueExecutorProvider tbPubSubQueueExecutorProvider; private final TbQueueAdmin coreAdmin; private final TbQueueAdmin ruleEngineAdmin; @@ -77,8 +75,7 @@ public class PubSubTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory TbServiceInfoProvider serviceInfoProvider, TbQueueRemoteJsInvokeSettings jsInvokeSettings, TbQueueTransportNotificationSettings transportNotificationSettings, - TbPubSubSubscriptionSettings pubSubSubscriptionSettings, - TbPubSubQueueExecutorProvider tbPubSubQueueExecutorProvider) { + TbPubSubSubscriptionSettings pubSubSubscriptionSettings) { this.pubSubSettings = pubSubSettings; this.coreSettings = coreSettings; this.ruleEngineSettings = ruleEngineSettings; @@ -86,7 +83,6 @@ public class PubSubTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory this.serviceInfoProvider = serviceInfoProvider; this.jsInvokeSettings = jsInvokeSettings; this.transportNotificationSettings = transportNotificationSettings; - this.tbPubSubQueueExecutorProvider = tbPubSubQueueExecutorProvider; this.coreAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getCoreSettings()); this.ruleEngineAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getRuleEngineSettings()); @@ -96,53 +92,53 @@ public class PubSubTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory @Override public TbQueueProducer> createTransportNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic()), tbPubSubQueueExecutorProvider); + return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic())); } @Override public TbQueueProducer> createRuleEngineMsgProducer() { - return new TbPubSubProducerTemplate<>(ruleEngineAdmin, pubSubSettings, topicService.buildTopicName(ruleEngineSettings.getTopic()), tbPubSubQueueExecutorProvider); + return new TbPubSubProducerTemplate<>(ruleEngineAdmin, pubSubSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); } @Override public TbQueueProducer> createRuleEngineNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(ruleEngineSettings.getTopic()), tbPubSubQueueExecutorProvider); + return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); } @Override public TbQueueProducer> createTbCoreMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic()), tbPubSubQueueExecutorProvider); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic()), tbPubSubQueueExecutorProvider); + return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueConsumer> createToRuleEngineMsgConsumer(Queue configuration) { return new TbPubSubConsumerTemplate<>(ruleEngineAdmin, pubSubSettings, topicService.buildTopicName(configuration.getTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineMsg.parseFrom(msg.getData()), msg.getHeaders()), tbPubSubQueueExecutorProvider); + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueConsumer> createToRuleEngineNotificationsMsgConsumer() { return new TbPubSubConsumerTemplate<>(notificationAdmin, pubSubSettings, topicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceInfoProvider.getServiceId()).getFullTopicName(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineNotificationMsg.parseFrom(msg.getData()), msg.getHeaders()), tbPubSubQueueExecutorProvider); + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override @Bean public TbQueueRequestTemplate, TbProtoQueueMsg> createRemoteJsRequestTemplate() { - TbQueueProducer> producer = new TbPubSubProducerTemplate<>(jsExecutorAdmin, pubSubSettings, jsInvokeSettings.getRequestTopic(), tbPubSubQueueExecutorProvider); + TbQueueProducer> producer = new TbPubSubProducerTemplate<>(jsExecutorAdmin, pubSubSettings, jsInvokeSettings.getRequestTopic()); TbQueueConsumer> consumer = new TbPubSubConsumerTemplate<>(jsExecutorAdmin, pubSubSettings, jsInvokeSettings.getResponseTopic() + "." + serviceInfoProvider.getServiceId(), msg -> { JsInvokeProtos.RemoteJsResponse.Builder builder = JsInvokeProtos.RemoteJsResponse.newBuilder(); JsonFormat.parser().ignoringUnknownFields().merge(new String(msg.getData(), StandardCharsets.UTF_8), builder); return new TbProtoQueueMsg<>(msg.getKey(), builder.build(), msg.getHeaders()); - }, tbPubSubQueueExecutorProvider); + }); DefaultTbQueueRequestTemplate.DefaultTbQueueRequestTemplateBuilder , TbProtoQueueMsg> builder = DefaultTbQueueRequestTemplate.builder(); @@ -157,12 +153,12 @@ public class PubSubTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory @Override public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic()), tbPubSubQueueExecutorProvider); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); } @Override public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic()), tbPubSubQueueExecutorProvider); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic())); } @PreDestroy diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbVersionControlQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbVersionControlQueueFactory.java index 29207ed429..8154d04d6d 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbVersionControlQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbVersionControlQueueFactory.java @@ -26,7 +26,6 @@ import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.pubsub.TbPubSubAdmin; import org.thingsboard.server.queue.pubsub.TbPubSubConsumerTemplate; import org.thingsboard.server.queue.pubsub.TbPubSubProducerTemplate; -import org.thingsboard.server.queue.pubsub.TbPubSubQueueExecutorProvider; import org.thingsboard.server.queue.pubsub.TbPubSubSettings; import org.thingsboard.server.queue.pubsub.TbPubSubSubscriptionSettings; import org.thingsboard.server.queue.settings.TbQueueCoreSettings; @@ -42,7 +41,6 @@ public class PubSubTbVersionControlQueueFactory implements TbVersionControlQueue private final TbQueueCoreSettings coreSettings; private final TbQueueVersionControlSettings vcSettings; private final TopicService topicService; - private final TbPubSubQueueExecutorProvider tbPubSubQueueExecutorProvider; private final TbQueueAdmin coreAdmin; private final TbQueueAdmin notificationAdmin; @@ -52,14 +50,12 @@ public class PubSubTbVersionControlQueueFactory implements TbVersionControlQueue TbQueueCoreSettings coreSettings, TbQueueVersionControlSettings vcSettings, TbPubSubSubscriptionSettings pubSubSubscriptionSettings, - TopicService topicService, - TbPubSubQueueExecutorProvider tbPubSubQueueExecutorProvider + TopicService topicService ) { this.pubSubSettings = pubSubSettings; this.coreSettings = coreSettings; this.vcSettings = vcSettings; this.topicService = topicService; - this.tbPubSubQueueExecutorProvider = tbPubSubQueueExecutorProvider; this.coreAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getCoreSettings()); this.notificationAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getNotificationsSettings()); @@ -68,19 +64,18 @@ public class PubSubTbVersionControlQueueFactory implements TbVersionControlQueue @Override public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic()), tbPubSubQueueExecutorProvider); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); } @Override public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic()), tbPubSubQueueExecutorProvider); + return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueConsumer> createToVersionControlMsgConsumer() { return new TbPubSubConsumerTemplate<>(vcAdmin, pubSubSettings, topicService.buildTopicName(vcSettings.getTopic()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportProtos.ToVersionControlServiceMsg.parseFrom(msg.getData()), msg.getHeaders()), - tbPubSubQueueExecutorProvider + msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportProtos.ToVersionControlServiceMsg.parseFrom(msg.getData()), msg.getHeaders()) ); } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTransportQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTransportQueueFactory.java index 94a454dd5b..edcad873c4 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTransportQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTransportQueueFactory.java @@ -37,7 +37,6 @@ import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.pubsub.TbPubSubAdmin; import org.thingsboard.server.queue.pubsub.TbPubSubConsumerTemplate; import org.thingsboard.server.queue.pubsub.TbPubSubProducerTemplate; -import org.thingsboard.server.queue.pubsub.TbPubSubQueueExecutorProvider; import org.thingsboard.server.queue.pubsub.TbPubSubSettings; import org.thingsboard.server.queue.pubsub.TbPubSubSubscriptionSettings; import org.thingsboard.server.queue.settings.TbQueueCoreSettings; @@ -59,7 +58,6 @@ public class PubSubTransportQueueFactory implements TbTransportQueueFactory { private final TbQueueTransportApiSettings transportApiSettings; private final TbQueueTransportNotificationSettings transportNotificationSettings; private final TopicService topicService; - private final TbPubSubQueueExecutorProvider tbPubSubQueueExecutorProvider; private final TbQueueAdmin coreAdmin; private final TbQueueAdmin ruleEngineAdmin; @@ -73,8 +71,7 @@ public class PubSubTransportQueueFactory implements TbTransportQueueFactory { TbQueueTransportApiSettings transportApiSettings, TbQueueTransportNotificationSettings transportNotificationSettings, TbPubSubSubscriptionSettings pubSubSubscriptionSettings, - TopicService topicService, - TbPubSubQueueExecutorProvider tbPubSubQueueExecutorProvider) { + TopicService topicService) { this.pubSubSettings = pubSubSettings; this.serviceInfoProvider = serviceInfoProvider; this.coreSettings = coreSettings; @@ -82,7 +79,6 @@ public class PubSubTransportQueueFactory implements TbTransportQueueFactory { this.transportApiSettings = transportApiSettings; this.transportNotificationSettings = transportNotificationSettings; this.topicService = topicService; - this.tbPubSubQueueExecutorProvider = tbPubSubQueueExecutorProvider; this.coreAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getCoreSettings()); this.ruleEngineAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getRuleEngineSettings()); @@ -92,10 +88,10 @@ public class PubSubTransportQueueFactory implements TbTransportQueueFactory { @Override public TbQueueRequestTemplate, TbProtoQueueMsg> createTransportApiRequestTemplate() { - TbQueueProducer> producer = new TbPubSubProducerTemplate<>(transportApiAdmin, pubSubSettings, topicService.buildTopicName(transportApiSettings.getRequestsTopic()), tbPubSubQueueExecutorProvider); + TbQueueProducer> producer = new TbPubSubProducerTemplate<>(transportApiAdmin, pubSubSettings, topicService.buildTopicName(transportApiSettings.getRequestsTopic())); TbQueueConsumer> consumer = new TbPubSubConsumerTemplate<>(transportApiAdmin, pubSubSettings, topicService.buildTopicName(transportApiSettings.getResponsesTopic() + "." + serviceInfoProvider.getServiceId()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiResponseMsg.parseFrom(msg.getData()), msg.getHeaders()), tbPubSubQueueExecutorProvider); + msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiResponseMsg.parseFrom(msg.getData()), msg.getHeaders())); DefaultTbQueueRequestTemplate.DefaultTbQueueRequestTemplateBuilder , TbProtoQueueMsg> templateBuilder = DefaultTbQueueRequestTemplate.builder(); @@ -110,29 +106,29 @@ public class PubSubTransportQueueFactory implements TbTransportQueueFactory { @Override public TbQueueProducer> createRuleEngineMsgProducer() { - return new TbPubSubProducerTemplate<>(ruleEngineAdmin, pubSubSettings, topicService.buildTopicName(ruleEngineSettings.getTopic()), tbPubSubQueueExecutorProvider); + return new TbPubSubProducerTemplate<>(ruleEngineAdmin, pubSubSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); } @Override public TbQueueProducer> createTbCoreMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic()), tbPubSubQueueExecutorProvider); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic()), tbPubSubQueueExecutorProvider); + return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueConsumer> createTransportNotificationsConsumer() { return new TbPubSubConsumerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic() + "." + serviceInfoProvider.getServiceId()), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToTransportMsg.parseFrom(msg.getData()), msg.getHeaders()), tbPubSubQueueExecutorProvider); + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToTransportMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic()), tbPubSubQueueExecutorProvider); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); } @PreDestroy diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubConsumerTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubConsumerTemplate.java index fd01696873..1135053a66 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubConsumerTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubConsumerTemplate.java @@ -63,7 +63,7 @@ public class TbPubSubConsumerTemplate extends AbstractPara private final SubscriberStub subscriber; private volatile int messagesPerTopic; - public TbPubSubConsumerTemplate(TbQueueAdmin admin, TbPubSubSettings pubSubSettings, String topic, TbQueueMsgDecoder decoder, TbPubSubQueueExecutorProvider tbPubSubQueueExecutorProvider) { + public TbPubSubConsumerTemplate(TbQueueAdmin admin, TbPubSubSettings pubSubSettings, String topic, TbQueueMsgDecoder decoder) { super(topic); this.admin = admin; this.pubSubSettings = pubSubSettings; @@ -77,7 +77,7 @@ public class TbPubSubConsumerTemplate extends AbstractPara SubscriberStubSettings.defaultGrpcTransportProviderBuilder() .setMaxInboundMessageSize(pubSubSettings.getMaxMsgSize()) .build()) - .setExecutorProvider(FixedExecutorProvider.create(tbPubSubQueueExecutorProvider.getExecutor())) + .setExecutorProvider(FixedExecutorProvider.create(pubSubSettings.getTbPubSubQueueExecutorProvider().getExecutor())) .build(); this.subscriber = GrpcSubscriberStub.create(subscriberStubSettings); } catch (IOException e) { diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubProducerTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubProducerTemplate.java index 48a104f20a..bf297d8af4 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubProducerTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubProducerTemplate.java @@ -25,8 +25,6 @@ import com.google.protobuf.ByteString; import com.google.pubsub.v1.ProjectTopicName; import com.google.pubsub.v1.PubsubMessage; import lombok.extern.slf4j.Slf4j; -import org.thingsboard.common.util.ExecutorProvider; -import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.queue.TbQueueAdmin; import org.thingsboard.server.queue.TbQueueCallback; @@ -39,7 +37,6 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @Slf4j @@ -54,14 +51,11 @@ public class TbPubSubProducerTemplate implements TbQueuePr private final Map publisherMap = new ConcurrentHashMap<>(); private final ExecutorService pubExecutor = Executors.newCachedThreadPool(); - private final FixedExecutorProvider fixedExecutorProvider; - public TbPubSubProducerTemplate(TbQueueAdmin admin, TbPubSubSettings pubSubSettings, String defaultTopic, ExecutorProvider executorProvider) { + public TbPubSubProducerTemplate(TbQueueAdmin admin, TbPubSubSettings pubSubSettings, String defaultTopic) { this.defaultTopic = defaultTopic; this.admin = admin; this.pubSubSettings = pubSubSettings; - - fixedExecutorProvider = FixedExecutorProvider.create(executorProvider.getExecutor()); } @Override @@ -129,7 +123,7 @@ public class TbPubSubProducerTemplate implements TbQueuePr ProjectTopicName topicName = ProjectTopicName.of(pubSubSettings.getProjectId(), topic); Publisher publisher = Publisher.newBuilder(topicName) .setCredentialsProvider(pubSubSettings.getCredentialsProvider()) - .setExecutorProvider(fixedExecutorProvider) + .setExecutorProvider(FixedExecutorProvider.create(pubSubSettings.getTbPubSubQueueExecutorProvider().getExecutor())) .build(); publisherMap.put(topic, publisher); return publisher; diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubQueueExecutorProvider.java b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubQueueExecutorProvider.java index 4de75faa56..5e5f19651c 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubQueueExecutorProvider.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubQueueExecutorProvider.java @@ -43,7 +43,7 @@ public class TbPubSubQueueExecutorProvider implements ExecutorProvider { if (threadPoolSize == null) { threadPoolSize = THREADS_PER_CPU * Runtime.getRuntime().availableProcessors(); } - executor = Executors.newScheduledThreadPool(threadPoolSize, ThingsBoardThreadFactory.forName("pubsub-queue-executor"));; + executor = Executors.newScheduledThreadPool(threadPoolSize, ThingsBoardThreadFactory.forName("pubsub-queue-executor")); } @Override diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubSettings.java b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubSettings.java index fe9d85c306..530453f26b 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubSettings.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubSettings.java @@ -46,6 +46,8 @@ public class TbPubSubSettings { @Value("${queue.pubsub.max_messages}") private int maxMessages; + private final TbPubSubQueueExecutorProvider tbPubSubQueueExecutorProvider; + private CredentialsProvider credentialsProvider; @PostConstruct diff --git a/msa/vc-executor/src/main/resources/tb-vc-executor.yml b/msa/vc-executor/src/main/resources/tb-vc-executor.yml index 9fc9b884f0..62957debcf 100644 --- a/msa/vc-executor/src/main/resources/tb-vc-executor.yml +++ b/msa/vc-executor/src/main/resources/tb-vc-executor.yml @@ -172,7 +172,7 @@ queue: max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" #in bytes # Number of messages per consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" - # Number of threads of pubsub executor provider + # Thread pool size for pubsub queue executor provider. If not set - default pubsub executor provider value will be used (5 * number of available processors) executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:}" queue-properties: # Pub/Sub properties for Core subscribers, messages which will commit after ackDeadlineInSec period can be consumed again diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNode.java index 39ab7bf525..3067947445 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNode.java @@ -69,7 +69,7 @@ public class TbPubSubNode extends TbAbstractExternalNode { super.init(ctx); this.config = TbNodeUtils.convert(configuration, TbPubSubNodeConfiguration.class); try { - this.pubSubClient = initPubSubClient(FixedExecutorProvider.create(ctx.getPubSubRuleNodeExecutorProvider().getExecutor())); + this.pubSubClient = initPubSubClient(ctx); } catch (Exception e) { throw new TbNodeException(e); } @@ -129,7 +129,7 @@ public class TbPubSubNode extends TbAbstractExternalNode { return TbMsg.transformMsgMetadata(origMsg, metaData); } - private Publisher initPubSubClient(FixedExecutorProvider fixedExecutorProvider) throws IOException { + private Publisher initPubSubClient(TbContext ctx) throws IOException { ProjectTopicName topicName = ProjectTopicName.of(config.getProjectId(), config.getTopicName()); ServiceAccountCredentials credentials = ServiceAccountCredentials.fromStream( @@ -149,7 +149,7 @@ public class TbPubSubNode extends TbAbstractExternalNode { return Publisher.newBuilder(topicName) .setCredentialsProvider(credProvider) .setRetrySettings(retrySettings) - .setExecutorProvider(fixedExecutorProvider) + .setExecutorProvider(FixedExecutorProvider.create(ctx.getPubSubRuleNodeExecutorProvider().getExecutor())) .build(); } } diff --git a/transport/coap/src/main/resources/tb-coap-transport.yml b/transport/coap/src/main/resources/tb-coap-transport.yml index 27e5c8b273..c376d6a176 100644 --- a/transport/coap/src/main/resources/tb-coap-transport.yml +++ b/transport/coap/src/main/resources/tb-coap-transport.yml @@ -295,7 +295,7 @@ queue: max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" # Number of messages per consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" - # Number of threads of pubsub executor provider + # Thread pool size for pubsub queue executor provider. If not set - default pubsub executor provider value will be used (5 * number of available processors) executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:}" queue-properties: # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consumed again diff --git a/transport/http/src/main/resources/tb-http-transport.yml b/transport/http/src/main/resources/tb-http-transport.yml index b8070f6575..f3fd100fd7 100644 --- a/transport/http/src/main/resources/tb-http-transport.yml +++ b/transport/http/src/main/resources/tb-http-transport.yml @@ -278,7 +278,7 @@ queue: max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" # Number of messages per a consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" - # Number of threads of pubsub executor provider + # Thread pool size for pubsub queue executor provider. If not set - default pubsub executor provider value will be used (5 * number of available processors) executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:}" queue-properties: # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consume again diff --git a/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml b/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml index b1d40c720f..968385b5e2 100644 --- a/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml +++ b/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml @@ -374,7 +374,7 @@ queue: max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" # Number of messages per consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" - # Number of threads of pubsub executor provider + # Thread pool size for pubsub queue executor provider. If not set - default pubsub executor provider value will be used (5 * number of available processors) executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:}" queue-properties: # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consumed again diff --git a/transport/mqtt/src/main/resources/tb-mqtt-transport.yml b/transport/mqtt/src/main/resources/tb-mqtt-transport.yml index 4d130eecd9..1e97c59253 100644 --- a/transport/mqtt/src/main/resources/tb-mqtt-transport.yml +++ b/transport/mqtt/src/main/resources/tb-mqtt-transport.yml @@ -311,7 +311,7 @@ queue: max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" # Number of messages per consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" - # Number of threads of pubsub executor provider + # Thread pool size for pubsub queue executor provider. If not set - default pubsub executor provider value will be used (5 * number of available processors) executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:}" queue-properties: # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consumed again diff --git a/transport/snmp/src/main/resources/tb-snmp-transport.yml b/transport/snmp/src/main/resources/tb-snmp-transport.yml index 2b89065a55..8d3f842325 100644 --- a/transport/snmp/src/main/resources/tb-snmp-transport.yml +++ b/transport/snmp/src/main/resources/tb-snmp-transport.yml @@ -264,7 +264,7 @@ queue: max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" # Number of messages per consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" - # Number of threads of pubsub executor provider + # Thread pool size for pubsub queue executor provider. If not set - default pubsub executor provider value will be used (5 * number of available processors) executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:}" queue-properties: # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consumed again From e6136148bbd49792adf9aa65861027a1ab5e6fd4 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Fri, 17 Nov 2023 12:37:25 +0200 Subject: [PATCH 156/252] Update apache batik version. Fix some svg images. --- ....markers_placement_image_map#map_image.svg | 766 ++--------------- .../widgets/maps_v2.image_map#map_image.svg | 768 ++---------------- pom.xml | 2 +- 3 files changed, 169 insertions(+), 1367 deletions(-) diff --git a/application/src/main/data/images/widgets/input_widgets.markers_placement_image_map#map_image.svg b/application/src/main/data/images/widgets/input_widgets.markers_placement_image_map#map_image.svg index d98a37e598..9ae4dc607b 100644 --- a/application/src/main/data/images/widgets/input_widgets.markers_placement_image_map#map_image.svg +++ b/application/src/main/data/images/widgets/input_widgets.markers_placement_image_map#map_image.svg @@ -1,685 +1,85 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Lincoln - Harry - Woodlawn - Edgemoor - Oliver - Hillside - Rock - Webb - Central - 13th - 21st - 29th - 37th - 25th - 21st - 13th - Amidon - Arkansas - West - Waco - Mazie - Zoo - 13th - 21st - Nims - Maple - Douglas - - Meridian - Central - Douglas - 47th - Kellogg - McClean - - MacArthur - Seneca - - Pawnee - - Broadway + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Lincoln + Harry + Woodlawn + Edgemoor + Oliver + Hillside + Rock + Webb + Central + 13th + 21st + 29th + 37th + 25th + 21st + 13th + Amidon + Arkansas + West + Waco + Mazie + Zoo + 13th + 21st + Nims + Maple + Douglas + + + + Meridian + Central + Douglas + 47th + Kellogg + McClean + + + MacArthur + Seneca + + Pawnee + + Broadway + diff --git a/application/src/main/data/images/widgets/maps_v2.image_map#map_image.svg b/application/src/main/data/images/widgets/maps_v2.image_map#map_image.svg index d98a37e598..fdfcdfccb4 100644 --- a/application/src/main/data/images/widgets/maps_v2.image_map#map_image.svg +++ b/application/src/main/data/images/widgets/maps_v2.image_map#map_image.svg @@ -1,685 +1,87 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Lincoln - Harry - Woodlawn - Edgemoor - Oliver - Hillside - Rock - Webb - Central - 13th - 21st - 29th - 37th - 25th - 21st - 13th - Amidon - Arkansas - West - Waco - Mazie - Zoo - 13th - 21st - Nims - Maple - Douglas - - Meridian - Central - Douglas - 47th - Kellogg - McClean - - MacArthur - Seneca - - Pawnee - - Broadway + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Lincoln + Harry + Woodlawn + Edgemoor + Oliver + Hillside + Rock + Webb + Central + 13th + 21st + 29th + 37th + 25th + 21st + 13th + Amidon + Arkansas + West + Waco + Mazie + Zoo + 13th + 21st + Nims + Maple + Douglas + + + + Meridian + Central + Douglas + 47th + Kellogg + McClean + + + MacArthur + Seneca + + Pawnee + + Broadway + diff --git a/pom.xml b/pom.xml index 26624cf93d..9fe71b0ae8 100755 --- a/pom.xml +++ b/pom.xml @@ -153,7 +153,7 @@ 1.12.1 6.4.2 1.34.1 - 1.14 + 1.17 From 5cfdf7ad8774a12c6f12c29421000717425602b1 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Fri, 17 Nov 2023 13:36:33 +0200 Subject: [PATCH 157/252] Images api: improvements --- .../service/install/update/ImagesUpdater.java | 35 ++++++++++--------- .../server/dao/resource/ImageService.java | 4 ++- .../server/dao/resource/ResourceService.java | 2 -- .../server/common/data/TbResourceInfo.java | 8 +++++ .../server/dao/resource/BaseImageService.java | 14 +++----- .../dao/resource/BaseResourceService.java | 6 ---- 6 files changed, 34 insertions(+), 35 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/install/update/ImagesUpdater.java b/application/src/main/java/org/thingsboard/server/service/install/update/ImagesUpdater.java index bb5da33631..5b316255a9 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/update/ImagesUpdater.java +++ b/application/src/main/java/org/thingsboard/server/service/install/update/ImagesUpdater.java @@ -30,14 +30,15 @@ import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.TbResourceInfo; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.dao.util.ImageUtils; import org.thingsboard.server.common.data.widget.WidgetTypeDetails; import org.thingsboard.server.common.data.widget.WidgetsBundle; import org.thingsboard.server.dao.resource.ImageService; +import org.thingsboard.server.dao.util.ImageUtils; import java.nio.file.Files; import java.nio.file.Path; import java.util.Base64; +import java.util.List; import java.util.Map; import java.util.Optional; @@ -58,10 +59,10 @@ public class ImagesUpdater { @SneakyThrows public void updateSystemImage(Path imageFile, Map imageNames) { - String imageKey = imageFile.getFileName().toString(); + String imageKey = imageFile.getParent().getFileName() + "." + imageFile.getFileName().toString(); // TODO: add subdir to image key String imageName = imageNames.get(imageKey); if (imageName == null) { - throw new IllegalArgumentException("Image name is missing for " + imageKey + ". Please add it to names.json file"); + imageName = imageKey; } byte[] imageData = Files.readAllBytes(imageFile); String mediaType = ImageUtils.fileExtensionToMediaType("image", StringUtils.substringAfterLast(imageKey, ".")); @@ -203,17 +204,19 @@ public class ImagesUpdater { String existingImageQuery) { TbResourceInfo imageInfo = imageService.getImageInfoByTenantIdAndKey(tenantId, key); if (imageInfo == null && !tenantId.isSysTenantId() && existingImageQuery != null) { - // TODO: need to search among tenant images too (custom widgets) -// List existingSystemImages = imageService.findByTenantIdAndDataAndKeyStartingWith(TenantId.SYS_TENANT_ID, imageData, existingImageQuery); -// if (!existingSystemImages.isEmpty()) { -// imageInfo = existingSystemImages.get(0); -// if (existingSystemImages.size() > 1) { -// log.warn("Found more than one system image resources for key {}", existingImageQuery); -// } -// String link = imageService.getImageLink(imageInfo); -// log.info("Using system image {} for {}", link, key); -// return link; -// } + List similarImages = imageService.findSimilarImagesByTenantIdAndKeyStartingWith(TenantId.SYS_TENANT_ID, imageData, existingImageQuery); + if (similarImages.isEmpty()) { + similarImages = imageService.findSimilarImagesByTenantIdAndKeyStartingWith(tenantId, imageData, existingImageQuery); + } + if (!similarImages.isEmpty()) { + imageInfo = similarImages.get(0); + if (similarImages.size() > 1) { + log.debug("Found more than one image resources for key {}: {}", existingImageQuery, similarImages); + } + String link = imageInfo.getLink(); + log.info("[{}] Using image {} for {}", tenantId, link, key); + return link; + } } TbResource image; if (imageInfo == null) { @@ -224,7 +227,7 @@ public class ImagesUpdater { } else if (tenantId.isSysTenantId()) { image = new TbResource(imageInfo); } else { - return imageService.getImageLink(imageInfo); + return imageInfo.getLink(); } image.setTitle(name); image.setFileName(key); @@ -235,7 +238,7 @@ public class ImagesUpdater { TbResourceInfo savedImage = imageService.saveImage(image); log.info("[{}] {} image '{}' ({})", tenantId, imageInfo == null ? "Created" : "Updated", image.getTitle(), image.getResourceKey()); - return imageService.getImageLink(savedImage); + return savedImage.getLink(); } private String getText(JsonNode jsonNode, String field) { diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ImageService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ImageService.java index 3ae45e86c5..f294083290 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ImageService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ImageService.java @@ -22,6 +22,8 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import java.util.List; + public interface ImageService { TbResourceInfo saveImage(TbResource image) throws Exception; @@ -40,6 +42,6 @@ public interface ImageService { void deleteImage(TenantId tenantId, TbResourceId imageId); - String getImageLink(TbResourceInfo imageInfo); + List findSimilarImagesByTenantIdAndKeyStartingWith(TenantId tenantId, byte[] data, String imageKeyStartingWith); } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java index 21b83d17bd..8614b74502 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java @@ -60,6 +60,4 @@ public interface ResourceService extends EntityDaoService { long sumDataSizeByTenantId(TenantId tenantId); - List findByTenantIdAndDataAndKeyStartingWith(TenantId tenantId, byte[] data, String query); - } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java index 11e21c6451..61df84af44 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java @@ -106,6 +106,14 @@ public class TbResourceInfo extends BaseData implements HasName, H return title; } + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public String getLink() { + if (resourceType == ResourceType.IMAGE) { + return "/api/images/" + (tenantId.isSysTenantId() ? "system" : "tenant") + "/" + resourceKey; + } + return null; + } + @JsonIgnore public String getSearchText() { return searchText != null ? searchText : title; diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java index 857296b9b5..500fe37d33 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java @@ -20,7 +20,6 @@ import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.RegexUtils; import org.thingsboard.server.common.data.ImageDescriptor; import org.thingsboard.server.common.data.ResourceType; @@ -35,6 +34,7 @@ import org.thingsboard.server.dao.service.validator.ResourceDataValidator; import org.thingsboard.server.dao.util.ImageUtils; import org.thingsboard.server.dao.util.ImageUtils.ProcessedImage; +import java.util.List; import java.util.Objects; import java.util.Set; import java.util.regex.Pattern; @@ -152,15 +152,9 @@ public class BaseImageService extends BaseResourceService implements ImageServic } @Override - public String getImageLink(TbResourceInfo imageInfo) { - String link = "/api/images/"; - if (imageInfo.getTenantId().isSysTenantId()) { - link += "system/"; - } else { - link += "tenant/"; - } - link += imageInfo.getResourceKey(); - return link; + public List findSimilarImagesByTenantIdAndKeyStartingWith(TenantId tenantId, byte[] data, String imageKeyStartingWith) { + String etag = calculateEtag(data); + return resourceInfoDao.findByTenantIdAndEtagAndKeyStartingWith(tenantId, etag, imageKeyStartingWith); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java index 9be95db9e7..13ed98b461 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java @@ -216,12 +216,6 @@ public class BaseResourceService extends AbstractCachedEntityService findByTenantIdAndDataAndKeyStartingWith(TenantId tenantId, byte[] data, String query) { - String etag = calculateEtag(data); - return resourceInfoDao.findByTenantIdAndEtagAndKeyStartingWith(tenantId, etag, query); - } - protected String calculateEtag(byte[] data) { return Hashing.sha256().hashBytes(data).toString(); } From 3739b12f19e81d684341b205b853fb600fbb6a22 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Fri, 17 Nov 2023 14:05:11 +0200 Subject: [PATCH 158/252] Update resource structure for edge --- .../edge/rpc/constructor/ResourceMsgConstructor.java | 7 +++++++ .../edge/rpc/processor/resource/BaseResourceProcessor.java | 3 +++ common/edge-api/src/main/proto/edge.proto | 2 ++ 3 files changed, 12 insertions(+) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ResourceMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ResourceMsgConstructor.java index 02f2d5e000..96dd5220bc 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ResourceMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ResourceMsgConstructor.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.service.edge.rpc.constructor; +import com.google.protobuf.ByteString; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.id.TbResourceId; @@ -42,6 +43,12 @@ public class ResourceMsgConstructor { if (tbResource.getEtag() != null) { builder.setEtag(tbResource.getEtag()); } + if (tbResource.getDescriptor() != null) { + builder.setDescriptorJson(tbResource.getDescriptor().toString()); + } + if (tbResource.getPreview() != null) { + builder.setPreview(ByteString.copyFrom(tbResource.getPreview())); + } if (tbResource.getTenantId().equals(TenantId.SYS_TENANT_ID)) { builder.setIsSystem(true); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/BaseResourceProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/BaseResourceProcessor.java index b28e6000d2..5d6d98469c 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/BaseResourceProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/BaseResourceProcessor.java @@ -17,6 +17,7 @@ package org.thingsboard.server.service.edge.rpc.processor.resource; import com.datastax.oss.driver.api.core.uuid.Uuids; import lombok.extern.slf4j.Slf4j; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.TbResource; @@ -61,7 +62,9 @@ public abstract class BaseResourceProcessor extends BaseEdgeProcessor { resource.setResourceKey(resourceKey); resource.setResourceType(resourceType); resource.setFileName(resourceUpdateMsg.getFileName()); + resource.setDescriptor(resourceUpdateMsg.hasDescriptorJson() ? JacksonUtil.toJsonNode(resourceUpdateMsg.getDescriptorJson()) : null); resource.setEncodedData(resourceUpdateMsg.hasData() ? resourceUpdateMsg.getData() : null); + resource.setPreview(resourceUpdateMsg.hasPreview() ? resourceUpdateMsg.getPreview().toByteArray() : null); resource.setEtag(resourceUpdateMsg.hasEtag() ? resourceUpdateMsg.getEtag() : null); resourceValidator.validate(resource, TbResourceInfo::getTenantId); if (created) { diff --git a/common/edge-api/src/main/proto/edge.proto b/common/edge-api/src/main/proto/edge.proto index 71e84304ed..2411b0af53 100644 --- a/common/edge-api/src/main/proto/edge.proto +++ b/common/edge-api/src/main/proto/edge.proto @@ -436,6 +436,8 @@ message ResourceUpdateMsg { optional string data = 8; optional string etag = 9; bool isSystem = 10; + optional string descriptorJson = 11; + optional bytes preview = 12; } message RuleChainMetadataRequestMsg { From 9abfd128aed4663115d77bf518d9c38671d6ea78 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Fri, 17 Nov 2023 13:35:12 +0100 Subject: [PATCH 159/252] fixed fluky tests --- .../server/msa/connectivity/MqttClientTest.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/MqttClientTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/MqttClientTest.java index 5a7e9b631d..6ad50196b3 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/MqttClientTest.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/MqttClientTest.java @@ -26,6 +26,7 @@ import io.netty.buffer.Unpooled; import io.netty.handler.codec.mqtt.MqttQoS; import lombok.Data; import lombok.extern.slf4j.Slf4j; +import org.awaitility.Awaitility; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @@ -337,8 +338,12 @@ public class MqttClientTest extends AbstractContainerTest { MqttClient mqttClient = getMqttClient(deviceCredentials, listener); testRestClient.deleteDeviceIfExists(device.getId()); - TimeUnit.SECONDS.sleep(3 * timeoutMultiplier); - assertThat(mqttClient.isConnected()).isFalse(); + + Awaitility + .await() + .alias("Check device connection.") + .atMost(10, TimeUnit.SECONDS) + .until(() -> !mqttClient.isConnected()); } @Test From a17a17ee973a9a04b672d2695a75761115999dff Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Fri, 17 Nov 2023 13:36:38 +0100 Subject: [PATCH 160/252] remove cassandra volume for blackbox --- .../org/thingsboard/server/msa/ThingsBoardDbInstaller.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ThingsBoardDbInstaller.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ThingsBoardDbInstaller.java index fe467ec341..7d87351bef 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ThingsBoardDbInstaller.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ThingsBoardDbInstaller.java @@ -253,12 +253,16 @@ public class ThingsBoardDbInstaller { .add(tbVcExecutorLogVolume) .add(resolveRedisComposeVolumeLog()); + if (IS_HYBRID_MODE) { + rmVolumesCommand.add(cassandraDataVolume); + } + dockerCompose.withCommand(rmVolumesCommand.toString()); } private String resolveRedisComposeVolumeLog() { if (IS_REDIS_CLUSTER) { - return IntStream.range(0, 6).mapToObj(i -> redisClusterDataVolume + "-" + i).collect(Collectors.joining()); + return IntStream.range(0, 6).mapToObj(i -> " " + redisClusterDataVolume + "-" + i).collect(Collectors.joining()); } if (IS_REDIS_SENTINEL) { return redisSentinelDataVolume + "-" + "master " + " " + From 66b768a499bb299960871fe56ae9a0da2f79f1cb Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Fri, 17 Nov 2023 14:45:38 +0200 Subject: [PATCH 161/252] added @Lazy to PubSubRuleNodeExecutorProvider --- .../org/thingsboard/server/actors/ActorSystemContext.java | 1 + .../service/executors/PubSubRuleNodeExecutorProvider.java | 6 +++++- .../server/queue/pubsub/TbPubSubQueueExecutorProvider.java | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java index 24a240661f..607d5d8791 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java @@ -323,6 +323,7 @@ public class ActorSystemContext { @Getter private NotificationExecutorService notificationExecutor; + @Lazy @Autowired @Getter private PubSubRuleNodeExecutorProvider pubSubRuleNodeExecutorProvider; diff --git a/application/src/main/java/org/thingsboard/server/service/executors/PubSubRuleNodeExecutorProvider.java b/application/src/main/java/org/thingsboard/server/service/executors/PubSubRuleNodeExecutorProvider.java index 520b8f855c..74e1222acf 100644 --- a/application/src/main/java/org/thingsboard/server/service/executors/PubSubRuleNodeExecutorProvider.java +++ b/application/src/main/java/org/thingsboard/server/service/executors/PubSubRuleNodeExecutorProvider.java @@ -16,15 +16,19 @@ package org.thingsboard.server.service.executors; import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import org.thingsboard.common.util.ExecutorProvider; import org.thingsboard.common.util.ThingsBoardThreadFactory; +import org.thingsboard.server.queue.util.TbRuleEngineComponent; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; +@Lazy +@TbRuleEngineComponent @Component public class PubSubRuleNodeExecutorProvider implements ExecutorProvider { @@ -53,7 +57,7 @@ public class PubSubRuleNodeExecutorProvider implements ExecutorProvider { @PreDestroy private void destroy() { if (executor != null) { - executor.shutdown(); + executor.shutdownNow(); } } } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubQueueExecutorProvider.java b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubQueueExecutorProvider.java index 5e5f19651c..370670d554 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubQueueExecutorProvider.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubQueueExecutorProvider.java @@ -54,7 +54,7 @@ public class TbPubSubQueueExecutorProvider implements ExecutorProvider { @PreDestroy private void destroy() { if (executor != null) { - executor.shutdown(); + executor.shutdownNow(); } } } From 3e95561e012a6fa8ebcf77eb982ee6b96897e42f Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Fri, 17 Nov 2023 16:54:03 +0200 Subject: [PATCH 162/252] Revert subdir for system image key --- .../server/service/install/update/ImagesUpdater.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/java/org/thingsboard/server/service/install/update/ImagesUpdater.java b/application/src/main/java/org/thingsboard/server/service/install/update/ImagesUpdater.java index 5b316255a9..81374f7a58 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/update/ImagesUpdater.java +++ b/application/src/main/java/org/thingsboard/server/service/install/update/ImagesUpdater.java @@ -59,7 +59,7 @@ public class ImagesUpdater { @SneakyThrows public void updateSystemImage(Path imageFile, Map imageNames) { - String imageKey = imageFile.getParent().getFileName() + "." + imageFile.getFileName().toString(); // TODO: add subdir to image key + String imageKey = imageFile.getFileName().toString(); // TODO: add subdir to image key String imageName = imageNames.get(imageKey); if (imageName == null) { imageName = imageKey; From 3c40cd8ff136b19ff0c000c3c10e93fbe759eb50 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Fri, 17 Nov 2023 17:12:47 +0200 Subject: [PATCH 163/252] UI: Implement image service. Implement image load pipe. --- ui-ngx/src/app/core/http/image.service.ts | 139 ++++++++++++++++++ ui-ngx/src/app/core/utils.ts | 9 +- .../dashboard-widget-select.component.html | 4 +- .../dashboard-widget-select.component.ts | 13 +- .../widget/lib/maps/common-maps-utils.ts | 82 ++++++----- .../components/widget/lib/maps/markers.ts | 27 ++-- .../widget/lib/maps/providers/image-map.ts | 58 ++++---- .../widgets-bundle-widgets.component.html | 2 +- .../widgets-bundle-widgets.component.ts | 10 -- .../src/app/shared/models/resource.models.ts | 29 +++- ui-ngx/src/app/shared/pipe/image.pipe.ts | 52 +++++++ ui-ngx/src/app/shared/pipe/public-api.ts | 1 + ui-ngx/src/app/shared/shared.module.ts | 4 + 13 files changed, 335 insertions(+), 95 deletions(-) create mode 100644 ui-ngx/src/app/core/http/image.service.ts create mode 100644 ui-ngx/src/app/shared/pipe/image.pipe.ts diff --git a/ui-ngx/src/app/core/http/image.service.ts b/ui-ngx/src/app/core/http/image.service.ts new file mode 100644 index 0000000000..f52f55eb27 --- /dev/null +++ b/ui-ngx/src/app/core/http/image.service.ts @@ -0,0 +1,139 @@ +/// +/// Copyright © 2016-2023 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 { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { PageLink } from '@shared/models/page/page-link'; +import { defaultHttpOptionsFromConfig, defaultHttpUploadOptions, RequestConfig } from '@core/http/http-utils'; +import { Observable, of } from 'rxjs'; +import { PageData } from '@shared/models/page/page-data'; +import { + NO_IMAGE_DATA_URI, + ImageResourceInfo, + imageResourceType, + ImageResourceType, + IMAGES_URL_PREFIX, isImageResourceUrl +} from '@shared/models/resource.models'; +import { catchError, map, switchMap } from 'rxjs/operators'; +import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; +import { blobToBase64 } from '@core/utils'; + +@Injectable({ + providedIn: 'root' +}) +export class ImageService { + constructor( + private http: HttpClient, + private sanitizer: DomSanitizer + ) { + } + + public uploadImage(file: File, config?: RequestConfig): Observable { + if (!config) { + config = {}; + } + const formData = new FormData(); + formData.append('file', file); + return this.http.post('/api/image', formData, + defaultHttpUploadOptions(config.ignoreLoading, config.ignoreErrors, config.resendRequest)); + } + + public updateImage(type: ImageResourceType, key: string, file: File, config?: RequestConfig): Observable { + if (!config) { + config = {}; + } + const formData = new FormData(); + formData.append('file', file); + return this.http.post(`${IMAGES_URL_PREFIX}/${type}/${encodeURIComponent(key)}`, formData, + defaultHttpUploadOptions(config.ignoreLoading, config.ignoreErrors, config.resendRequest)); + } + + public updateImageInfo(imageInfo: ImageResourceInfo, config?: RequestConfig): Observable { + const type = imageResourceType(imageInfo); + const key = encodeURIComponent(imageInfo.resourceKey); + return this.http.put(`${IMAGES_URL_PREFIX}/${type}/${key}/info`, + imageInfo, defaultHttpOptionsFromConfig(config)); + } + + public getImages(pageLink: PageLink, config?: RequestConfig): Observable> { + return this.http.get>(`${IMAGES_URL_PREFIX}${pageLink.toQuery()}`, defaultHttpOptionsFromConfig(config)); + } + + public getImageInfo(type: ImageResourceType, key: string, config?: RequestConfig): Observable { + return this.http.get(`${IMAGES_URL_PREFIX}/${type}/${encodeURIComponent(key)}/info`, + defaultHttpOptionsFromConfig(config)); + } + + public getImageDataUrl(imageUrl: string, preview = false, asString = false): Observable { + const parts = imageUrl.split('/'); + const key = parts[parts.length - 1]; + parts[parts.length - 1] = encodeURIComponent(key); + const encodedUrl = parts.join('/'); + const imageLink = preview ? (encodedUrl + '/preview') : encodedUrl; + const options = defaultHttpOptionsFromConfig({ignoreLoading: true, ignoreErrors: true}); + return this.http + .get(imageLink, {...options, ...{ responseType: 'blob' } }).pipe( + switchMap(val => blobToBase64(val).pipe( + map((dataUrl) => asString ? dataUrl : this.sanitizer.bypassSecurityTrustUrl(dataUrl)) + )), + catchError(() => of(asString ? NO_IMAGE_DATA_URI : this.sanitizer.bypassSecurityTrustUrl(NO_IMAGE_DATA_URI))) + ); + } + + public resolveImageUrl(imageUrl: string, preview = false, asString = false): Observable { + if (isImageResourceUrl(imageUrl)) { + return this.getImageDataUrl(imageUrl, preview, asString); + } else { + return of(asString ? imageUrl : this.sanitizer.bypassSecurityTrustUrl(imageUrl)); + } + } + + public downloadImage(type: ImageResourceType, key: string): Observable { + return this.http.get(`${IMAGES_URL_PREFIX}/${type}/${encodeURIComponent(key)}`, { + responseType: 'arraybuffer', + observe: 'response' + }).pipe( + map((response) => { + const headers = response.headers; + const filename = headers.get('x-filename'); + const contentType = headers.get('content-type'); + const linkElement = document.createElement('a'); + try { + const blob = new Blob([response.body], {type: contentType}); + const url = URL.createObjectURL(blob); + linkElement.setAttribute('href', url); + linkElement.setAttribute('download', filename); + const clickEvent = new MouseEvent('click', + { + view: window, + bubbles: true, + cancelable: false + } + ); + linkElement.dispatchEvent(clickEvent); + return null; + } catch (e) { + throw e; + } + }) + ); + } + + public deleteImage(type: ImageResourceType, key: string, config?: RequestConfig) { + return this.http.delete(`${IMAGES_URL_PREFIX}${type}/${encodeURIComponent}`, defaultHttpOptionsFromConfig(config)); + } + +} diff --git a/ui-ngx/src/app/core/utils.ts b/ui-ngx/src/app/core/utils.ts index b810689b69..feeb5de02f 100644 --- a/ui-ngx/src/app/core/utils.ts +++ b/ui-ngx/src/app/core/utils.ts @@ -15,7 +15,7 @@ /// import _ from 'lodash'; -import { Observable, Subject } from 'rxjs'; +import { from, Observable, Subject } from 'rxjs'; import { finalize, share } from 'rxjs/operators'; import { Datasource, DatasourceData, FormattedData, ReplaceInfo } from '@app/shared/models/widget.models'; import { EntityId } from '@shared/models/id/entity-id'; @@ -209,6 +209,13 @@ export function stringToBase64(value: string): string { })); } +export const blobToBase64 = (blob: Blob): Observable => from(new Promise((resolve) => { + const reader = new FileReader(); + reader.onloadend = () => resolve(reader.result as string); + reader.readAsDataURL(blob); + } + )); + const scrollRegex = /(auto|scroll)/; function parentNodes(node: Node, nodes: Node[]): Node[] { diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.html b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.html index 4431820808..a32dc812cd 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.html +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.html @@ -51,7 +51,7 @@
- {{ item.title }} + {{ item.title }}
@@ -95,7 +95,7 @@
- {{ item.title }} + {{ item.title }}
diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.ts b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.ts index 7e8f31578b..ff9afe6920 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.ts +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.ts @@ -28,8 +28,7 @@ import { } from '@shared/models/widget.models'; import { debounceTime, distinctUntilChanged, map, skip } from 'rxjs/operators'; import { BehaviorSubject, combineLatest } from 'rxjs'; -import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; -import { isDefinedAndNotNull, isObject } from '@core/utils'; +import { isObject } from '@core/utils'; import { PageLink } from '@shared/models/page/page-link'; import { Direction } from '@shared/models/page/sort-order'; import { GridEntitiesFetchFunction, ScrollGridColumns } from '@home/models/datasource/scroll-grid-datasource'; @@ -158,8 +157,7 @@ export class DashboardWidgetSelectComponent implements OnInit { widgetsFilter: BundleWidgetsFilter = {search: '', filter: null, deprecatedFilter: DeprecatedFilter.ACTUAL, widgetsBundleId: null}; constructor(private widgetsService: WidgetService, - private cd: ChangeDetectorRef, - private sanitizer: DomSanitizer) { + private cd: ChangeDetectorRef) { this.widgetBundlesFetchFunction = (pageSize, page, filter) => { const pageLink = new PageLink(pageSize, page, filter, { @@ -241,13 +239,6 @@ export class DashboardWidgetSelectComponent implements OnInit { } } - getPreviewImage(imageUrl: string | null): SafeUrl | string { - if (isDefinedAndNotNull(imageUrl)) { - return this.sanitizer.bypassSecurityTrustUrl(imageUrl); - } - return '/assets/widget-preview-empty.svg'; - } - isObject(value: any): boolean { return isObject(value); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/common-maps-utils.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/common-maps-utils.ts index 934f9be6fe..476a755bc2 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/common-maps-utils.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/common-maps-utils.ts @@ -25,10 +25,11 @@ import { isUndefined, padValue } from '@core/utils'; -import { Observable, Observer, of } from 'rxjs'; +import { Observable, Observer, of, switchMap } from 'rxjs'; import { map } from 'rxjs/operators'; import { FormattedData } from '@shared/models/widget.models'; import L from 'leaflet'; +import { ImagePipe } from '@shared/pipe/image.pipe'; export function getRatio(firsMoment: number, secondMoment: number, intermediateMoment: number): number { return (intermediateMoment - firsMoment) / (secondMoment - firsMoment); @@ -71,44 +72,59 @@ export function getDefCenterPosition(position): [number, number] { } -const imageAspectMap = {}; - -function imageLoader(imageUrl: string): Observable { - return new Observable((observer: Observer) => { - const image = document.createElement('img'); // support IE - image.style.position = 'absolute'; - image.style.left = '-99999px'; - image.style.top = '-99999px'; - image.onload = () => { - observer.next(image); - document.body.removeChild(image); - observer.complete(); - }; - image.onerror = err => { - observer.error(err); - document.body.removeChild(image); - observer.complete(); - }; - document.body.appendChild(image); - image.src = imageUrl; - }); +const imageAspectMap: {[key: string]: ImageWithAspect} = {}; + +const imageLoader = (imageUrl: string): Observable => new Observable((observer: Observer) => { + const image = document.createElement('img'); // support IE + image.style.position = 'absolute'; + image.style.left = '-99999px'; + image.style.top = '-99999px'; + image.onload = () => { + observer.next(image); + document.body.removeChild(image); + observer.complete(); + }; + image.onerror = err => { + observer.error(err); + document.body.removeChild(image); + observer.complete(); + }; + document.body.appendChild(image); + image.src = imageUrl; +}); + +const loadImageAspect = (imageUrl: string): Observable => + imageLoader(imageUrl).pipe(map(image => image.width / image.height)); + +export interface ImageWithAspect { + url: string; + aspect: number; } -export function aspectCache(imageUrl: string): Observable { +export const loadImageWithAspect = (imagePipe: ImagePipe, imageUrl: string): Observable => { if (imageUrl?.length) { const hash = hashCode(imageUrl); - let aspect = imageAspectMap[hash]; - if (aspect) { - return of(aspect); + let imageWithAspect = imageAspectMap[hash]; + if (imageWithAspect) { + return of(imageWithAspect); + } else { + return imagePipe.transform(imageUrl, {asString: true, ignoreLoadingImage: true}).pipe( + switchMap((res) => { + const url = res as string; + return loadImageAspect(url).pipe( + map((aspect) => { + imageWithAspect = {url, aspect}; + imageAspectMap[hash] = imageWithAspect; + return imageWithAspect; + }) + ); + }) + ); } - return imageLoader(imageUrl).pipe(map(image => { - aspect = image.width / image.height; - imageAspectMap[hash] = aspect; - return aspect; - })); + } else { + return of(null); } - return of(0); -} +}; export type TranslateFunc = (key: string, defaultTranslation?: string) => string; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts index 5a4c80b12c..29d92b2057 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts @@ -17,11 +17,12 @@ import L, { LeafletMouseEvent } from 'leaflet'; import { MarkerIconInfo, MarkerIconReadyFunction, MarkerImageInfo, WidgetMarkersSettings, } from './map-models'; import { bindPopupActions, createTooltip } from './maps-utils'; -import { aspectCache, parseWithTranslation } from './common-maps-utils'; +import { loadImageWithAspect, parseWithTranslation } from './common-maps-utils'; import tinycolor from 'tinycolor2'; import { fillDataPattern, isDefined, isDefinedAndNotNull, processDataPattern, safeExecute } from '@core/utils'; import LeafletMap from './leaflet-map'; import { FormattedData } from '@shared/models/widget.models'; +import { ImagePipe } from '@shared/pipe/image.pipe'; export class Marker { @@ -167,16 +168,16 @@ export class Marker { } } if (currentImage && currentImage.url) { - aspectCache(currentImage.url).subscribe( - (aspect) => { - if (aspect) { + loadImageWithAspect(this.map.ctx.$injector.get(ImagePipe), currentImage.url).subscribe( + (aspectImage) => { + if (aspectImage?.aspect) { let width; let height; - if (aspect > 1) { + if (aspectImage.aspect > 1) { width = currentImage.size; - height = currentImage.size / aspect; + height = currentImage.size / aspectImage.aspect; } else { - width = currentImage.size * aspect; + width = currentImage.size * aspectImage.aspect; height = currentImage.size; } let iconAnchor = currentImage.markerOffset; @@ -188,7 +189,7 @@ export class Marker { popupAnchor = [width * this.tooltipOffset[0], height * this.tooltipOffset[1]]; } const icon = L.icon({ - iconUrl: currentImage.url, + iconUrl: aspectImage.url, iconSize: [width, height], iconAnchor, popupAnchor @@ -237,7 +238,15 @@ export class Marker { } createColorIconURI(color: tinycolor.Instance): string { - const svg = ``; + const svg = `` + + ``; return 'data:image/svg+xml;base64,' + btoa(svg); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts index be6ee64753..1a05ec547b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts @@ -23,11 +23,10 @@ import { PosFunction, WidgetUnitedMapSettings } from '../map-models'; -import { Observable, ReplaySubject } from 'rxjs'; +import { Observable, of, ReplaySubject, switchMap } from 'rxjs'; import { catchError, map, mergeMap } from 'rxjs/operators'; import { - aspectCache, - calculateNewPointCoordinate + calculateNewPointCoordinate, loadImageWithAspect } from '@home/components/widget/lib/maps/common-maps-utils'; import { WidgetContext } from '@home/models/widget-component.models'; import { DataSet, DatasourceType, FormattedData, widgetType } from '@shared/models/widget.models'; @@ -35,6 +34,7 @@ import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { WidgetSubscriptionOptions } from '@core/api/widget-api.models'; import { isDefinedAndNotNull, isEmptyStr, isNotEmptyStr, parseFunction } from '@core/utils'; import { EntityDataPageLink } from '@shared/models/query/query.models'; +import { ImagePipe } from '@shared/pipe/image.pipe'; const maxZoom = 4; // ? @@ -122,39 +122,43 @@ export class ImageMap extends LeafletMap { } private imageFromUrl(url: string): Observable { - return aspectCache(url).pipe( - map( aspect => { - const mapImage: MapImage = { - imageUrl: url, - aspect, - update: false - }; - return mapImage; + return loadImageWithAspect(this.ctx.$injector.get(ImagePipe), url).pipe( + switchMap( aspectImage => { + if (aspectImage) { + return of({ + imageUrl: aspectImage.url, + aspect: aspectImage.aspect, + update: false + }); + } else { + return this.imageFromUrl(defaultImageMapProviderSettings.mapImageUrl); + } } ), - catchError((e) => { - return this.imageFromUrl(defaultImageMapProviderSettings.mapImageUrl); - }) + catchError((e) => this.imageFromUrl(defaultImageMapProviderSettings.mapImageUrl)) ); } private imageFromAlias(alias: Observable<[DataSet, boolean]>): Observable { return alias.pipe( - mergeMap(res => { + switchMap(res => { + const url = res[0][0][1]; const mapImage: MapImage = { - imageUrl: res[0][0][1], + imageUrl: null, aspect: null, update: res[1] }; - return aspectCache(mapImage.imageUrl).pipe( - map((aspect) => { - mapImage.aspect = aspect; - return mapImage; - } - ), - catchError((e) => { - return this.imageFromUrl(defaultImageMapProviderSettings.mapImageUrl); - }) + return loadImageWithAspect(this.ctx.$injector.get(ImagePipe), url).pipe( + switchMap((aspectImage) => { + if (aspectImage) { + mapImage.aspect = aspectImage.aspect; + mapImage.imageUrl = aspectImage.url; + return of(mapImage); + } else { + return this.imageFromUrl(defaultImageMapProviderSettings.mapImageUrl); + } + }), + catchError((e) => this.imageFromUrl(defaultImageMapProviderSettings.mapImageUrl)) ); }) ); @@ -249,7 +253,7 @@ export class ImageMap extends LeafletMap { } } - extractPosition(data: FormattedData): {x: number, y: number} { + extractPosition(data: FormattedData): {x: number; y: number} { if (!data) { return null; } @@ -261,7 +265,7 @@ export class ImageMap extends LeafletMap { return {x: xPos, y: yPos}; } - positionToLatLng(position: {x: number, y: number}): L.LatLng { + positionToLatLng(position: {x: number; y: number}): L.LatLng { return this.pointToLatLng( position.x * this.width, position.y * this.height); diff --git a/ui-ngx/src/app/modules/home/pages/widget/widgets-bundle-widgets.component.html b/ui-ngx/src/app/modules/home/pages/widget/widgets-bundle-widgets.component.html index fdd49c4a61..f9f4990d21 100644 --- a/ui-ngx/src/app/modules/home/pages/widget/widgets-bundle-widgets.component.html +++ b/ui-ngx/src/app/modules/home/pages/widget/widgets-bundle-widgets.component.html @@ -62,7 +62,7 @@
- {{ widget.name }} + {{ widget.name }}
{{ widget.name }}
{{ 'widget.deprecated' | translate }}
diff --git a/ui-ngx/src/app/modules/home/pages/widget/widgets-bundle-widgets.component.ts b/ui-ngx/src/app/modules/home/pages/widget/widgets-bundle-widgets.component.ts index cc54c53901..23cc43bada 100644 --- a/ui-ngx/src/app/modules/home/pages/widget/widgets-bundle-widgets.component.ts +++ b/ui-ngx/src/app/modules/home/pages/widget/widgets-bundle-widgets.component.ts @@ -28,8 +28,6 @@ import { widgetType as WidgetDataType, WidgetTypeInfo } from '@shared/models/wid import { CdkDragDrop } from '@angular/cdk/drag-drop'; import { ImportExportService } from '@home/components/import-export/import-export.service'; import { WidgetService } from '@core/http/widget.service'; -import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; -import { isDefinedAndNotNull } from '@core/utils'; import { FormControl, Validators } from '@angular/forms'; import { MatDialog } from '@angular/material/dialog'; import { SelectWidgetTypeDialogComponent } from '@home/pages/widget/select-widget-type-dialog.component'; @@ -61,7 +59,6 @@ export class WidgetsBundleWidgetsComponent extends PageComponent implements OnIn private route: ActivatedRoute, private widgetsService: WidgetService, private importExport: ImportExportService, - private sanitizer: DomSanitizer, private cd: ChangeDetectorRef, private dialog: MatDialog) { super(store); @@ -86,13 +83,6 @@ export class WidgetsBundleWidgetsComponent extends PageComponent implements OnIn ngOnInit(): void { } - getPreviewImage(imageUrl: string | null): SafeUrl | string { - if (isDefinedAndNotNull(imageUrl)) { - return this.sanitizer.bypassSecurityTrustUrl(imageUrl); - } - return '/assets/widget-preview-empty.svg'; - } - trackByWidget(index: number, widget: WidgetTypeBundle): any { return widget; } diff --git a/ui-ngx/src/app/shared/models/resource.models.ts b/ui-ngx/src/app/shared/models/resource.models.ts index 3014711d57..e494e3842e 100644 --- a/ui-ngx/src/app/shared/models/resource.models.ts +++ b/ui-ngx/src/app/shared/models/resource.models.ts @@ -17,6 +17,8 @@ import { BaseData, ExportableEntity } from '@shared/models/base-data'; import { TenantId } from '@shared/models/id/tenant-id'; import { TbResourceId } from '@shared/models/id/tb-resource-id'; +import { NULL_UUID } from '@shared/models/id/has-uuid'; +import { BaseWidgetType } from '@shared/models/widget.models'; export enum ResourceType { LWM2M_MODEL = 'LWM2M_MODEL', @@ -55,15 +57,40 @@ export const ResourceTypeTranslationMap = new Map( ] ); -export interface ResourceInfo extends Omit, 'name' | 'label'>, ExportableEntity { +export interface TbResourceInfo extends Omit, 'name' | 'label'>, ExportableEntity { tenantId?: TenantId; resourceKey?: string; title?: string; resourceType: ResourceType; fileName: string; + descriptor?: D; } +export type ResourceInfo = TbResourceInfo; + export interface Resource extends ResourceInfo { data: string; name?: string; } + +export interface ImageDescriptor { + mediaType: string; + width: number; + height: number; + size: number; + etag: string; + previewDescriptor: ImageDescriptor; +} + +export type ImageResourceInfo = TbResourceInfo; + +export type ImageResourceType = 'tenant' | 'system'; + +export const imageResourceType = (imageInfo: ImageResourceInfo): ImageResourceType => + (!imageInfo.tenantId || imageInfo.tenantId?.id === NULL_UUID) ? 'system' : 'tenant'; + +export const IMAGES_URL_PREFIX = '/api/images'; + +export const isImageResourceUrl = (url: string): boolean => url && url.startsWith(IMAGES_URL_PREFIX); + +export const NO_IMAGE_DATA_URI = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=='; diff --git a/ui-ngx/src/app/shared/pipe/image.pipe.ts b/ui-ngx/src/app/shared/pipe/image.pipe.ts new file mode 100644 index 0000000000..fb54cb6985 --- /dev/null +++ b/ui-ngx/src/app/shared/pipe/image.pipe.ts @@ -0,0 +1,52 @@ +/// +/// Copyright © 2016-2023 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 { Pipe, PipeTransform } from '@angular/core'; +import { ImageService } from '@core/http/image.service'; +import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; +import { BehaviorSubject, Observable, of, Subject } from 'rxjs'; +import { isDefinedAndNotNull } from '@core/utils'; +import { NO_IMAGE_DATA_URI } from '@shared/models/resource.models'; + +const LOADING_IMAGE_DATA_URI = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZlcnNpb249IjEuMSIgdmlld0JveD0iMCAwIDIwIDIwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPgo='; + +@Pipe({ + name: 'image' +}) +export class ImagePipe implements PipeTransform { + + constructor(private imageService: ImageService, + private sanitizer: DomSanitizer) { } + + transform(url: string, args?: any): Observable { + const ignoreLoadingImage = !!args?.ignoreLoadingImage; + const asString = !!args?.asString; + const image$ = ignoreLoadingImage ? new Subject() : new BehaviorSubject(LOADING_IMAGE_DATA_URI); + if (isDefinedAndNotNull(url)) { + const preview = !!args?.preview; + this.imageService.resolveImageUrl(url, preview, asString).subscribe((imageUrl) => { + image$.next(imageUrl); + image$.complete(); + }); + } else { + const emptyUrl = args?.emptyUrl || NO_IMAGE_DATA_URI; + image$.next(asString ? emptyUrl : this.sanitizer.bypassSecurityTrustUrl(emptyUrl)); + image$.complete(); + } + return image$.asObservable(); + } + +} diff --git a/ui-ngx/src/app/shared/pipe/public-api.ts b/ui-ngx/src/app/shared/pipe/public-api.ts index 9ba3be3610..f03d669345 100644 --- a/ui-ngx/src/app/shared/pipe/public-api.ts +++ b/ui-ngx/src/app/shared/pipe/public-api.ts @@ -23,3 +23,4 @@ export * from './nospace.pipe'; export * from './truncate.pipe'; export * from './file-size.pipe'; export * from './selectable-columns.pipe'; +export * from './image.pipe'; diff --git a/ui-ngx/src/app/shared/shared.module.ts b/ui-ngx/src/app/shared/shared.module.ts index 042e453fe1..f1302e0b24 100644 --- a/ui-ngx/src/app/shared/shared.module.ts +++ b/ui-ngx/src/app/shared/shared.module.ts @@ -200,6 +200,7 @@ import { ColorPickerPanelComponent } from '@shared/components/color-picker/color import { TbIconComponent } from '@shared/components/icon.component'; import { HintTooltipIconComponent } from '@shared/components/hint-tooltip-icon.component'; import { StringAutocompleteComponent } from '@shared/components/string-autocomplete.component'; +import { ImagePipe } from '@shared/pipe/image.pipe'; export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) { return markedOptionsService; @@ -217,6 +218,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) DateAgoPipe, SafePipe, ShortNumberPipe, + ImagePipe, { provide: FlowInjectionToken, useValue: Flow @@ -340,6 +342,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) TbJsonPipe, FileSizePipe, DateAgoPipe, + ImagePipe, SafePipe, ShortNumberPipe, SelectableColumnsPipe, @@ -576,6 +579,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) KeyboardShortcutPipe, FileSizePipe, DateAgoPipe, + ImagePipe, SafePipe, ShortNumberPipe, SelectableColumnsPipe, From 336161e1a2101ad442570021699fa15c73319089 Mon Sep 17 00:00:00 2001 From: imbeacon Date: Fri, 17 Nov 2023 17:36:22 +0200 Subject: [PATCH 164/252] Added fix for docker command for gateway launch command and support for ipv6 adresses in device connectivity --- .../DeviceConnectivityController.java | 2 +- .../DeviceConnectivityControllerTest.java | 271 ++++++++++++------ .../device/DeviceConnectivityServiceImpl.java | 20 +- .../dao/util/DeviceConnectivityUtil.java | 50 +++- 4 files changed, 235 insertions(+), 108 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceConnectivityController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceConnectivityController.java index 83d4d16034..dca113621c 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceConnectivityController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceConnectivityController.java @@ -106,7 +106,7 @@ public class DeviceConnectivityController extends BaseController { @RequestMapping(value = "/device-connectivity/gateway-launch/{deviceId}", method = RequestMethod.GET) @ResponseBody public JsonNode getGatewayLaunchCommands(@ApiParam(value = DEVICE_ID_PARAM_DESCRIPTION) - @PathVariable(DEVICE_ID) String strDeviceId, HttpServletRequest request) throws ThingsboardException, URISyntaxException { + @PathVariable(DEVICE_ID) String strDeviceId, HttpServletRequest request) throws ThingsboardException, URISyntaxException { checkParameter(DEVICE_ID, strDeviceId); DeviceId deviceId = new DeviceId(toUUID(strDeviceId)); Device device = checkDeviceId(deviceId, Operation.READ_CREDENTIALS); 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 227d37f483..b629408855 100644 --- a/application/src/test/java/org/thingsboard/server/controller/DeviceConnectivityControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/DeviceConnectivityControllerTest.java @@ -18,8 +18,6 @@ package org.thingsboard.server.controller; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; -import com.google.common.util.concurrent.ListeningExecutorService; -import com.google.common.util.concurrent.MoreExecutors; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -32,7 +30,6 @@ import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestPropertySource; import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; @@ -46,7 +43,6 @@ import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileCon import org.thingsboard.server.common.data.device.profile.DeviceProfileData; import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.id.DeviceProfileId; -import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentialsType; @@ -58,14 +54,16 @@ import java.nio.file.Path; import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.CA_ROOT_CERT_PEM; import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.COAP; import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.COAPS; import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.DOCKER; import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.HTTP; import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.HTTPS; +import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.LINUX; import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.MQTT; import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.MQTTS; -import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.CA_ROOT_CERT_PEM; +import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.WINDOWS; @TestPropertySource(properties = { "device.connectivity.mqtts.pem_cert_file=/tmp/" + CA_ROOT_CERT_PEM @@ -294,6 +292,140 @@ public class DeviceConnectivityControllerTest extends AbstractControllerTest { DEVICE_TELEMETRY_TOPIC, credentials.getCredentialsId())); } + @Test + public void testFetchGatewayLaunchCommands() throws Exception { + Device device = new Device(); + device.setName("My device"); + device.setType("default"); + ObjectNode additionalInfo = JacksonUtil.newObjectNode(); + additionalInfo.put("gateway", true); + device.setAdditionalInfo(additionalInfo); + Device savedDevice = doPost("/api/device", device, Device.class); + DeviceCredentials credentials = + doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class); + + JsonNode commands = + doGetTyped("/api/device-connectivity/gateway-launch/" + savedDevice.getId().getId(), new TypeReference<>() { + }); + + JsonNode dockerMqttCommands = commands.get(MQTT); + assertThat(dockerMqttCommands.get(LINUX).asText()).isEqualTo(String.format("docker run -it -v ~/.tb-gateway/logs:/thingsboard_gateway/logs -v ~/.tb-gateway/extensions:/thingsboard_gateway/extensions -v ~/.tb-gateway/config:/thingsboard_gateway/config --network=host -p 5000:5000 --name tbGatewayLocalhost -e host=localhost -e port=1883 -e accessToken=%s --restart always thingsboard/tb-gateway", credentials.getCredentialsId())); + assertThat(dockerMqttCommands.get(WINDOWS).asText()).isEqualTo("docker run -it -v %HOMEPATH%/tb-gateway/logs:/thingsboard_gateway/logs -v %HOMEPATH%/tb-gateway/extensions:/thingsboard_gateway/extensions -v %HOMEPATH%/tb-gateway/config:/thingsboard_gateway/config --network=host -p 5000:5000 --name tbGatewayLocalhost -e host=localhost -e port=1883 -e accessToken=" + credentials.getCredentialsId() + " --restart always thingsboard/tb-gateway"); + + JsonNode dockerMqttsCommands = commands.get(MQTTS); + assertThat(dockerMqttsCommands.get(LINUX).asText()).isEqualTo(String.format("docker run -it -v ~/.tb-gateway/logs:/thingsboard_gateway/logs -v ~/.tb-gateway/extensions:/thingsboard_gateway/extensions -v ~/.tb-gateway/config:/thingsboard_gateway/config --network=host -p 5000:5000 --name tbGatewayLocalhost -e host=localhost -e port=8883 -e accessToken=%s --restart always thingsboard/tb-gateway", credentials.getCredentialsId())); + assertThat(dockerMqttsCommands.get(WINDOWS).asText()).isEqualTo("docker run -it -v %HOMEPATH%/tb-gateway/logs:/thingsboard_gateway/logs -v %HOMEPATH%/tb-gateway/extensions:/thingsboard_gateway/extensions -v %HOMEPATH%/tb-gateway/config:/thingsboard_gateway/config --network=host -p 5000:5000 --name tbGatewayLocalhost -e host=localhost -e port=8883 -e accessToken=" + credentials.getCredentialsId() + " --restart always thingsboard/tb-gateway"); + } + + @Test + public void testFetchPublishTelemetryCommandsForDeviceWithIpV6LocalhostAddress() throws Exception { + loginSysAdmin(); + setConnectivityHost("::1"); + loginTenantAdmin(); + + Device device = new Device(); + device.setName("My device"); + device.setType("default"); + Device savedDevice = doPost("/api/device", device, Device.class); + DeviceCredentials credentials = + doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class); + + JsonNode commands = + doGetTyped("/api/device-connectivity/" + savedDevice.getId().getId(), new TypeReference<>() { + }); + + assertThat(commands).hasSize(3); + JsonNode httpCommands = commands.get(HTTP); + assertThat(httpCommands.get(HTTP).asText()).isEqualTo(String.format("curl -v -X POST http://localhost:8080/api/v1/%s/telemetry " + + "--header Content-Type:application/json --data \"{temperature:25}\"", + credentials.getCredentialsId())); + assertThat(httpCommands.get(HTTPS).asText()).isEqualTo(String.format("curl -v -X POST https://localhost/api/v1/%s/telemetry " + + "--header Content-Type:application/json --data \"{temperature:25}\"", + credentials.getCredentialsId())); + + JsonNode mqttCommands = commands.get(MQTT); + assertThat(mqttCommands.get(MQTT).asText()).isEqualTo(String.format("mosquitto_pub -d -q 1 -h localhost -p 1883 -t v1/devices/me/telemetry " + + "-u \"%s\" -m \"{temperature:25}\"", credentials.getCredentialsId())); + assertThat(mqttCommands.get(MQTTS).get(0).asText()).isEqualTo("curl -f -S -o ca-root.pem http://localhost:80/api/device-connectivity/mqtts/certificate/download"); + assertThat(mqttCommands.get(MQTTS).get(1).asText()).isEqualTo(String.format("mosquitto_pub -d -q 1 --cafile ca-root.pem -h localhost -p 8883 " + + "-t v1/devices/me/telemetry -u \"%s\" -m \"{temperature:25}\"", credentials.getCredentialsId())); + + JsonNode dockerMqttCommands = commands.get(MQTT).get(DOCKER); + assertThat(dockerMqttCommands.get(MQTT).asText()).isEqualTo(String.format("docker run --rm -it --network=host thingsboard/mosquitto-clients mosquitto_pub -d -q 1 -h localhost" + + " -p 1883 -t v1/devices/me/telemetry -u \"%s\" -m \"{temperature:25}\"", credentials.getCredentialsId())); + assertThat(dockerMqttCommands.get(MQTTS).asText()).isEqualTo(String.format("docker run --rm -it --network=host thingsboard/mosquitto-clients " + + "/bin/sh -c \"curl -f -S -o ca-root.pem http://localhost:80/api/device-connectivity/mqtts/certificate/download && " + + "mosquitto_pub -d -q 1 --cafile ca-root.pem -h localhost -p 8883 -t v1/devices/me/telemetry -u \"%s\" -m \"{temperature:25}\"\"", + credentials.getCredentialsId())); + + JsonNode linuxCoapCommands = commands.get(COAP); + assertThat(linuxCoapCommands.get(COAP).asText()).isEqualTo(String.format("coap-client -v 6 -m POST coap://localhost:5683/api/v1/%s/telemetry " + + "-t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + assertThat(linuxCoapCommands.get(COAPS).asText()).isEqualTo(String.format("coap-client-openssl -v 6 -m POST coaps://localhost:5684/api/v1/%s/telemetry" + + " -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + + JsonNode dockerCoapCommands = commands.get(COAP).get(DOCKER); + assertThat(dockerCoapCommands.get(COAP).asText()).isEqualTo(String.format("docker run --rm -it --network=host" + + " thingsboard/coap-clients coap-client -v 6 -m POST coap://localhost:5683/api/v1/%s/telemetry -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + assertThat(dockerCoapCommands.get(COAPS).asText()).isEqualTo(String.format("docker run --rm -it --network=host" + + " thingsboard/coap-clients coap-client-openssl -v 6 -m POST coaps://localhost:5684/api/v1/%s/telemetry -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + } + + @Test + public void testFetchPublishTelemetryCommandsForDeviceWithIpV6Address() throws Exception { + + loginSysAdmin(); + setConnectivityHost("1:1:1:1:1:1:1:1"); + loginTenantAdmin(); + + Device device = new Device(); + device.setName("My device"); + device.setType("default"); + Device savedDevice = doPost("/api/device", device, Device.class); + DeviceCredentials credentials = + doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class); + + JsonNode commands = + doGetTyped("/api/device-connectivity/" + savedDevice.getId().getId(), new TypeReference<>() { + }); + assertThat(commands).hasSize(3); + JsonNode httpCommands = commands.get(HTTP); + assertThat(httpCommands.get(HTTP).asText()).isEqualTo(String.format("curl -v -X POST http://[1:1:1:1:1:1:1:1]:8080/api/v1/%s/telemetry " + + "--header Content-Type:application/json --data \"{temperature:25}\"", + credentials.getCredentialsId())); + assertThat(httpCommands.get(HTTPS).asText()).isEqualTo(String.format("curl -v -X POST https://[1:1:1:1:1:1:1:1]/api/v1/%s/telemetry " + + "--header Content-Type:application/json --data \"{temperature:25}\"", + credentials.getCredentialsId())); + + JsonNode mqttCommands = commands.get(MQTT); + assertThat(mqttCommands.get(MQTT).asText()).isEqualTo(String.format("mosquitto_pub -d -q 1 -h 1:1:1:1:1:1:1:1 -p 1883 -t v1/devices/me/telemetry " + + "-u \"%s\" -m \"{temperature:25}\"", credentials.getCredentialsId())); + assertThat(mqttCommands.get(MQTTS).get(0).asText()).isEqualTo("curl -f -S -o ca-root.pem http://localhost:80/api/device-connectivity/mqtts/certificate/download"); + assertThat(mqttCommands.get(MQTTS).get(1).asText()).isEqualTo(String.format("mosquitto_pub -d -q 1 --cafile ca-root.pem -h 1:1:1:1:1:1:1:1 -p 8883 " + + "-t v1/devices/me/telemetry -u \"%s\" -m \"{temperature:25}\"", credentials.getCredentialsId())); + + JsonNode dockerMqttCommands = commands.get(MQTT).get(DOCKER); + assertThat(dockerMqttCommands.get(MQTT).asText()).isEqualTo(String.format("docker run --rm -it thingsboard/mosquitto-clients mosquitto_pub -d -q 1 -h 1:1:1:1:1:1:1:1" + + " -p 1883 -t v1/devices/me/telemetry -u \"%s\" -m \"{temperature:25}\"", credentials.getCredentialsId())); + assertThat(dockerMqttCommands.get(MQTTS).asText()).isEqualTo(String.format("docker run --rm -it thingsboard/mosquitto-clients " + + "/bin/sh -c \"curl -f -S -o ca-root.pem http://localhost:80/api/device-connectivity/mqtts/certificate/download && " + + "mosquitto_pub -d -q 1 --cafile ca-root.pem -h 1:1:1:1:1:1:1:1 -p 8883 -t v1/devices/me/telemetry -u \"%s\" -m \"{temperature:25}\"\"", + credentials.getCredentialsId())); + + JsonNode linuxCoapCommands = commands.get(COAP); + assertThat(linuxCoapCommands.get(COAP).asText()).isEqualTo(String.format("coap-client -v 6 -m POST coap://[1:1:1:1:1:1:1:1]:5683/api/v1/%s/telemetry " + + "-t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + assertThat(linuxCoapCommands.get(COAPS).asText()).isEqualTo(String.format("coap-client-openssl -v 6 -m POST coaps://[1:1:1:1:1:1:1:1]:5684/api/v1/%s/telemetry" + + " -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + + JsonNode dockerCoapCommands = commands.get(COAP).get(DOCKER); + assertThat(dockerCoapCommands.get(COAP).asText()).isEqualTo(String.format("docker run --rm -it" + + " thingsboard/coap-clients coap-client -v 6 -m POST coap://[1:1:1:1:1:1:1:1]:5683/api/v1/%s/telemetry -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + assertThat(dockerCoapCommands.get(COAPS).asText()).isEqualTo(String.format("docker run --rm -it" + + " thingsboard/coap-clients coap-client-openssl -v 6 -m POST coaps://[1:1:1:1:1:1:1:1]:5684/api/v1/%s/telemetry -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + + } + @Test public void testFetchPublishTelemetryCommandsForDeviceWithMqttBasicCreds() throws Exception { Device device = new Device(); @@ -449,47 +581,7 @@ public class DeviceConnectivityControllerTest extends AbstractControllerTest { public void testFetchPublishTelemetryCommandsForDefaultDeviceIfPortsSetToDefault() throws Exception { loginSysAdmin(); - ObjectNode config = JacksonUtil.newObjectNode(); - - ObjectNode http = JacksonUtil.newObjectNode(); - http.put("enabled", true); - http.put("host", ""); - http.put("port", 80); - config.set("http", http); - - ObjectNode https = JacksonUtil.newObjectNode(); - https.put("enabled", true); - https.put("host", ""); - https.put("port", 443); - config.set("https", https); - - ObjectNode mqtt = JacksonUtil.newObjectNode(); - mqtt.put("enabled", false); - mqtt.put("host", ""); - mqtt.put("port", 1883); - config.set("mqtt", mqtt); - - ObjectNode mqtts = JacksonUtil.newObjectNode(); - mqtts.put("enabled", false); - mqtts.put("host", ""); - mqtts.put("port", 8883); - config.set("mqtts", mqtts); - - ObjectNode coap = JacksonUtil.newObjectNode(); - coap.put("enabled", false); - coap.put("host", ""); - coap.put("port", 5683); - config.set("coap", coap); - - ObjectNode coaps = JacksonUtil.newObjectNode(); - coaps.put("enabled", false); - coaps.put("host", ""); - coaps.put("port", 5684); - config.set("coaps", coaps); - - AdminSettings adminSettings = doGet("/api/admin/settings/connectivity", AdminSettings.class); - adminSettings.setJsonValue(config); - doPost("/api/admin/settings", adminSettings).andExpect(status().isOk()); + setConnectivityHost(""); login("tenant2@thingsboard.org", "testPassword1"); @@ -518,47 +610,7 @@ public class DeviceConnectivityControllerTest extends AbstractControllerTest { public void testFetchPublishTelemetryCommandsForDefaultDeviceIfHostIsNotLocalhost() throws Exception { loginSysAdmin(); - ObjectNode config = JacksonUtil.newObjectNode(); - - ObjectNode http = JacksonUtil.newObjectNode(); - http.put("enabled", true); - http.put("host", "test.domain"); - http.put("port", 8080); - config.set("http", http); - - ObjectNode https = JacksonUtil.newObjectNode(); - https.put("enabled", true); - https.put("host", "test.domain"); - https.put("port", 443); - config.set("https", https); - - ObjectNode mqtt = JacksonUtil.newObjectNode(); - mqtt.put("enabled", true); - mqtt.put("host", "test.domain"); - mqtt.put("port", 1883); - config.set("mqtt", mqtt); - - ObjectNode mqtts = JacksonUtil.newObjectNode(); - mqtts.put("enabled", true); - mqtts.put("host", "test.domain"); - mqtts.put("port", 8883); - config.set("mqtts", mqtts); - - ObjectNode coap = JacksonUtil.newObjectNode(); - coap.put("enabled", true); - coap.put("host", "test.domain"); - coap.put("port", 5683); - config.set("coap", coap); - - ObjectNode coaps = JacksonUtil.newObjectNode(); - coaps.put("enabled", true); - coaps.put("host", "test.domain"); - coaps.put("port", 5684); - config.set("coaps", coaps); - - AdminSettings adminSettings = doGet("/api/admin/settings/connectivity", AdminSettings.class); - adminSettings.setJsonValue(config); - doPost("/api/admin/settings", adminSettings).andExpect(status().isOk()); + setConnectivityHost("test.domain"); login("tenant2@thingsboard.org", "testPassword1"); @@ -612,4 +664,49 @@ public class DeviceConnectivityControllerTest extends AbstractControllerTest { assertThat(dockerCoapCommands.get(COAPS).asText()).isEqualTo(String.format("docker run --rm -it " + "thingsboard/coap-clients coap-client-openssl -v 6 -m POST coaps://test.domain:5684/api/v1/%s/telemetry -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); } + + + private void setConnectivityHost(String host) throws Exception { + ObjectNode config = JacksonUtil.newObjectNode(); + + ObjectNode http = JacksonUtil.newObjectNode(); + http.put("enabled", true); + http.put("host", host); + http.put("port", 8080); + config.set("http", http); + + ObjectNode https = JacksonUtil.newObjectNode(); + https.put("enabled", true); + https.put("host", host); + https.put("port", 443); + config.set("https", https); + + ObjectNode mqtt = JacksonUtil.newObjectNode(); + mqtt.put("enabled", true); + mqtt.put("host", host); + mqtt.put("port", 1883); + config.set("mqtt", mqtt); + + ObjectNode mqtts = JacksonUtil.newObjectNode(); + mqtts.put("enabled", true); + mqtts.put("host", host); + mqtts.put("port", 8883); + config.set("mqtts", mqtts); + + ObjectNode coap = JacksonUtil.newObjectNode(); + coap.put("enabled", true); + coap.put("host", host); + coap.put("port", 5683); + config.set("coap", coap); + + ObjectNode coaps = JacksonUtil.newObjectNode(); + coaps.put("enabled", true); + coaps.put("host", host); + coaps.put("port", 5684); + config.set("coaps", coaps); + + AdminSettings adminSettings = doGet("/api/admin/settings/connectivity", AdminSettings.class); + adminSettings.setJsonValue(config); + doPost("/api/admin/settings", adminSettings).andExpect(status().isOk()); + } } 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 9f67093529..58f2f352d7 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 @@ -43,7 +43,6 @@ import org.thingsboard.server.dao.util.DeviceConnectivityUtil; import java.io.InputStream; import java.io.InputStreamReader; -import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -64,6 +63,7 @@ import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.LINUX; import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.MQTT; import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.MQTTS; import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.WINDOWS; +import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.getHost; @Service("DeviceConnectivityDaoService") @Slf4j @@ -230,7 +230,7 @@ public class DeviceConnectivityServiceImpl implements DeviceConnectivityService deviceCredentials.getCredentialsType() != DeviceCredentialsType.ACCESS_TOKEN) { return null; } - String hostName = getHost(baseUrl, properties); + String hostName = getHost(baseUrl, properties, protocol); String propertiesPort = properties.getPort(); String port = (propertiesPort.isEmpty() || HTTP_DEFAULT_PORT.equals(propertiesPort) || HTTPS_DEFAULT_PORT.equals(propertiesPort)) ? "" : ":" + propertiesPort; @@ -278,14 +278,14 @@ public class DeviceConnectivityServiceImpl implements DeviceConnectivityService private String getMqttPublishCommand(String baseUrl, String deviceTelemetryTopic, DeviceCredentials deviceCredentials) throws URISyntaxException { DeviceConnectivityInfo properties = getConnectivity(MQTT); - String mqttHost = getHost(baseUrl, properties); + String mqttHost = getHost(baseUrl, properties, MQTT); String mqttPort = properties.getPort().isEmpty() ? null : properties.getPort(); return DeviceConnectivityUtil.getMqttPublishCommand(MQTT, mqttHost, mqttPort, deviceTelemetryTopic, deviceCredentials); } private List getMqttsPublishCommand(String baseUrl, String deviceTelemetryTopic, DeviceCredentials deviceCredentials) throws URISyntaxException { DeviceConnectivityInfo properties = getConnectivity(MQTTS); - String mqttHost = getHost(baseUrl, properties); + String mqttHost = getHost(baseUrl, properties, MQTTS); String mqttPort = properties.getPort().isEmpty() ? null : properties.getPort(); String pubCommand = DeviceConnectivityUtil.getMqttPublishCommand(MQTTS, mqttHost, mqttPort, deviceTelemetryTopic, deviceCredentials); @@ -301,7 +301,7 @@ public class DeviceConnectivityServiceImpl implements DeviceConnectivityService private JsonNode getGatewayDockerCommands(String baseUrl, DeviceCredentials deviceCredentials, String mqttType) throws URISyntaxException { ObjectNode dockerLaunchCommands = JacksonUtil.newObjectNode(); DeviceConnectivityInfo properties = getConnectivity(mqttType); - String mqttHost = getHost(baseUrl, properties); + String mqttHost = getHost(baseUrl, properties, mqttType); String mqttPort = properties.getPort().isEmpty() ? null : properties.getPort(); Optional.ofNullable(DeviceConnectivityUtil.getGatewayLaunchCommand(LINUX, mqttHost, mqttPort, deviceCredentials)) .ifPresent(v -> dockerLaunchCommands.put(LINUX, v)); @@ -312,7 +312,7 @@ public class DeviceConnectivityServiceImpl implements DeviceConnectivityService private String getDockerMqttPublishCommand(String protocol, String baseUrl, String deviceTelemetryTopic, DeviceCredentials deviceCredentials) throws URISyntaxException { DeviceConnectivityInfo properties = getConnectivity(protocol); - String mqttHost = getHost(baseUrl, properties); + String mqttHost = getHost(baseUrl, properties, protocol); String mqttPort = properties.getPort().isEmpty() ? null : properties.getPort(); return DeviceConnectivityUtil.getDockerMqttPublishCommand(protocol, baseUrl, mqttHost, mqttPort, deviceTelemetryTopic, deviceCredentials); } @@ -352,20 +352,16 @@ public class DeviceConnectivityServiceImpl implements DeviceConnectivityService private String getCoapPublishCommand(String protocol, String baseUrl, DeviceCredentials deviceCredentials) throws URISyntaxException { DeviceConnectivityInfo properties = getConnectivity(protocol); - String hostName = getHost(baseUrl, properties); + String hostName = getHost(baseUrl, properties, protocol); String port = properties.getPort().isEmpty() ? "" : ":" + properties.getPort(); return DeviceConnectivityUtil.getCoapPublishCommand(protocol, hostName, port, deviceCredentials); } private String getDockerCoapPublishCommand(String protocol, String baseUrl, DeviceCredentials deviceCredentials) throws URISyntaxException { DeviceConnectivityInfo properties = getConnectivity(protocol); - String host = getHost(baseUrl, properties); + String host = getHost(baseUrl, properties, protocol); String port = properties.getPort().isEmpty() ? "" : ":" + properties.getPort(); return DeviceConnectivityUtil.getDockerCoapPublishCommand(protocol, host, port, deviceCredentials); } - private String getHost(String baseUrl, DeviceConnectivityInfo properties) throws URISyntaxException { - return properties.getHost().isEmpty() ? new URI(baseUrl).getHost() : properties.getHost(); - } - } diff --git a/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java b/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java index aa446c12b5..f905a9d288 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java +++ b/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java @@ -19,9 +19,14 @@ import org.apache.commons.lang3.StringUtils; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials; import org.thingsboard.server.common.data.security.DeviceCredentials; +import org.thingsboard.server.dao.device.DeviceConnectivityInfo; -import java.util.Arrays; -import java.util.List; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.UnknownHostException; +import java.util.regex.Pattern; public class DeviceConnectivityUtil { @@ -41,7 +46,7 @@ public class DeviceConnectivityUtil { public static final String GATEWAY_DOCKER_RUN = "docker run -it "; public static final String MQTT_IMAGE = "thingsboard/mosquitto-clients "; public static final String COAP_IMAGE = "thingsboard/coap-clients "; - public static final List LOCAL_HOSTS = Arrays.asList("localhost", "127.0.0.1"); + private final static Pattern VALID_URL_PATTERN = Pattern.compile("^(http|https|ftp)://.*$"); public static String getHttpPublishCommand(String protocol, String host, String port, DeviceCredentials deviceCredentials) { return String.format("curl -v -X POST %s://%s%s/api/v1/%s/telemetry --header Content-Type:application/json --data " + JSON_EXAMPLE_PAYLOAD, @@ -71,7 +76,7 @@ public class DeviceConnectivityUtil { command.append(" -u \"").append(credentials.getUserName()).append("\""); } if (credentials.getPassword() != null) { - command.append(" -P \"").append(credentials.getPassword()).append("\"");; + command.append(" -P \"").append(credentials.getPassword()).append("\""); } } else { return null; @@ -90,17 +95,19 @@ public class DeviceConnectivityUtil { gatewayVolumePathPrefix = "%HOMEPATH%/tb-gateway"; } - String gatewayContainerName = "tbGateway" + StringUtils.capitalize(host.replace(".", "")); + String gatewayContainerName = "tbGateway" + StringUtils.capitalize(host.replaceAll("[^A-Za-z0-9]", "")); StringBuilder command = new StringBuilder(GATEWAY_DOCKER_RUN); command.append("-v {gatewayVolumePathPrefix}/logs:/thingsboard_gateway/logs".replace("{gatewayVolumePathPrefix}", gatewayVolumePathPrefix)); command.append(" -v {gatewayVolumePathPrefix}/extensions:/thingsboard_gateway/extensions".replace("{gatewayVolumePathPrefix}", gatewayVolumePathPrefix)); command.append(" -v {gatewayVolumePathPrefix}/config:/thingsboard_gateway/config".replace("{gatewayVolumePathPrefix}", gatewayVolumePathPrefix)); + command.append(isLocalhost(host) ? " --network=host" : ""); + command.append(" -p 5000:5000"); command.append(" --name ").append(gatewayContainerName); command.append(" -e host=").append(host); command.append(" -e port=").append(port); - switch(deviceCredentials.getCredentialsType()) { + switch (deviceCredentials.getCredentialsType()) { case ACCESS_TOKEN: command.append(" -e accessToken=").append(deviceCredentials.getCredentialsId()); break; @@ -139,7 +146,7 @@ public class DeviceConnectivityUtil { } StringBuilder mqttDockerCommand = new StringBuilder(); - mqttDockerCommand.append(DOCKER_RUN).append(LOCAL_HOSTS.contains(host) ? "--network=host ":"").append(MQTT_IMAGE); + mqttDockerCommand.append(DOCKER_RUN).append(isLocalhost(host) ? "--network=host " : "").append(MQTT_IMAGE); if (MQTTS.equals(protocol)) { mqttDockerCommand.append("/bin/sh -c \"") @@ -171,6 +178,33 @@ public class DeviceConnectivityUtil { public static String getDockerCoapPublishCommand(String protocol, String host, String port, DeviceCredentials deviceCredentials) { String coapCommand = getCoapPublishCommand(protocol, host, port, deviceCredentials); - return coapCommand != null ? String.format("%s%s%s", DOCKER_RUN + (LOCAL_HOSTS.contains(host) ? "--network=host ":""), COAP_IMAGE, coapCommand) : null; + return coapCommand != null ? String.format("%s%s%s", DOCKER_RUN + (isLocalhost(host) ? "--network=host " : ""), COAP_IMAGE, coapCommand) : null; + } + + public static String getHost(String baseUrl, DeviceConnectivityInfo properties, String protocol) throws URISyntaxException { + String host = properties.getHost().isEmpty() ? baseUrl : properties.getHost(); + if (VALID_URL_PATTERN.matcher(host).matches()) { + host = new URI(host).getHost(); + } else { + try { + InetAddress inetAddress = InetAddress.getByName(host); + host = inetAddress.getCanonicalHostName().replaceAll("[\\[\\]]", ""); + if (inetAddress instanceof Inet6Address && !inetAddress.isLoopbackAddress() && !protocol.startsWith(MQTT)) { + host = "[" + host + "]"; + } + } catch (UnknownHostException e) { + return host; + } + } + return host; + } + + private static boolean isLocalhost(String host) { + try { + InetAddress inetAddress = InetAddress.getByName(host); + return inetAddress.isLoopbackAddress(); + } catch (UnknownHostException e) { + return false; + } } } From b6b501c3a3507906f2de27239f72bf020ed3cb26 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Fri, 17 Nov 2023 18:53:40 +0200 Subject: [PATCH 165/252] UI: Fix scroll grid. --- .../home/components/grid/scroll-grid.component.html | 4 ++-- .../modules/home/components/grid/scroll-grid.component.ts | 8 ++++++++ ui-ngx/src/app/shared/models/resource.models.ts | 4 +++- ui-ngx/src/app/shared/pipe/image.pipe.ts | 6 ++++-- 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/grid/scroll-grid.component.html b/ui-ngx/src/app/modules/home/components/grid/scroll-grid.component.html index 5340293a12..849dc2e6a9 100644 --- a/ui-ngx/src/app/modules/home/components/grid/scroll-grid.component.html +++ b/ui-ngx/src/app/modules/home/components/grid/scroll-grid.component.html @@ -16,9 +16,9 @@ --> - +
-
+
diff --git a/ui-ngx/src/app/modules/home/components/grid/scroll-grid.component.ts b/ui-ngx/src/app/modules/home/components/grid/scroll-grid.component.ts index e0eea202ff..7044fc040e 100644 --- a/ui-ngx/src/app/modules/home/components/grid/scroll-grid.component.ts +++ b/ui-ngx/src/app/modules/home/components/grid/scroll-grid.component.ts @@ -100,4 +100,12 @@ export class ScrollGridComponent implements OnInit, AfterViewInit, OnChang isObject(value: any): boolean { return isObject(value); } + + trackByItemsRow(index: number, itemsRow: T[]): number { + return index; + } + + trackByItem(index: number, item: T): T { + return item; + } } diff --git a/ui-ngx/src/app/shared/models/resource.models.ts b/ui-ngx/src/app/shared/models/resource.models.ts index e494e3842e..02ae41d481 100644 --- a/ui-ngx/src/app/shared/models/resource.models.ts +++ b/ui-ngx/src/app/shared/models/resource.models.ts @@ -82,7 +82,9 @@ export interface ImageDescriptor { previewDescriptor: ImageDescriptor; } -export type ImageResourceInfo = TbResourceInfo; +export interface ImageResourceInfo extends TbResourceInfo { + link?: string; +} export type ImageResourceType = 'tenant' | 'system'; diff --git a/ui-ngx/src/app/shared/pipe/image.pipe.ts b/ui-ngx/src/app/shared/pipe/image.pipe.ts index fb54cb6985..24f91c500d 100644 --- a/ui-ngx/src/app/shared/pipe/image.pipe.ts +++ b/ui-ngx/src/app/shared/pipe/image.pipe.ts @@ -17,11 +17,13 @@ import { Pipe, PipeTransform } from '@angular/core'; import { ImageService } from '@core/http/image.service'; import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; -import { BehaviorSubject, Observable, of, Subject } from 'rxjs'; +import { BehaviorSubject, Observable, Subject } from 'rxjs'; import { isDefinedAndNotNull } from '@core/utils'; import { NO_IMAGE_DATA_URI } from '@shared/models/resource.models'; -const LOADING_IMAGE_DATA_URI = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZlcnNpb249IjEuMSIgdmlld0JveD0iMCAwIDIwIDIwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPgo='; +const LOADING_IMAGE_DATA_URI = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRG' + + 'LTgiPz4KPHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZlcnNpb249IjEuMSIgdmlld0JveD0iMCAw' + + 'IDIwIDIwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPgo='; @Pipe({ name: 'image' From 9a14260bf1d42d83882d452606a2e6e86263bf29 Mon Sep 17 00:00:00 2001 From: imbeacon Date: Fri, 17 Nov 2023 18:54:01 +0200 Subject: [PATCH 166/252] Host check refactoring --- .../DeviceConnectivityControllerTest.java | 42 ++++++++++++++++++- .../dao/util/DeviceConnectivityUtil.java | 21 ++++++---- 2 files changed, 53 insertions(+), 10 deletions(-) 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 b629408855..4ca59dc843 100644 --- a/application/src/test/java/org/thingsboard/server/controller/DeviceConnectivityControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/DeviceConnectivityControllerTest.java @@ -581,7 +581,47 @@ public class DeviceConnectivityControllerTest extends AbstractControllerTest { public void testFetchPublishTelemetryCommandsForDefaultDeviceIfPortsSetToDefault() throws Exception { loginSysAdmin(); - setConnectivityHost(""); + ObjectNode config = JacksonUtil.newObjectNode(); + + ObjectNode http = JacksonUtil.newObjectNode(); + http.put("enabled", true); + http.put("host", ""); + http.put("port", 80); + config.set("http", http); + + ObjectNode https = JacksonUtil.newObjectNode(); + https.put("enabled", true); + https.put("host", ""); + https.put("port", 443); + config.set("https", https); + + ObjectNode mqtt = JacksonUtil.newObjectNode(); + mqtt.put("enabled", false); + mqtt.put("host", ""); + mqtt.put("port", 1883); + config.set("mqtt", mqtt); + + ObjectNode mqtts = JacksonUtil.newObjectNode(); + mqtts.put("enabled", false); + mqtts.put("host", ""); + mqtts.put("port", 8883); + config.set("mqtts", mqtts); + + ObjectNode coap = JacksonUtil.newObjectNode(); + coap.put("enabled", false); + coap.put("host", ""); + coap.put("port", 5683); + config.set("coap", coap); + + ObjectNode coaps = JacksonUtil.newObjectNode(); + coaps.put("enabled", false); + coaps.put("host", ""); + coaps.put("port", 5684); + config.set("coaps", coaps); + + AdminSettings adminSettings = doGet("/api/admin/settings/connectivity", AdminSettings.class); + adminSettings.setJsonValue(config); + doPost("/api/admin/settings", adminSettings).andExpect(status().isOk()); login("tenant2@thingsboard.org", "testPassword1"); diff --git a/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java b/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java index f905a9d288..d1eb434100 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java +++ b/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java @@ -183,17 +183,20 @@ public class DeviceConnectivityUtil { public static String getHost(String baseUrl, DeviceConnectivityInfo properties, String protocol) throws URISyntaxException { String host = properties.getHost().isEmpty() ? baseUrl : properties.getHost(); + InetAddress inetAddress; if (VALID_URL_PATTERN.matcher(host).matches()) { host = new URI(host).getHost(); - } else { - try { - InetAddress inetAddress = InetAddress.getByName(host); - host = inetAddress.getCanonicalHostName().replaceAll("[\\[\\]]", ""); - if (inetAddress instanceof Inet6Address && !inetAddress.isLoopbackAddress() && !protocol.startsWith(MQTT)) { - host = "[" + host + "]"; - } - } catch (UnknownHostException e) { - return host; + } + try { + inetAddress = InetAddress.getByName(host); + host = inetAddress.getHostName(); + } catch (UnknownHostException e) { + return host; + } + if (inetAddress instanceof Inet6Address) { + host = host.replaceAll("[\\[\\]]", ""); + if (!inetAddress.isLoopbackAddress() && !protocol.startsWith(MQTT)) { + host = "[" + host + "]"; } } return host; From bb8af6cb220090f691d81964fc983bd190320a85 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Fri, 17 Nov 2023 19:05:14 +0200 Subject: [PATCH 167/252] Local ETag cache --- .../server/controller/ImageController.java | 49 +++++++++++-------- .../resource/DefaultTbImageService.java | 29 ++++++++++- .../service/resource/ImageCacheKey.java | 13 +++++ .../service/resource/TbImageService.java | 3 ++ .../src/main/resources/thingsboard.yml | 3 ++ 5 files changed, 74 insertions(+), 23 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/resource/ImageCacheKey.java diff --git a/application/src/main/java/org/thingsboard/server/controller/ImageController.java b/application/src/main/java/org/thingsboard/server/controller/ImageController.java index 98e7a3a4e8..2ae358ff15 100644 --- a/application/src/main/java/org/thingsboard/server/controller/ImageController.java +++ b/application/src/main/java/org/thingsboard/server/controller/ImageController.java @@ -5,7 +5,7 @@ * 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 + * 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, @@ -15,6 +15,7 @@ */ package org.thingsboard.server.controller; +import com.fasterxml.jackson.core.JsonProcessingException; import io.swagger.annotations.ApiParam; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -37,6 +38,7 @@ import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import org.thingsboard.server.common.data.ImageDescriptor; import org.thingsboard.server.common.data.ResourceType; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.TbResourceInfo; import org.thingsboard.server.common.data.exception.ThingsboardException; @@ -46,11 +48,13 @@ import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.dao.resource.ImageService; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.resource.ImageCacheKey; import org.thingsboard.server.service.resource.TbImageService; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; +import java.time.Duration; import java.util.function.Supplier; import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION; @@ -122,11 +126,7 @@ public class ImageController extends BaseController { public ResponseEntity downloadImage(@PathVariable String type, @PathVariable String key, @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws Exception { - TenantId tenantId = getTenantId(); - TbResourceInfo imageInfo = checkImageInfo(type, key, Operation.READ); - ImageDescriptor descriptor = imageInfo.getDescriptor(ImageDescriptor.class); - return downloadIfChanged(etag, descriptor, imageInfo.getFileName(), - () -> imageService.getImageData(tenantId, imageInfo.getId())); + return downloadIfChanged(type, key, etag, false); } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @@ -134,11 +134,7 @@ public class ImageController extends BaseController { public ResponseEntity downloadImagePreview(@PathVariable String type, @PathVariable String key, @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws Exception { - TenantId tenantId = getTenantId(); - TbResourceInfo imageInfo = checkImageInfo(type, key, Operation.READ); - ImageDescriptor descriptor = imageInfo.getDescriptor(ImageDescriptor.class); - return downloadIfChanged(etag, descriptor.getPreviewDescriptor(), imageInfo.getFileName(), - () -> imageService.getImagePreview(tenantId, imageInfo.getId())); + return downloadIfChanged(type, key, etag, true); } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @@ -178,22 +174,33 @@ public class ImageController extends BaseController { tbImageService.delete(imageInfo, getCurrentUser()); } - private ResponseEntity downloadIfChanged(String actualEtag, ImageDescriptor imageDescriptor, - String fileName, Supplier dataSupplier) { - if (imageDescriptor.getEtag().equals(actualEtag)) { - return ResponseEntity.status(HttpStatus.NOT_MODIFIED) - .eTag(actualEtag) - .build(); + private ResponseEntity downloadIfChanged(String type, String key, String etag, boolean preview) throws ThingsboardException, JsonProcessingException { + ImageCacheKey cacheKey = new ImageCacheKey(getTenantId(type), key, preview); + if (StringUtils.isNotEmpty(etag)) { + etag = etag.replaceAll("\"", ""); // TODO: investigate why Spring provides extra quotes. + if (etag.equals(tbImageService.getETag(cacheKey))) { + return ResponseEntity.status(HttpStatus.NOT_MODIFIED).build(); + } } - - byte[] data = dataSupplier.get(); + TenantId tenantId = getTenantId(); + TbResourceInfo imageInfo = checkImageInfo(type, key, Operation.READ); + String fileName = imageInfo.getFileName(); + ImageDescriptor descriptor = imageInfo.getDescriptor(ImageDescriptor.class); + byte[] data; + if (preview) { + descriptor = descriptor.getPreviewDescriptor(); + data = imageService.getImagePreview(tenantId, imageInfo.getId()); + } else { + data = imageService.getImageData(tenantId, imageInfo.getId()); + } + tbImageService.putETag(cacheKey, descriptor.getEtag()); return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + fileName) .header("x-filename", fileName) .contentLength(data.length) - .header("Content-Type", imageDescriptor.getMediaType()) + .header("Content-Type", descriptor.getMediaType()) .cacheControl(CacheControl.noCache()) - .eTag(imageDescriptor.getEtag()) + .eTag(descriptor.getEtag()) .body(new ByteArrayResource(data)); } diff --git a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java index 65304c8270..5ded1d7fb5 100644 --- a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java +++ b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java @@ -5,7 +5,7 @@ * 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 + * 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, @@ -15,7 +15,10 @@ */ package org.thingsboard.server.service.resource; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.TbResource; @@ -28,12 +31,34 @@ import org.thingsboard.server.dao.resource.ImageService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.entitiy.AbstractTbEntityService; +import java.util.concurrent.TimeUnit; + @Service @TbCoreComponent -@RequiredArgsConstructor public class DefaultTbImageService extends AbstractTbEntityService implements TbImageService { private final ImageService imageService; + private final Cache cache; + + public DefaultTbImageService(ImageService imageService, + @Value("${cache.imageETags.timeToLiveInMinutes:120}") int cacheTtl, + @Value("${cache.imageETags.maxSize:200000}") int cacheMaxSize) { + this.imageService = imageService; + this.cache = Caffeine.newBuilder() + .expireAfterAccess(cacheTtl, TimeUnit.MINUTES) + .maximumSize(cacheMaxSize) + .build(); + } + + @Override + public String getETag(ImageCacheKey imageCacheKey) { + return cache.getIfPresent(imageCacheKey); + } + + @Override + public void putETag(ImageCacheKey imageCacheKey, String etag) { + cache.put(imageCacheKey, etag); + } @Override public TbResourceInfo save(TbResource image, User user) throws Exception { diff --git a/application/src/main/java/org/thingsboard/server/service/resource/ImageCacheKey.java b/application/src/main/java/org/thingsboard/server/service/resource/ImageCacheKey.java new file mode 100644 index 0000000000..222fc5e875 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/resource/ImageCacheKey.java @@ -0,0 +1,13 @@ +package org.thingsboard.server.service.resource; + +import lombok.Data; +import org.thingsboard.server.common.data.id.TenantId; + +@Data +public class ImageCacheKey { + + private final TenantId tenantId; + private final String key; + private final boolean preview; + +} diff --git a/application/src/main/java/org/thingsboard/server/service/resource/TbImageService.java b/application/src/main/java/org/thingsboard/server/service/resource/TbImageService.java index a1f6cf3e7e..3db512528a 100644 --- a/application/src/main/java/org/thingsboard/server/service/resource/TbImageService.java +++ b/application/src/main/java/org/thingsboard/server/service/resource/TbImageService.java @@ -27,4 +27,7 @@ public interface TbImageService { void delete(TbResourceInfo imageInfo, User user); + String getETag(ImageCacheKey imageCacheKey); + + void putETag(ImageCacheKey imageCacheKey, String etag); } diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index ecc89c84c5..9e59db76e8 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -586,6 +586,9 @@ cache: entityLimits: timeToLiveInMinutes: "${CACHE_SPECS_ENTITY_LIMITS_TTL:5}" # Entity limits cache TTL maxSize: "${CACHE_SPECS_ENTITY_LIMITS_MAX_SIZE:100000}" # 0 means the cache is disabled + imageETags: + timeToLiveInMinutes: "${CACHE_SPECS_IMAGE_ETAGS_TTL:44640}" # Image ETags cache TTL + maxSize: "${CACHE_SPECS_IMAGE_ETAGS_MAX_SIZE:1000000}" # 0 means the cache is disabled # Spring data parameters spring.data.redis.repositories.enabled: false # Disable this because it is not required. From 0051abee3446a8264ab0a2217fa9cc30b1e081cd Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Fri, 17 Nov 2023 19:06:14 +0200 Subject: [PATCH 168/252] Local ETag cache --- .../server/controller/ImageController.java | 2 +- .../service/resource/DefaultTbImageService.java | 2 +- .../server/service/resource/ImageCacheKey.java | 15 +++++++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/ImageController.java b/application/src/main/java/org/thingsboard/server/controller/ImageController.java index 2ae358ff15..a027e77b2e 100644 --- a/application/src/main/java/org/thingsboard/server/controller/ImageController.java +++ b/application/src/main/java/org/thingsboard/server/controller/ImageController.java @@ -5,7 +5,7 @@ * 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 + * 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, diff --git a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java index 5ded1d7fb5..4b70814e26 100644 --- a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java +++ b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java @@ -5,7 +5,7 @@ * 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 + * 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, diff --git a/application/src/main/java/org/thingsboard/server/service/resource/ImageCacheKey.java b/application/src/main/java/org/thingsboard/server/service/resource/ImageCacheKey.java index 222fc5e875..55ddb25dec 100644 --- a/application/src/main/java/org/thingsboard/server/service/resource/ImageCacheKey.java +++ b/application/src/main/java/org/thingsboard/server/service/resource/ImageCacheKey.java @@ -1,3 +1,18 @@ +/** + * Copyright © 2016-2023 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. + */ package org.thingsboard.server.service.resource; import lombok.Data; From 754399edf193e87b8de4a84a355fa793d5d15e65 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Fri, 17 Nov 2023 19:10:03 +0200 Subject: [PATCH 169/252] UI: Fix widgets bundle widgets row height. --- .../home/pages/widget/widgets-bundle-widgets.component.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/pages/widget/widgets-bundle-widgets.component.scss b/ui-ngx/src/app/modules/home/pages/widget/widgets-bundle-widgets.component.scss index 3c971105ff..11c611c739 100644 --- a/ui-ngx/src/app/modules/home/pages/widget/widgets-bundle-widgets.component.scss +++ b/ui-ngx/src/app/modules/home/pages/widget/widgets-bundle-widgets.component.scss @@ -154,7 +154,8 @@ gap: 12px; .tb-bundle-widget-image-preview { width: 64px; - max-height: 100%; + max-height: 52px; + height: 100%; object-fit: contain; border-radius: 6px; } From d196e69b23be90617c78826cdddb6fa2642484f1 Mon Sep 17 00:00:00 2001 From: imbeacon Date: Mon, 20 Nov 2023 09:11:52 +0200 Subject: [PATCH 170/252] Updated regex to check url --- .../org/thingsboard/server/dao/util/DeviceConnectivityUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java b/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java index d1eb434100..151932af1a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java +++ b/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java @@ -46,7 +46,7 @@ public class DeviceConnectivityUtil { public static final String GATEWAY_DOCKER_RUN = "docker run -it "; public static final String MQTT_IMAGE = "thingsboard/mosquitto-clients "; public static final String COAP_IMAGE = "thingsboard/coap-clients "; - private final static Pattern VALID_URL_PATTERN = Pattern.compile("^(http|https|ftp)://.*$"); + private final static Pattern VALID_URL_PATTERN = Pattern.compile("^(https?)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]"); public static String getHttpPublishCommand(String protocol, String host, String port, DeviceCredentials deviceCredentials) { return String.format("curl -v -X POST %s://%s%s/api/v1/%s/telemetry --header Content-Type:application/json --data " + JSON_EXAMPLE_PAYLOAD, From 8777f1297240470aa238ce3231c88fc25212f860 Mon Sep 17 00:00:00 2001 From: imbeacon Date: Mon, 20 Nov 2023 09:47:25 +0200 Subject: [PATCH 171/252] Added null safety if user set a bad ipv6 address --- .../server/dao/util/DeviceConnectivityUtil.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java b/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java index 151932af1a..44ddb095aa 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java +++ b/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java @@ -182,12 +182,17 @@ public class DeviceConnectivityUtil { } public static String getHost(String baseUrl, DeviceConnectivityInfo properties, String protocol) throws URISyntaxException { - String host = properties.getHost().isEmpty() ? baseUrl : properties.getHost(); + String initialHost = properties.getHost().isEmpty() ? baseUrl : properties.getHost(); InetAddress inetAddress; - if (VALID_URL_PATTERN.matcher(host).matches()) { - host = new URI(host).getHost(); + String host = null; + if (VALID_URL_PATTERN.matcher(initialHost).matches()) { + host = new URI(initialHost).getHost(); + } + if (host == null) { + host = initialHost; } try { + host = host.replaceAll("^https?://", ""); inetAddress = InetAddress.getByName(host); host = inetAddress.getHostName(); } catch (UnknownHostException e) { From 5e8858d5637ad1801761c2de5998379ed3c6ef8b Mon Sep 17 00:00:00 2001 From: imbeacon Date: Mon, 20 Nov 2023 11:17:19 +0200 Subject: [PATCH 172/252] Updated host check logic and related test --- .../DeviceConnectivityControllerTest.java | 20 +++++++++---------- .../dao/util/DeviceConnectivityUtil.java | 3 +-- 2 files changed, 11 insertions(+), 12 deletions(-) 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 4ca59dc843..f517d2e9c5 100644 --- a/application/src/test/java/org/thingsboard/server/controller/DeviceConnectivityControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/DeviceConnectivityControllerTest.java @@ -336,39 +336,39 @@ public class DeviceConnectivityControllerTest extends AbstractControllerTest { assertThat(commands).hasSize(3); JsonNode httpCommands = commands.get(HTTP); - assertThat(httpCommands.get(HTTP).asText()).isEqualTo(String.format("curl -v -X POST http://localhost:8080/api/v1/%s/telemetry " + + assertThat(httpCommands.get(HTTP).asText()).isEqualTo(String.format("curl -v -X POST http://[::1]:8080/api/v1/%s/telemetry " + "--header Content-Type:application/json --data \"{temperature:25}\"", credentials.getCredentialsId())); - assertThat(httpCommands.get(HTTPS).asText()).isEqualTo(String.format("curl -v -X POST https://localhost/api/v1/%s/telemetry " + + assertThat(httpCommands.get(HTTPS).asText()).isEqualTo(String.format("curl -v -X POST https://[::1]/api/v1/%s/telemetry " + "--header Content-Type:application/json --data \"{temperature:25}\"", credentials.getCredentialsId())); JsonNode mqttCommands = commands.get(MQTT); - assertThat(mqttCommands.get(MQTT).asText()).isEqualTo(String.format("mosquitto_pub -d -q 1 -h localhost -p 1883 -t v1/devices/me/telemetry " + + assertThat(mqttCommands.get(MQTT).asText()).isEqualTo(String.format("mosquitto_pub -d -q 1 -h ::1 -p 1883 -t v1/devices/me/telemetry " + "-u \"%s\" -m \"{temperature:25}\"", credentials.getCredentialsId())); assertThat(mqttCommands.get(MQTTS).get(0).asText()).isEqualTo("curl -f -S -o ca-root.pem http://localhost:80/api/device-connectivity/mqtts/certificate/download"); - assertThat(mqttCommands.get(MQTTS).get(1).asText()).isEqualTo(String.format("mosquitto_pub -d -q 1 --cafile ca-root.pem -h localhost -p 8883 " + + assertThat(mqttCommands.get(MQTTS).get(1).asText()).isEqualTo(String.format("mosquitto_pub -d -q 1 --cafile ca-root.pem -h ::1 -p 8883 " + "-t v1/devices/me/telemetry -u \"%s\" -m \"{temperature:25}\"", credentials.getCredentialsId())); JsonNode dockerMqttCommands = commands.get(MQTT).get(DOCKER); - assertThat(dockerMqttCommands.get(MQTT).asText()).isEqualTo(String.format("docker run --rm -it --network=host thingsboard/mosquitto-clients mosquitto_pub -d -q 1 -h localhost" + + assertThat(dockerMqttCommands.get(MQTT).asText()).isEqualTo(String.format("docker run --rm -it --network=host thingsboard/mosquitto-clients mosquitto_pub -d -q 1 -h ::1" + " -p 1883 -t v1/devices/me/telemetry -u \"%s\" -m \"{temperature:25}\"", credentials.getCredentialsId())); assertThat(dockerMqttCommands.get(MQTTS).asText()).isEqualTo(String.format("docker run --rm -it --network=host thingsboard/mosquitto-clients " + "/bin/sh -c \"curl -f -S -o ca-root.pem http://localhost:80/api/device-connectivity/mqtts/certificate/download && " + - "mosquitto_pub -d -q 1 --cafile ca-root.pem -h localhost -p 8883 -t v1/devices/me/telemetry -u \"%s\" -m \"{temperature:25}\"\"", + "mosquitto_pub -d -q 1 --cafile ca-root.pem -h ::1 -p 8883 -t v1/devices/me/telemetry -u \"%s\" -m \"{temperature:25}\"\"", credentials.getCredentialsId())); JsonNode linuxCoapCommands = commands.get(COAP); - assertThat(linuxCoapCommands.get(COAP).asText()).isEqualTo(String.format("coap-client -v 6 -m POST coap://localhost:5683/api/v1/%s/telemetry " + + assertThat(linuxCoapCommands.get(COAP).asText()).isEqualTo(String.format("coap-client -v 6 -m POST coap://[::1]:5683/api/v1/%s/telemetry " + "-t json -e \"{temperature:25}\"", credentials.getCredentialsId())); - assertThat(linuxCoapCommands.get(COAPS).asText()).isEqualTo(String.format("coap-client-openssl -v 6 -m POST coaps://localhost:5684/api/v1/%s/telemetry" + + assertThat(linuxCoapCommands.get(COAPS).asText()).isEqualTo(String.format("coap-client-openssl -v 6 -m POST coaps://[::1]:5684/api/v1/%s/telemetry" + " -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); JsonNode dockerCoapCommands = commands.get(COAP).get(DOCKER); assertThat(dockerCoapCommands.get(COAP).asText()).isEqualTo(String.format("docker run --rm -it --network=host" + - " thingsboard/coap-clients coap-client -v 6 -m POST coap://localhost:5683/api/v1/%s/telemetry -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + " thingsboard/coap-clients coap-client -v 6 -m POST coap://[::1]:5683/api/v1/%s/telemetry -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); assertThat(dockerCoapCommands.get(COAPS).asText()).isEqualTo(String.format("docker run --rm -it --network=host" + - " thingsboard/coap-clients coap-client-openssl -v 6 -m POST coaps://localhost:5684/api/v1/%s/telemetry -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + " thingsboard/coap-clients coap-client-openssl -v 6 -m POST coaps://[::1]:5684/api/v1/%s/telemetry -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); } @Test diff --git a/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java b/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java index 44ddb095aa..efd75e305e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java +++ b/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java @@ -194,13 +194,12 @@ public class DeviceConnectivityUtil { try { host = host.replaceAll("^https?://", ""); inetAddress = InetAddress.getByName(host); - host = inetAddress.getHostName(); } catch (UnknownHostException e) { return host; } if (inetAddress instanceof Inet6Address) { host = host.replaceAll("[\\[\\]]", ""); - if (!inetAddress.isLoopbackAddress() && !protocol.startsWith(MQTT)) { + if (!protocol.startsWith(MQTT)) { host = "[" + host + "]"; } } From 561e825eda32a70499052e6bfccfa59d6b626a8e Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Mon, 20 Nov 2023 13:22:15 +0200 Subject: [PATCH 173/252] Minor refactoring: use edgeVersion instead of passing boolean --- .../rpc/processor/alarm/AlarmEdgeProcessor.java | 3 +-- .../rpc/processor/alarm/BaseAlarmProcessor.java | 3 ++- .../rpc/processor/asset/AssetEdgeProcessor.java | 7 +++---- .../processor/asset/AssetProfileEdgeProcessor.java | 11 +++++------ .../rpc/processor/asset/BaseAssetProcessor.java | 10 ++++++---- .../processor/asset/BaseAssetProfileProcessor.java | 14 ++++++++------ .../dashboard/BaseDashboardProcessor.java | 6 ++++-- .../dashboard/DashboardEdgeProcessor.java | 3 +-- .../rpc/processor/device/BaseDeviceProcessor.java | 10 ++++++---- .../device/BaseDeviceProfileProcessor.java | 14 ++++++++------ .../rpc/processor/device/DeviceEdgeProcessor.java | 7 +++---- .../device/DeviceProfileEdgeProcessor.java | 11 +++++------ .../entityview/BaseEntityViewProcessor.java | 10 ++++++---- .../entityview/EntityViewEdgeProcessor.java | 7 +++---- .../processor/relation/BaseRelationProcessor.java | 6 ++++-- .../processor/relation/RelationEdgeProcessor.java | 3 +-- .../processor/resource/BaseResourceProcessor.java | 6 ++++-- .../processor/resource/ResourceEdgeProcessor.java | 3 +-- .../telemetry/TelemetryEdgeProcessor.java | 3 +-- 19 files changed, 72 insertions(+), 65 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/AlarmEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/AlarmEdgeProcessor.java index a09d36f656..b934a33d54 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/AlarmEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/AlarmEdgeProcessor.java @@ -37,7 +37,6 @@ import org.thingsboard.server.gen.edge.v1.DownlinkMsg; import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; import java.util.ArrayList; import java.util.List; @@ -52,7 +51,7 @@ public class AlarmEdgeProcessor extends BaseAlarmProcessor { log.trace("[{}] processAlarmMsgFromEdge [{}]", tenantId, alarmUpdateMsg); try { edgeSynchronizationManager.getEdgeId().set(edgeId); - return processAlarmMsg(tenantId, alarmUpdateMsg, EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)); + return processAlarmMsg(tenantId, alarmUpdateMsg, edgeVersion); } finally { edgeSynchronizationManager.getEdgeId().remove(); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java index ab30d21e1f..fd53e38f4a 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java @@ -47,9 +47,10 @@ import java.util.UUID; @Slf4j public abstract class BaseAlarmProcessor extends BaseEdgeProcessor { - public ListenableFuture processAlarmMsg(TenantId tenantId, AlarmUpdateMsg alarmUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2) { + public ListenableFuture processAlarmMsg(TenantId tenantId, AlarmUpdateMsg alarmUpdateMsg, EdgeVersion edgeVersion) { log.trace("[{}] processAlarmMsg [{}]", tenantId, alarmUpdateMsg); AlarmId alarmId = new AlarmId(new UUID(alarmUpdateMsg.getIdMSB(), alarmUpdateMsg.getIdLSB())); + boolean isEdgeVersionOlderThan_3_6_2 = EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion); Alarm alarm = isEdgeVersionOlderThan_3_6_2 ? createDeprecatedAlarm(tenantId, alarmUpdateMsg) : JacksonUtil.fromStringIgnoreUnknownProperties(alarmUpdateMsg.getEntity(), Alarm.class); if (alarm == null) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessor.java index 36a04dbb62..886a148f5a 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessor.java @@ -84,8 +84,7 @@ public class AssetEdgeProcessor extends BaseAssetProcessor { } private void saveOrUpdateAsset(TenantId tenantId, AssetId assetId, AssetUpdateMsg assetUpdateMsg, Edge edge, EdgeVersion edgeVersion) { - Pair resultPair = super.saveOrUpdateAsset(tenantId, assetId, assetUpdateMsg, - EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)); + Pair resultPair = super.saveOrUpdateAsset(tenantId, assetId, assetUpdateMsg, edgeVersion); Boolean created = resultPair.getFirst(); if (created) { createRelationFromEdge(tenantId, edge.getId(), assetId); @@ -148,8 +147,8 @@ public class AssetEdgeProcessor extends BaseAssetProcessor { } @Override - protected void setCustomerId(TenantId tenantId, CustomerId customerId, Asset asset, AssetUpdateMsg assetUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2) { - CustomerId customerUUID = isEdgeVersionOlderThan_3_6_2 + protected void setCustomerId(TenantId tenantId, CustomerId customerId, Asset asset, AssetUpdateMsg assetUpdateMsg, EdgeVersion edgeVersion) { + CustomerId customerUUID = EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion) ? safeGetCustomerId(assetUpdateMsg.getCustomerIdMSB(), assetUpdateMsg.getCustomerIdLSB()) : asset.getCustomerId() != null ? asset.getCustomerId() : customerId; asset.setCustomerId(customerUUID); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetProfileEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetProfileEdgeProcessor.java index 7f8efe1392..d5a98b523e 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetProfileEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetProfileEdgeProcessor.java @@ -74,8 +74,7 @@ public class AssetProfileEdgeProcessor extends BaseAssetProfileProcessor { } private void saveOrUpdateAssetProfile(TenantId tenantId, AssetProfileId assetProfileId, AssetProfileUpdateMsg assetProfileUpdateMsg, Edge edge, EdgeVersion edgeVersion) { - Pair resultPair = super.saveOrUpdateAssetProfile(tenantId, assetProfileId, assetProfileUpdateMsg, - EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)); + Pair resultPair = super.saveOrUpdateAssetProfile(tenantId, assetProfileId, assetProfileUpdateMsg, edgeVersion); Boolean created = resultPair.getFirst(); if (created) { createRelationFromEdge(tenantId, edge.getId(), assetProfileId); @@ -134,16 +133,16 @@ public class AssetProfileEdgeProcessor extends BaseAssetProfileProcessor { } @Override - protected void setDefaultEdgeRuleChainId(AssetProfile assetProfile, RuleChainId ruleChainId, AssetProfileUpdateMsg assetProfileUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2) { - UUID defaultEdgeRuleChainUUID = isEdgeVersionOlderThan_3_6_2 + protected void setDefaultEdgeRuleChainId(AssetProfile assetProfile, RuleChainId ruleChainId, AssetProfileUpdateMsg assetProfileUpdateMsg, EdgeVersion edgeVersion) { + UUID defaultEdgeRuleChainUUID = EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion) ? safeGetUUID(assetProfileUpdateMsg.getDefaultRuleChainIdMSB(), assetProfileUpdateMsg.getDefaultRuleChainIdLSB()) : ruleChainId != null ? ruleChainId.getId() : null; assetProfile.setDefaultEdgeRuleChainId(defaultEdgeRuleChainUUID != null ? new RuleChainId(defaultEdgeRuleChainUUID) : null); } @Override - protected void setDefaultDashboardId(TenantId tenantId, DashboardId dashboardId, AssetProfile assetProfile, AssetProfileUpdateMsg assetProfileUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2) { - UUID defaultDashboardUUID = isEdgeVersionOlderThan_3_6_2 + protected void setDefaultDashboardId(TenantId tenantId, DashboardId dashboardId, AssetProfile assetProfile, AssetProfileUpdateMsg assetProfileUpdateMsg, EdgeVersion edgeVersion) { + UUID defaultDashboardUUID = EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion) ? safeGetUUID(assetProfileUpdateMsg.getDefaultDashboardIdMSB(), assetProfileUpdateMsg.getDefaultDashboardIdLSB()) : assetProfile.getDefaultDashboardId() != null ? assetProfile.getDefaultDashboardId().getId() : (dashboardId != null ? dashboardId.getId() : null); assetProfile.setDefaultDashboardId(defaultDashboardUUID != null ? new DashboardId(defaultDashboardUUID) : null); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProcessor.java index b5a1b48a1c..6403631f50 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProcessor.java @@ -26,19 +26,21 @@ import org.thingsboard.server.common.data.id.AssetProfileId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; +import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; import java.util.UUID; @Slf4j public abstract class BaseAssetProcessor extends BaseEdgeProcessor { - protected Pair saveOrUpdateAsset(TenantId tenantId, AssetId assetId, AssetUpdateMsg assetUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2) { + protected Pair saveOrUpdateAsset(TenantId tenantId, AssetId assetId, AssetUpdateMsg assetUpdateMsg, EdgeVersion edgeVersion) { boolean created = false; boolean assetNameUpdated = false; assetCreationLock.lock(); try { - Asset asset = isEdgeVersionOlderThan_3_6_2 + Asset asset = EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion) ? createAsset(tenantId, assetId, assetUpdateMsg) : JacksonUtil.fromStringIgnoreUnknownProperties(assetUpdateMsg.getEntity(), Asset.class); if (asset == null) { @@ -60,7 +62,7 @@ public abstract class BaseAssetProcessor extends BaseEdgeProcessor { assetNameUpdated = true; } asset.setName(assetName); - setCustomerId(tenantId, created ? null : assetById.getCustomerId(), asset, assetUpdateMsg, isEdgeVersionOlderThan_3_6_2); + setCustomerId(tenantId, created ? null : assetById.getCustomerId(), asset, assetUpdateMsg, edgeVersion); assetValidator.validate(asset, Asset::getTenantId); if (created) { @@ -94,5 +96,5 @@ public abstract class BaseAssetProcessor extends BaseEdgeProcessor { return asset; } - protected abstract void setCustomerId(TenantId tenantId, CustomerId customerId, Asset asset, AssetUpdateMsg assetUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2); + protected abstract void setCustomerId(TenantId tenantId, CustomerId customerId, Asset asset, AssetUpdateMsg assetUpdateMsg, EdgeVersion edgeVersion); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProfileProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProfileProcessor.java index bae0c8c068..98061dbecc 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProfileProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProfileProcessor.java @@ -26,19 +26,21 @@ import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; +import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; import java.nio.charset.StandardCharsets; @Slf4j public abstract class BaseAssetProfileProcessor extends BaseEdgeProcessor { - protected Pair saveOrUpdateAssetProfile(TenantId tenantId, AssetProfileId assetProfileId, AssetProfileUpdateMsg assetProfileUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2) { + protected Pair saveOrUpdateAssetProfile(TenantId tenantId, AssetProfileId assetProfileId, AssetProfileUpdateMsg assetProfileUpdateMsg, EdgeVersion edgeVersion) { boolean created = false; boolean assetProfileNameUpdated = false; assetCreationLock.lock(); try { - AssetProfile assetProfile = isEdgeVersionOlderThan_3_6_2 + AssetProfile assetProfile = EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion) ? createAssetProfile(tenantId, assetProfileId, assetProfileUpdateMsg) : JacksonUtil.fromStringIgnoreUnknownProperties(assetProfileUpdateMsg.getEntity(), AssetProfile.class); if (assetProfile == null) { @@ -63,8 +65,8 @@ public abstract class BaseAssetProfileProcessor extends BaseEdgeProcessor { RuleChainId ruleChainId = assetProfile.getDefaultRuleChainId(); setDefaultRuleChainId(tenantId, assetProfile, created ? null : assetProfileById.getDefaultRuleChainId()); - setDefaultEdgeRuleChainId(assetProfile, ruleChainId, assetProfileUpdateMsg, isEdgeVersionOlderThan_3_6_2); - setDefaultDashboardId(tenantId, created ? null : assetProfileById.getDefaultDashboardId(), assetProfile, assetProfileUpdateMsg, isEdgeVersionOlderThan_3_6_2); + setDefaultEdgeRuleChainId(assetProfile, ruleChainId, assetProfileUpdateMsg, edgeVersion); + setDefaultDashboardId(tenantId, created ? null : assetProfileById.getDefaultDashboardId(), assetProfile, assetProfileUpdateMsg, edgeVersion); assetProfileValidator.validate(assetProfile, AssetProfile::getTenantId); if (created) { @@ -95,7 +97,7 @@ public abstract class BaseAssetProfileProcessor extends BaseEdgeProcessor { protected abstract void setDefaultRuleChainId(TenantId tenantId, AssetProfile assetProfile, RuleChainId ruleChainId); - protected abstract void setDefaultEdgeRuleChainId(AssetProfile assetProfile, RuleChainId ruleChainId, AssetProfileUpdateMsg assetProfileUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2); + protected abstract void setDefaultEdgeRuleChainId(AssetProfile assetProfile, RuleChainId ruleChainId, AssetProfileUpdateMsg assetProfileUpdateMsg, EdgeVersion edgeVersion); - protected abstract void setDefaultDashboardId(TenantId tenantId, DashboardId dashboardId, AssetProfile assetProfile, AssetProfileUpdateMsg assetProfileUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2); + protected abstract void setDefaultDashboardId(TenantId tenantId, DashboardId dashboardId, AssetProfile assetProfile, AssetProfileUpdateMsg assetProfileUpdateMsg, EdgeVersion edgeVersion); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/BaseDashboardProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/BaseDashboardProcessor.java index 46f4088f41..0ae26d5160 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/BaseDashboardProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/BaseDashboardProcessor.java @@ -25,16 +25,18 @@ import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; +import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; import java.util.Set; @Slf4j public abstract class BaseDashboardProcessor extends BaseEdgeProcessor { - protected boolean saveOrUpdateDashboard(TenantId tenantId, DashboardId dashboardId, DashboardUpdateMsg dashboardUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2, CustomerId customerId) { + protected boolean saveOrUpdateDashboard(TenantId tenantId, DashboardId dashboardId, DashboardUpdateMsg dashboardUpdateMsg, CustomerId customerId, EdgeVersion edgeVersion) { boolean created = false; - Dashboard dashboard = isEdgeVersionOlderThan_3_6_2 + Dashboard dashboard = EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion) ? createDashboard(tenantId, dashboardId, dashboardUpdateMsg) : JacksonUtil.fromStringIgnoreUnknownProperties(dashboardUpdateMsg.getEntity(), Dashboard.class); if (dashboard == null) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardEdgeProcessor.java index a7e7cdbc12..eec398e5ff 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardEdgeProcessor.java @@ -35,7 +35,6 @@ import org.thingsboard.server.gen.edge.v1.DownlinkMsg; import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; import java.util.Set; import java.util.UUID; @@ -80,7 +79,7 @@ public class DashboardEdgeProcessor extends BaseDashboardProcessor { private void saveOrUpdateDashboard(TenantId tenantId, DashboardId dashboardId, DashboardUpdateMsg dashboardUpdateMsg, Edge edge, EdgeVersion edgeVersion) { boolean created = super.saveOrUpdateDashboard(tenantId, dashboardId, dashboardUpdateMsg, - EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion), edge.getCustomerId()); + edge.getCustomerId(), edgeVersion); if (created) { createRelationFromEdge(tenantId, edge.getId(), dashboardId); pushDashboardCreatedEventToRuleEngine(tenantId, edge, dashboardId); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProcessor.java index 4c5141034d..771073b091 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProcessor.java @@ -32,8 +32,10 @@ import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentialsType; import org.thingsboard.server.gen.edge.v1.DeviceCredentialsUpdateMsg; import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.queue.util.DataDecodingEncodingService; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; +import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; import java.util.Optional; import java.util.UUID; @@ -44,12 +46,12 @@ public abstract class BaseDeviceProcessor extends BaseEdgeProcessor { @Autowired protected DataDecodingEncodingService dataDecodingEncodingService; - protected Pair saveOrUpdateDevice(TenantId tenantId, DeviceId deviceId, DeviceUpdateMsg deviceUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2) { + protected Pair saveOrUpdateDevice(TenantId tenantId, DeviceId deviceId, DeviceUpdateMsg deviceUpdateMsg, EdgeVersion edgeVersion) { boolean created = false; boolean deviceNameUpdated = false; deviceCreationLock.lock(); try { - Device device = isEdgeVersionOlderThan_3_6_2 + Device device = EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion) ? createDevice(tenantId, deviceId, deviceUpdateMsg) : JacksonUtil.fromStringIgnoreUnknownProperties(deviceUpdateMsg.getEntity(), Device.class); if (device == null) { @@ -71,7 +73,7 @@ public abstract class BaseDeviceProcessor extends BaseEdgeProcessor { deviceNameUpdated = true; } device.setName(deviceName); - setCustomerId(tenantId, created ? null : deviceById.getCustomerId(), device, deviceUpdateMsg, isEdgeVersionOlderThan_3_6_2); + setCustomerId(tenantId, created ? null : deviceById.getCustomerId(), device, deviceUpdateMsg, edgeVersion); deviceValidator.validate(device, Device::getTenantId); if (created) { @@ -157,5 +159,5 @@ public abstract class BaseDeviceProcessor extends BaseEdgeProcessor { return deviceCredentials; } - protected abstract void setCustomerId(TenantId tenantId, CustomerId customerId, Device device, DeviceUpdateMsg deviceUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2); + protected abstract void setCustomerId(TenantId tenantId, CustomerId customerId, Device device, DeviceUpdateMsg deviceUpdateMsg, EdgeVersion edgeVersion); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProfileProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProfileProcessor.java index 97d751dc27..e97f0aaac1 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProfileProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProfileProcessor.java @@ -32,8 +32,10 @@ import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.queue.util.DataDecodingEncodingService; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; +import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; import java.nio.charset.StandardCharsets; import java.util.Optional; @@ -45,12 +47,12 @@ public abstract class BaseDeviceProfileProcessor extends BaseEdgeProcessor { @Autowired private DataDecodingEncodingService dataDecodingEncodingService; - protected Pair saveOrUpdateDeviceProfile(TenantId tenantId, DeviceProfileId deviceProfileId, DeviceProfileUpdateMsg deviceProfileUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2) { + protected Pair saveOrUpdateDeviceProfile(TenantId tenantId, DeviceProfileId deviceProfileId, DeviceProfileUpdateMsg deviceProfileUpdateMsg, EdgeVersion edgeVersion) { boolean created = false; boolean deviceProfileNameUpdated = false; deviceCreationLock.lock(); try { - DeviceProfile deviceProfile = isEdgeVersionOlderThan_3_6_2 + DeviceProfile deviceProfile = EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion) ? createDeviceProfile(tenantId, deviceProfileId, deviceProfileUpdateMsg) : JacksonUtil.fromStringIgnoreUnknownProperties(deviceProfileUpdateMsg.getEntity(), DeviceProfile.class); if (deviceProfile == null) { @@ -75,8 +77,8 @@ public abstract class BaseDeviceProfileProcessor extends BaseEdgeProcessor { RuleChainId ruleChainId = deviceProfile.getDefaultRuleChainId(); setDefaultRuleChainId(tenantId, deviceProfile, created ? null : deviceProfileById.getDefaultRuleChainId()); - setDefaultEdgeRuleChainId(deviceProfile, ruleChainId, deviceProfileUpdateMsg, isEdgeVersionOlderThan_3_6_2); - setDefaultDashboardId(tenantId, created ? null : deviceProfileById.getDefaultDashboardId(), deviceProfile, deviceProfileUpdateMsg, isEdgeVersionOlderThan_3_6_2); + setDefaultEdgeRuleChainId(deviceProfile, ruleChainId, deviceProfileUpdateMsg, edgeVersion); + setDefaultDashboardId(tenantId, created ? null : deviceProfileById.getDefaultDashboardId(), deviceProfile, deviceProfileUpdateMsg, edgeVersion); deviceProfileValidator.validate(deviceProfile, DeviceProfile::getTenantId); if (created) { @@ -127,7 +129,7 @@ public abstract class BaseDeviceProfileProcessor extends BaseEdgeProcessor { protected abstract void setDefaultRuleChainId(TenantId tenantId, DeviceProfile deviceProfile, RuleChainId ruleChainId); - protected abstract void setDefaultEdgeRuleChainId(DeviceProfile deviceProfile, RuleChainId ruleChainId, DeviceProfileUpdateMsg deviceProfileUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2); + protected abstract void setDefaultEdgeRuleChainId(DeviceProfile deviceProfile, RuleChainId ruleChainId, DeviceProfileUpdateMsg deviceProfileUpdateMsg, EdgeVersion edgeVersion); - protected abstract void setDefaultDashboardId(TenantId tenantId, DashboardId dashboardId, DeviceProfile deviceProfile, DeviceProfileUpdateMsg deviceProfileUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2); + protected abstract void setDefaultDashboardId(TenantId tenantId, DashboardId dashboardId, DeviceProfile deviceProfile, DeviceProfileUpdateMsg deviceProfileUpdateMsg, EdgeVersion edgeVersion); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java index fb8db00890..b4ca6b3436 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java @@ -110,8 +110,7 @@ public class DeviceEdgeProcessor extends BaseDeviceProcessor { } private void saveOrUpdateDevice(TenantId tenantId, DeviceId deviceId, DeviceUpdateMsg deviceUpdateMsg, Edge edge, EdgeVersion edgeVersion) { - Pair resultPair = super.saveOrUpdateDevice(tenantId, deviceId, deviceUpdateMsg, - EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)); + Pair resultPair = super.saveOrUpdateDevice(tenantId, deviceId, deviceUpdateMsg, edgeVersion); Boolean created = resultPair.getFirst(); if (created) { createRelationFromEdge(tenantId, edge.getId(), deviceId); @@ -289,8 +288,8 @@ public class DeviceEdgeProcessor extends BaseDeviceProcessor { } @Override - protected void setCustomerId(TenantId tenantId, CustomerId customerId, Device device, DeviceUpdateMsg deviceUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2) { - CustomerId customerUUID = isEdgeVersionOlderThan_3_6_2 + protected void setCustomerId(TenantId tenantId, CustomerId customerId, Device device, DeviceUpdateMsg deviceUpdateMsg, EdgeVersion edgeVersion) { + CustomerId customerUUID = EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion) ? safeGetCustomerId(deviceUpdateMsg.getCustomerIdMSB(), deviceUpdateMsg.getCustomerIdLSB()) : device.getCustomerId() != null ? device.getCustomerId() : customerId; device.setCustomerId(customerUUID); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceProfileEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceProfileEdgeProcessor.java index 7e265e6907..4f43fea35b 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceProfileEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceProfileEdgeProcessor.java @@ -74,8 +74,7 @@ public class DeviceProfileEdgeProcessor extends BaseDeviceProfileProcessor { } private void saveOrUpdateDeviceProfile(TenantId tenantId, DeviceProfileId deviceProfileId, DeviceProfileUpdateMsg deviceProfileUpdateMsg, Edge edge, EdgeVersion edgeVersion) { - Pair resultPair = super.saveOrUpdateDeviceProfile(tenantId, deviceProfileId, deviceProfileUpdateMsg, - EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)); + Pair resultPair = super.saveOrUpdateDeviceProfile(tenantId, deviceProfileId, deviceProfileUpdateMsg, edgeVersion); Boolean created = resultPair.getFirst(); if (created) { createRelationFromEdge(tenantId, edge.getId(), deviceProfileId); @@ -134,16 +133,16 @@ public class DeviceProfileEdgeProcessor extends BaseDeviceProfileProcessor { } @Override - protected void setDefaultEdgeRuleChainId(DeviceProfile deviceProfile, RuleChainId ruleChainId, DeviceProfileUpdateMsg deviceProfileUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2) { - UUID defaultEdgeRuleChainUUID = isEdgeVersionOlderThan_3_6_2 + protected void setDefaultEdgeRuleChainId(DeviceProfile deviceProfile, RuleChainId ruleChainId, DeviceProfileUpdateMsg deviceProfileUpdateMsg, EdgeVersion edgeVersion) { + UUID defaultEdgeRuleChainUUID = EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion) ? safeGetUUID(deviceProfileUpdateMsg.getDefaultRuleChainIdMSB(), deviceProfileUpdateMsg.getDefaultRuleChainIdLSB()) : ruleChainId != null ? ruleChainId.getId() : null; deviceProfile.setDefaultEdgeRuleChainId(defaultEdgeRuleChainUUID != null ? new RuleChainId(defaultEdgeRuleChainUUID) : null); } @Override - protected void setDefaultDashboardId(TenantId tenantId, DashboardId dashboardId, DeviceProfile deviceProfile, DeviceProfileUpdateMsg deviceProfileUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2) { - UUID defaultDashboardUUID = isEdgeVersionOlderThan_3_6_2 + protected void setDefaultDashboardId(TenantId tenantId, DashboardId dashboardId, DeviceProfile deviceProfile, DeviceProfileUpdateMsg deviceProfileUpdateMsg, EdgeVersion edgeVersion) { + UUID defaultDashboardUUID = EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion) ? safeGetUUID(deviceProfileUpdateMsg.getDefaultDashboardIdMSB(), deviceProfileUpdateMsg.getDefaultDashboardIdLSB()) : deviceProfile.getDefaultDashboardId() != null ? deviceProfile.getDefaultDashboardId().getId() : (dashboardId != null ? dashboardId.getId() : null); deviceProfile.setDefaultDashboardId(defaultDashboardUUID != null ? new DashboardId(defaultDashboardUUID) : null); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/BaseEntityViewProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/BaseEntityViewProcessor.java index 7ffa4874fd..723f3e0e2f 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/BaseEntityViewProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/BaseEntityViewProcessor.java @@ -27,18 +27,20 @@ import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.gen.edge.v1.EdgeEntityType; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.EntityViewUpdateMsg; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; +import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; import java.util.UUID; @Slf4j public abstract class BaseEntityViewProcessor extends BaseEdgeProcessor { - protected Pair saveOrUpdateEntityView(TenantId tenantId, EntityViewId entityViewId, EntityViewUpdateMsg entityViewUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2) { + protected Pair saveOrUpdateEntityView(TenantId tenantId, EntityViewId entityViewId, EntityViewUpdateMsg entityViewUpdateMsg, EdgeVersion edgeVersion) { boolean created = false; boolean entityViewNameUpdated = false; - EntityView entityView = isEdgeVersionOlderThan_3_6_2 + EntityView entityView = EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion) ? createEntityView(tenantId, entityViewId, entityViewUpdateMsg) : JacksonUtil.fromStringIgnoreUnknownProperties(entityViewUpdateMsg.getEntity(), EntityView.class); if (entityView == null) { @@ -60,7 +62,7 @@ public abstract class BaseEntityViewProcessor extends BaseEdgeProcessor { entityViewNameUpdated = true; } entityView.setName(entityViewName); - setCustomerId(tenantId, created ? null : entityViewById.getCustomerId(), entityView, entityViewUpdateMsg, isEdgeVersionOlderThan_3_6_2); + setCustomerId(tenantId, created ? null : entityViewById.getCustomerId(), entityView, entityViewUpdateMsg, edgeVersion); entityViewValidator.validate(entityView, EntityView::getTenantId); if (created) { @@ -92,5 +94,5 @@ public abstract class BaseEntityViewProcessor extends BaseEdgeProcessor { return entityView; } - protected abstract void setCustomerId(TenantId tenantId, CustomerId customerId, EntityView entityView, EntityViewUpdateMsg entityViewUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2); + protected abstract void setCustomerId(TenantId tenantId, CustomerId customerId, EntityView entityView, EntityViewUpdateMsg entityViewUpdateMsg, EdgeVersion edgeVersion); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewEdgeProcessor.java index 50c5755661..73f66fc614 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewEdgeProcessor.java @@ -81,8 +81,7 @@ public class EntityViewEdgeProcessor extends BaseEntityViewProcessor { } private void saveOrUpdateEntityView(TenantId tenantId, EntityViewId entityViewId, EntityViewUpdateMsg entityViewUpdateMsg, Edge edge, EdgeVersion edgeVersion) { - Pair resultPair = super.saveOrUpdateEntityView(tenantId, entityViewId, entityViewUpdateMsg, - EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)); + Pair resultPair = super.saveOrUpdateEntityView(tenantId, entityViewId, entityViewUpdateMsg, edgeVersion); Boolean created = resultPair.getFirst(); if (created) { createRelationFromEdge(tenantId, edge.getId(), entityViewId); @@ -140,8 +139,8 @@ public class EntityViewEdgeProcessor extends BaseEntityViewProcessor { } @Override - protected void setCustomerId(TenantId tenantId, CustomerId customerId, EntityView entityView, EntityViewUpdateMsg entityViewUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2) { - CustomerId customerUUID = isEdgeVersionOlderThan_3_6_2 + protected void setCustomerId(TenantId tenantId, CustomerId customerId, EntityView entityView, EntityViewUpdateMsg entityViewUpdateMsg, EdgeVersion edgeVersion) { + CustomerId customerUUID = EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion) ? safeGetCustomerId(entityViewUpdateMsg.getCustomerIdMSB(), entityViewUpdateMsg.getCustomerIdLSB()) : entityView.getCustomerId() != null ? entityView.getCustomerId() : customerId; entityView.setCustomerId(customerUUID); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/BaseRelationProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/BaseRelationProcessor.java index d5987c29cd..d673b1a613 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/BaseRelationProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/BaseRelationProcessor.java @@ -25,18 +25,20 @@ import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.RelationTypeGroup; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; +import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; import java.util.UUID; @Slf4j public abstract class BaseRelationProcessor extends BaseEdgeProcessor { - protected ListenableFuture processRelationMsg(TenantId tenantId, RelationUpdateMsg relationUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2) { + protected ListenableFuture processRelationMsg(TenantId tenantId, RelationUpdateMsg relationUpdateMsg, EdgeVersion edgeVersion) { log.trace("[{}] processRelationMsg [{}]", tenantId, relationUpdateMsg); try { - EntityRelation entityRelation = isEdgeVersionOlderThan_3_6_2 + EntityRelation entityRelation = EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion) ? createEntityRelation(relationUpdateMsg) : JacksonUtil.fromStringIgnoreUnknownProperties(relationUpdateMsg.getEntity(), EntityRelation.class); if (entityRelation == null) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessor.java index 8c6854ff3d..566bed5146 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessor.java @@ -35,7 +35,6 @@ import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; import java.util.ArrayList; import java.util.HashSet; @@ -51,7 +50,7 @@ public class RelationEdgeProcessor extends BaseRelationProcessor { log.trace("[{}] executing processRelationMsgFromEdge [{}] from edge [{}]", tenantId, relationUpdateMsg, edge.getId()); try { edgeSynchronizationManager.getEdgeId().set(edge.getId()); - return processRelationMsg(tenantId, relationUpdateMsg, EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)); + return processRelationMsg(tenantId, relationUpdateMsg, edgeVersion); } finally { edgeSynchronizationManager.getEdgeId().remove(); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/BaseResourceProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/BaseResourceProcessor.java index 5ed377fed2..ebcbb2b7c9 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/BaseResourceProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/BaseResourceProcessor.java @@ -25,16 +25,18 @@ import org.thingsboard.server.common.data.TbResourceInfo; import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageDataIterable; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.ResourceUpdateMsg; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; +import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; @Slf4j public abstract class BaseResourceProcessor extends BaseEdgeProcessor { - protected boolean saveOrUpdateTbResource(TenantId tenantId, TbResourceId tbResourceId, ResourceUpdateMsg resourceUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2) { + protected boolean saveOrUpdateTbResource(TenantId tenantId, TbResourceId tbResourceId, ResourceUpdateMsg resourceUpdateMsg, EdgeVersion edgeVersion) { boolean resourceKeyUpdated = false; try { - TbResource resource = isEdgeVersionOlderThan_3_6_2 + TbResource resource = EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion) ? createTbResource(tenantId, resourceUpdateMsg) : JacksonUtil.fromStringIgnoreUnknownProperties(resourceUpdateMsg.getEntity(), TbResource.class); if (resource == null) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessor.java index bd99e48622..ae514d0a54 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessor.java @@ -33,7 +33,6 @@ import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.ResourceUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; import java.util.UUID; @@ -50,7 +49,7 @@ public class ResourceEdgeProcessor extends BaseResourceProcessor { switch (resourceUpdateMsg.getMsgType()) { case ENTITY_CREATED_RPC_MESSAGE: case ENTITY_UPDATED_RPC_MESSAGE: - boolean resourceKeyUpdated = super.saveOrUpdateTbResource(tenantId, tbResourceId, resourceUpdateMsg, EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)); + boolean resourceKeyUpdated = super.saveOrUpdateTbResource(tenantId, tbResourceId, resourceUpdateMsg, edgeVersion); if (resourceKeyUpdated) { saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.TB_RESOURCE, EdgeEventActionType.UPDATED, tbResourceId, null); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessor.java index 667d661d2e..af9ff3de37 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessor.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.service.edge.rpc.processor.telemetry; -import com.fasterxml.jackson.core.JsonProcessingException; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.DataConstants; @@ -37,7 +36,7 @@ public class TelemetryEdgeProcessor extends BaseTelemetryProcessor { return DataConstants.EDGE_MSG_SOURCE; } - public DownlinkMsg convertTelemetryEventToDownlink(EdgeEvent edgeEvent) throws JsonProcessingException { + public DownlinkMsg convertTelemetryEventToDownlink(EdgeEvent edgeEvent) { if (edgeEvent.getBody() != null) { String bodyStr = edgeEvent.getBody().toString(); if (bodyStr.length() > 1000) { From 320de6b6c98cf20e4e6d73f72f3c94fb3b05f599 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Mon, 20 Nov 2023 13:28:28 +0200 Subject: [PATCH 174/252] Provide same logic with edgeVersion for updateDeviceCredentials --- .../edge/rpc/processor/device/BaseDeviceProcessor.java | 4 ++-- .../edge/rpc/processor/device/DeviceEdgeProcessor.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProcessor.java index 771073b091..e2ac06e84b 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProcessor.java @@ -121,8 +121,8 @@ public abstract class BaseDeviceProcessor extends BaseEdgeProcessor { return device; } - protected void updateDeviceCredentials(TenantId tenantId, DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg, boolean isEdgeVersionOlderThan_3_6_2) { - DeviceCredentials deviceCredentials = isEdgeVersionOlderThan_3_6_2 + protected void updateDeviceCredentials(TenantId tenantId, DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg, EdgeVersion edgeVersion) { + DeviceCredentials deviceCredentials = EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion) ? createDeviceCredentials(deviceCredentialsUpdateMsg) : JacksonUtil.fromStringIgnoreUnknownProperties(deviceCredentialsUpdateMsg.getEntity(), DeviceCredentials.class); if (deviceCredentials == null) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java index b4ca6b3436..97eb6bcfa1 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java @@ -102,7 +102,7 @@ public class DeviceEdgeProcessor extends BaseDeviceProcessor { try { edgeSynchronizationManager.getEdgeId().set(edgeId); - updateDeviceCredentials(tenantId, deviceCredentialsUpdateMsg, EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)); + updateDeviceCredentials(tenantId, deviceCredentialsUpdateMsg, edgeVersion); } finally { edgeSynchronizationManager.getEdgeId().remove(); } From 932c6e8b706ab89ef2e5c9e4ab4b914759babeb7 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Mon, 20 Nov 2023 15:18:33 +0200 Subject: [PATCH 175/252] Add settings for browser cache TTL for system and tenant images --- .../server/controller/ImageController.java | 22 ++++++++++++++----- .../resource/DefaultTbImageService.java | 4 ++-- .../src/main/resources/thingsboard.yml | 9 +++++--- 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/ImageController.java b/application/src/main/java/org/thingsboard/server/controller/ImageController.java index a027e77b2e..1a1b4e3b46 100644 --- a/application/src/main/java/org/thingsboard/server/controller/ImageController.java +++ b/application/src/main/java/org/thingsboard/server/controller/ImageController.java @@ -19,6 +19,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import io.swagger.annotations.ApiParam; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.ByteArrayResource; import org.springframework.http.CacheControl; import org.springframework.http.HttpHeaders; @@ -55,6 +56,7 @@ import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; import java.time.Duration; +import java.util.concurrent.TimeUnit; import java.util.function.Supplier; import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION; @@ -73,6 +75,10 @@ public class ImageController extends BaseController { private final ImageService imageService; private final TbImageService tbImageService; + @Value("${cache.image.systemImagesBrowserTtlInMinutes:0}") + private int systemImagesBrowserTtlInMinutes; + @Value("${cache.image.tenantImagesBrowserTtlInMinutes:0}") + private int tenantImagesBrowserTtlInMinutes; private static final String IMAGE_URL = "/api/images/{type}/{key}"; private static final String SYSTEM_IMAGE = "system"; @@ -194,14 +200,20 @@ public class ImageController extends BaseController { data = imageService.getImageData(tenantId, imageInfo.getId()); } tbImageService.putETag(cacheKey, descriptor.getEtag()); - return ResponseEntity.ok() + var result = ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + fileName) .header("x-filename", fileName) - .contentLength(data.length) .header("Content-Type", descriptor.getMediaType()) - .cacheControl(CacheControl.noCache()) - .eTag(descriptor.getEtag()) - .body(new ByteArrayResource(data)); + .contentLength(data.length) + .eTag(descriptor.getEtag()); + if (systemImagesBrowserTtlInMinutes > 0 && imageInfo.getTenantId().isSysTenantId()) { + result.cacheControl(CacheControl.maxAge(systemImagesBrowserTtlInMinutes, TimeUnit.MINUTES)); + } else if (tenantImagesBrowserTtlInMinutes > 0 && !imageInfo.getTenantId().isSysTenantId()) { + result.cacheControl(CacheControl.maxAge(tenantImagesBrowserTtlInMinutes, TimeUnit.MINUTES)); + } else { + result.cacheControl(CacheControl.noCache()); + } + return result.body(new ByteArrayResource(data)); } private TbResourceInfo checkImageInfo(String imageType, String key, Operation operation) throws ThingsboardException { diff --git a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java index 4b70814e26..0820965544 100644 --- a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java +++ b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java @@ -41,8 +41,8 @@ public class DefaultTbImageService extends AbstractTbEntityService implements Tb private final Cache cache; public DefaultTbImageService(ImageService imageService, - @Value("${cache.imageETags.timeToLiveInMinutes:120}") int cacheTtl, - @Value("${cache.imageETags.maxSize:200000}") int cacheMaxSize) { + @Value("${cache.image.etag.timeToLiveInMinutes:120}") int cacheTtl, + @Value("${cache.image.etag.maxSize:200000}") int cacheMaxSize) { this.imageService = imageService; this.cache = Caffeine.newBuilder() .expireAfterAccess(cacheTtl, TimeUnit.MINUTES) diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 9e59db76e8..6dde5de957 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -586,9 +586,12 @@ cache: entityLimits: timeToLiveInMinutes: "${CACHE_SPECS_ENTITY_LIMITS_TTL:5}" # Entity limits cache TTL maxSize: "${CACHE_SPECS_ENTITY_LIMITS_MAX_SIZE:100000}" # 0 means the cache is disabled - imageETags: - timeToLiveInMinutes: "${CACHE_SPECS_IMAGE_ETAGS_TTL:44640}" # Image ETags cache TTL - maxSize: "${CACHE_SPECS_IMAGE_ETAGS_MAX_SIZE:1000000}" # 0 means the cache is disabled + image: + etag: + timeToLiveInMinutes: "${CACHE_SPECS_IMAGE_ETAGS_TTL:44640}" # Image ETags cache TTL + maxSize: "${CACHE_SPECS_IMAGE_ETAGS_MAX_SIZE:1000000}" # 0 means the cache is disabled + systemImagesBrowserTtlInMinutes: "${CACHE_SPECS_IMAGE_SYSTEM_BROWSER_TTL:0}" # Browser cache TTL for system images in minutes. 0 means the cache is disabled + tenantImagesBrowserTtlInMinutes: "${CACHE_SPECS_IMAGE_TENANT_BROWSER_TTL:0}" # Browser cache TTL for tenant images in minutes. 0 means the cache is disabled # Spring data parameters spring.data.redis.repositories.enabled: false # Disable this because it is not required. From fa12ba544d3f35bd01afa169728e8881054c8222 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Mon, 20 Nov 2023 15:20:40 +0200 Subject: [PATCH 176/252] moved pubsub queue executor to PubSubSettings --- .../src/main/resources/thingsboard.yml | 6 +- .../pubsub/TbPubSubConsumerTemplate.java | 2 +- .../pubsub/TbPubSubProducerTemplate.java | 2 +- .../pubsub/TbPubSubQueueExecutorProvider.java | 60 ------------------- .../server/queue/pubsub/TbPubSubSettings.java | 25 +++++++- .../src/main/resources/tb-vc-executor.yml | 2 +- .../src/main/resources/tb-coap-transport.yml | 2 +- .../src/main/resources/tb-http-transport.yml | 2 +- .../src/main/resources/tb-lwm2m-transport.yml | 2 +- .../src/main/resources/tb-mqtt-transport.yml | 2 +- .../src/main/resources/tb-snmp-transport.yml | 2 +- 11 files changed, 35 insertions(+), 72 deletions(-) delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubQueueExecutorProvider.java diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 304ad96a34..6025114a3d 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -1411,8 +1411,8 @@ queue: max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" # Number of messages per consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" - # Thread pool size for pubsub queue executor provider. If not set - default pubsub executor provider value will be used (5 * number of available processors) - executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:}" + # Thread pool size for pubsub queue executor provider. If set to 0 - default pubsub executor provider value will be used (5 * number of available processors) + executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:0}" queue-properties: # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consumed again rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" @@ -1643,7 +1643,7 @@ service: assigned_tenant_profiles: "${TB_RULE_ENGINE_ASSIGNED_TENANT_PROFILES:}" pubsub: # Thread pool size for pubsub rule node executor provider. If not set - default pubsub executor provider value will be used (5 * number of available processors) - executor_thread_pool_size: "${TB_RULE_ENGINE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:}" + executor_thread_pool_size: "${TB_RULE_ENGINE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:0}" # Metrics parameters metrics: diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubConsumerTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubConsumerTemplate.java index 1135053a66..1a84b80cd2 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubConsumerTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubConsumerTemplate.java @@ -77,7 +77,7 @@ public class TbPubSubConsumerTemplate extends AbstractPara SubscriberStubSettings.defaultGrpcTransportProviderBuilder() .setMaxInboundMessageSize(pubSubSettings.getMaxMsgSize()) .build()) - .setExecutorProvider(FixedExecutorProvider.create(pubSubSettings.getTbPubSubQueueExecutorProvider().getExecutor())) + .setExecutorProvider(pubSubSettings.getExecutorProvider()) .build(); this.subscriber = GrpcSubscriberStub.create(subscriberStubSettings); } catch (IOException e) { diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubProducerTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubProducerTemplate.java index bf297d8af4..3107855ab1 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubProducerTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubProducerTemplate.java @@ -123,7 +123,7 @@ public class TbPubSubProducerTemplate implements TbQueuePr ProjectTopicName topicName = ProjectTopicName.of(pubSubSettings.getProjectId(), topic); Publisher publisher = Publisher.newBuilder(topicName) .setCredentialsProvider(pubSubSettings.getCredentialsProvider()) - .setExecutorProvider(FixedExecutorProvider.create(pubSubSettings.getTbPubSubQueueExecutorProvider().getExecutor())) + .setExecutorProvider(pubSubSettings.getExecutorProvider()) .build(); publisherMap.put(topic, publisher); return publisher; diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubQueueExecutorProvider.java b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubQueueExecutorProvider.java deleted file mode 100644 index 370670d554..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubQueueExecutorProvider.java +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Copyright © 2016-2023 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. - */ -package org.thingsboard.server.queue.pubsub; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.stereotype.Component; -import org.thingsboard.common.util.ExecutorProvider; -import org.thingsboard.common.util.ThingsBoardThreadFactory; - -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; - -@ConditionalOnExpression("'${queue.type:null}'=='pubsub'") -@Component -public class TbPubSubQueueExecutorProvider implements ExecutorProvider { - - @Value("${queue.pubsub.executor_thread_pool_size}") - private Integer threadPoolSize; - /** - * Refers to com.google.cloud.pubsub.v1.Publisher default executor configuration - */ - private static final int THREADS_PER_CPU = 5; - private ScheduledExecutorService executor; - - @PostConstruct - public void init() { - if (threadPoolSize == null) { - threadPoolSize = THREADS_PER_CPU * Runtime.getRuntime().availableProcessors(); - } - executor = Executors.newScheduledThreadPool(threadPoolSize, ThingsBoardThreadFactory.forName("pubsub-queue-executor")); - } - - @Override - public ScheduledExecutorService getExecutor() { - return executor; - } - - @PreDestroy - private void destroy() { - if (executor != null) { - executor.shutdownNow(); - } - } -} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubSettings.java b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubSettings.java index 530453f26b..145661fed1 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubSettings.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubSettings.java @@ -17,16 +17,20 @@ package org.thingsboard.server.queue.pubsub; import com.google.api.gax.core.CredentialsProvider; import com.google.api.gax.core.FixedCredentialsProvider; +import com.google.api.gax.core.FixedExecutorProvider; import com.google.auth.oauth2.ServiceAccountCredentials; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Component; +import org.thingsboard.common.util.ThingsBoardThreadFactory; import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; import java.io.ByteArrayInputStream; import java.io.IOException; +import java.util.concurrent.Executors; @Slf4j @ConditionalOnExpression("'${queue.type:null}'=='pubsub'") @@ -46,7 +50,15 @@ public class TbPubSubSettings { @Value("${queue.pubsub.max_messages}") private int maxMessages; - private final TbPubSubQueueExecutorProvider tbPubSubQueueExecutorProvider; + @Value("${queue.pubsub.executor_thread_pool_size:0}") + private int threadPoolSize; + + /** + * Refers to com.google.cloud.pubsub.v1.Publisher default executor configuration + */ + private static final int THREADS_PER_CPU = 5; + + private FixedExecutorProvider executorProvider; private CredentialsProvider credentialsProvider; @@ -55,6 +67,17 @@ public class TbPubSubSettings { ServiceAccountCredentials credentials = ServiceAccountCredentials.fromStream( new ByteArrayInputStream(serviceAccount.getBytes())); credentialsProvider = FixedCredentialsProvider.create(credentials); + if (threadPoolSize == 0) { + threadPoolSize = THREADS_PER_CPU * Runtime.getRuntime().availableProcessors(); + } + executorProvider = FixedExecutorProvider + .create(Executors.newScheduledThreadPool(threadPoolSize, ThingsBoardThreadFactory.forName("pubsub-queue-executor"))); } + @PreDestroy + private void destroy() { + if (executorProvider != null) { + executorProvider.getExecutor().shutdownNow(); + } + } } diff --git a/msa/vc-executor/src/main/resources/tb-vc-executor.yml b/msa/vc-executor/src/main/resources/tb-vc-executor.yml index 62957debcf..8c363b019c 100644 --- a/msa/vc-executor/src/main/resources/tb-vc-executor.yml +++ b/msa/vc-executor/src/main/resources/tb-vc-executor.yml @@ -173,7 +173,7 @@ queue: # Number of messages per consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" # Thread pool size for pubsub queue executor provider. If not set - default pubsub executor provider value will be used (5 * number of available processors) - executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:}" + executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:0}" queue-properties: # Pub/Sub properties for Core subscribers, messages which will commit after ackDeadlineInSec period can be consumed again core: "${TB_QUEUE_PUBSUB_CORE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" diff --git a/transport/coap/src/main/resources/tb-coap-transport.yml b/transport/coap/src/main/resources/tb-coap-transport.yml index c376d6a176..0db3e71f52 100644 --- a/transport/coap/src/main/resources/tb-coap-transport.yml +++ b/transport/coap/src/main/resources/tb-coap-transport.yml @@ -296,7 +296,7 @@ queue: # Number of messages per consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" # Thread pool size for pubsub queue executor provider. If not set - default pubsub executor provider value will be used (5 * number of available processors) - executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:}" + executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:0}" queue-properties: # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consumed again rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" diff --git a/transport/http/src/main/resources/tb-http-transport.yml b/transport/http/src/main/resources/tb-http-transport.yml index f3fd100fd7..d4fda384c7 100644 --- a/transport/http/src/main/resources/tb-http-transport.yml +++ b/transport/http/src/main/resources/tb-http-transport.yml @@ -279,7 +279,7 @@ queue: # Number of messages per a consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" # Thread pool size for pubsub queue executor provider. If not set - default pubsub executor provider value will be used (5 * number of available processors) - executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:}" + executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:0}" queue-properties: # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consume again rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" diff --git a/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml b/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml index 968385b5e2..06b8c1a84a 100644 --- a/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml +++ b/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml @@ -375,7 +375,7 @@ queue: # Number of messages per consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" # Thread pool size for pubsub queue executor provider. If not set - default pubsub executor provider value will be used (5 * number of available processors) - executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:}" + executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:0}" queue-properties: # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consumed again rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" diff --git a/transport/mqtt/src/main/resources/tb-mqtt-transport.yml b/transport/mqtt/src/main/resources/tb-mqtt-transport.yml index 1e97c59253..afb34d7063 100644 --- a/transport/mqtt/src/main/resources/tb-mqtt-transport.yml +++ b/transport/mqtt/src/main/resources/tb-mqtt-transport.yml @@ -312,7 +312,7 @@ queue: # Number of messages per consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" # Thread pool size for pubsub queue executor provider. If not set - default pubsub executor provider value will be used (5 * number of available processors) - executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:}" + executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:0}" queue-properties: # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consumed again rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" diff --git a/transport/snmp/src/main/resources/tb-snmp-transport.yml b/transport/snmp/src/main/resources/tb-snmp-transport.yml index 8d3f842325..6ee6333fc2 100644 --- a/transport/snmp/src/main/resources/tb-snmp-transport.yml +++ b/transport/snmp/src/main/resources/tb-snmp-transport.yml @@ -265,7 +265,7 @@ queue: # Number of messages per consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" # Thread pool size for pubsub queue executor provider. If not set - default pubsub executor provider value will be used (5 * number of available processors) - executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:}" + executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:0}" queue-properties: # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consumed again rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" From 8a926ae239820181778314c071e8f605fd63dbbf Mon Sep 17 00:00:00 2001 From: imbeacon Date: Mon, 20 Nov 2023 17:25:19 +0200 Subject: [PATCH 177/252] Changes, due to comments --- .../dao/util/DeviceConnectivityUtil.java | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java b/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java index efd75e305e..3434b47965 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java +++ b/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java @@ -44,6 +44,8 @@ public class DeviceConnectivityUtil { public static final String JSON_EXAMPLE_PAYLOAD = "\"{temperature:25}\""; public static final String DOCKER_RUN = "docker run --rm -it "; public static final String GATEWAY_DOCKER_RUN = "docker run -it "; + + public static final String NETWORK_HOST_PARAM = "--network=host "; public static final String MQTT_IMAGE = "thingsboard/mosquitto-clients "; public static final String COAP_IMAGE = "thingsboard/coap-clients "; private final static Pattern VALID_URL_PATTERN = Pattern.compile("^(https?)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]"); @@ -98,14 +100,14 @@ public class DeviceConnectivityUtil { String gatewayContainerName = "tbGateway" + StringUtils.capitalize(host.replaceAll("[^A-Za-z0-9]", "")); StringBuilder command = new StringBuilder(GATEWAY_DOCKER_RUN); - command.append("-v {gatewayVolumePathPrefix}/logs:/thingsboard_gateway/logs".replace("{gatewayVolumePathPrefix}", gatewayVolumePathPrefix)); - command.append(" -v {gatewayVolumePathPrefix}/extensions:/thingsboard_gateway/extensions".replace("{gatewayVolumePathPrefix}", gatewayVolumePathPrefix)); - command.append(" -v {gatewayVolumePathPrefix}/config:/thingsboard_gateway/config".replace("{gatewayVolumePathPrefix}", gatewayVolumePathPrefix)); - command.append(isLocalhost(host) ? " --network=host" : ""); - command.append(" -p 5000:5000"); - command.append(" --name ").append(gatewayContainerName); - command.append(" -e host=").append(host); - command.append(" -e port=").append(port); + command.append("-v {gatewayVolumePathPrefix}/logs:/thingsboard_gateway/logs ".replace("{gatewayVolumePathPrefix}", gatewayVolumePathPrefix)); + command.append("-v {gatewayVolumePathPrefix}/extensions:/thingsboard_gateway/extensions ".replace("{gatewayVolumePathPrefix}", gatewayVolumePathPrefix)); + command.append("-v {gatewayVolumePathPrefix}/config:/thingsboard_gateway/config ".replace("{gatewayVolumePathPrefix}", gatewayVolumePathPrefix)); + command.append(isLocalhost(host) ? NETWORK_HOST_PARAM : ""); + command.append("-p 5000:5000 "); + command.append("--name ").append(gatewayContainerName).append(" "); + command.append("-e host=").append(host).append(" "); + command.append("-e port=").append(port); switch (deviceCredentials.getCredentialsType()) { case ACCESS_TOKEN: @@ -146,7 +148,7 @@ public class DeviceConnectivityUtil { } StringBuilder mqttDockerCommand = new StringBuilder(); - mqttDockerCommand.append(DOCKER_RUN).append(isLocalhost(host) ? "--network=host " : "").append(MQTT_IMAGE); + mqttDockerCommand.append(DOCKER_RUN).append(isLocalhost(host) ? NETWORK_HOST_PARAM : "").append(MQTT_IMAGE); if (MQTTS.equals(protocol)) { mqttDockerCommand.append("/bin/sh -c \"") @@ -178,7 +180,7 @@ public class DeviceConnectivityUtil { public static String getDockerCoapPublishCommand(String protocol, String host, String port, DeviceCredentials deviceCredentials) { String coapCommand = getCoapPublishCommand(protocol, host, port, deviceCredentials); - return coapCommand != null ? String.format("%s%s%s", DOCKER_RUN + (isLocalhost(host) ? "--network=host " : ""), COAP_IMAGE, coapCommand) : null; + return coapCommand != null ? String.format("%s%s%s", DOCKER_RUN + (isLocalhost(host) ? NETWORK_HOST_PARAM : ""), COAP_IMAGE, coapCommand) : null; } public static String getHost(String baseUrl, DeviceConnectivityInfo properties, String protocol) throws URISyntaxException { @@ -199,7 +201,7 @@ public class DeviceConnectivityUtil { } if (inetAddress instanceof Inet6Address) { host = host.replaceAll("[\\[\\]]", ""); - if (!protocol.startsWith(MQTT)) { + if (!MQTT.equals(protocol) && !MQTTS.equals(protocol)) { host = "[" + host + "]"; } } From 276debbd5d9e11b6b70e9e9c6ce818f4fbbda3f7 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Tue, 21 Nov 2023 11:27:37 +0200 Subject: [PATCH 178/252] UI: Image Gallery component. --- .../server/controller/ImageController.java | 12 +- .../server/dao/util/ImageUtils.java | 53 +- ui-ngx/src/app/core/http/image.service.ts | 5 +- ui-ngx/src/app/core/services/menu.service.ts | 7 + .../components/grid/scroll-grid.component.ts | 4 + .../home/components/home-components.module.ts | 10 +- .../image/image-gallery.component.html | 269 +++++++++ .../image/image-gallery.component.scss | 182 ++++++ .../image/image-gallery.component.ts | 545 ++++++++++++++++++ .../image/upload-image-dialog.component.html | 61 ++ .../image/upload-image-dialog.component.ts | 90 +++ .../datasource/scroll-grid-datasource.ts | 4 + .../home/pages/admin/admin-routing.module.ts | 20 + .../components/image-input.component.ts | 38 +- .../assets/locale/locale.constant-en_US.json | 28 +- 15 files changed, 1310 insertions(+), 18 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/components/image/image-gallery.component.html create mode 100644 ui-ngx/src/app/modules/home/components/image/image-gallery.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/image/image-gallery.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/image/upload-image-dialog.component.html create mode 100644 ui-ngx/src/app/modules/home/components/image/upload-image-dialog.component.ts diff --git a/application/src/main/java/org/thingsboard/server/controller/ImageController.java b/application/src/main/java/org/thingsboard/server/controller/ImageController.java index a027e77b2e..0d5630c2c1 100644 --- a/application/src/main/java/org/thingsboard/server/controller/ImageController.java +++ b/application/src/main/java/org/thingsboard/server/controller/ImageController.java @@ -54,9 +54,6 @@ import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; -import java.time.Duration; -import java.util.function.Supplier; - import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.RESOURCE_SORT_PROPERTY_ALLOWABLE_VALUES; @@ -80,14 +77,19 @@ public class ImageController extends BaseController { @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @PostMapping("/api/image") - public TbResourceInfo uploadImage(@RequestPart MultipartFile file) throws Exception { + public TbResourceInfo uploadImage(@RequestPart MultipartFile file, + @RequestPart(required = false) String title) throws Exception { SecurityUser user = getCurrentUser(); TbResource image = new TbResource(); image.setTenantId(user.getTenantId()); accessControlService.checkPermission(user, Resource.TB_RESOURCE, Operation.CREATE, null, image); image.setFileName(file.getOriginalFilename()); - image.setTitle(file.getOriginalFilename()); + if (StringUtils.isNotEmpty(title)) { + image.setTitle(title); + } else { + image.setTitle(file.getOriginalFilename()); + } image.setResourceType(ResourceType.IMAGE); ImageDescriptor descriptor = new ImageDescriptor(); descriptor.setMediaType(file.getContentType()); diff --git a/dao/src/main/java/org/thingsboard/server/dao/util/ImageUtils.java b/dao/src/main/java/org/thingsboard/server/dao/util/ImageUtils.java index 6f312b3b72..ee10a403fd 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/util/ImageUtils.java +++ b/dao/src/main/java/org/thingsboard/server/dao/util/ImageUtils.java @@ -18,11 +18,21 @@ package org.thingsboard.server.dao.util; import lombok.AccessLevel; import lombok.Data; import lombok.NoArgsConstructor; +import org.apache.batik.anim.dom.SAXSVGDocumentFactory; +import org.apache.batik.bridge.BridgeContext; +import org.apache.batik.bridge.DocumentLoader; +import org.apache.batik.bridge.GVTBuilder; +import org.apache.batik.bridge.UserAgent; +import org.apache.batik.bridge.UserAgentAdapter; +import org.apache.batik.gvt.GraphicsNode; import org.apache.batik.transcoder.TranscoderInput; import org.apache.batik.transcoder.TranscoderOutput; import org.apache.batik.transcoder.image.PNGTranscoder; +import org.apache.batik.util.XMLResourceDescriptor; import org.springframework.util.MimeType; import org.springframework.util.MimeTypeUtils; +import org.thingsboard.server.common.data.StringUtils; +import org.w3c.dom.Document; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; @@ -52,7 +62,7 @@ public class ImageUtils { public static ProcessedImage processImage(byte[] data, String mediaType, int thumbnailMaxDimension) throws Exception { if (mediaTypeToFileExtension(mediaType).equals("svg")) { - return processSvgImage(data, thumbnailMaxDimension); + return processSvgImage(data, mediaType, thumbnailMaxDimension); } BufferedImage bufferedImage = ImageIO.read(new ByteArrayInputStream(data)); @@ -90,10 +100,45 @@ public class ImageUtils { return image; } - public static ProcessedImage processSvgImage(byte[] data, int thumbnailMaxDimension) throws Exception { + public static ProcessedImage processSvgImage(byte[] data, String mediaType, int thumbnailMaxDimension) throws Exception { + SAXSVGDocumentFactory factory = new SAXSVGDocumentFactory( + XMLResourceDescriptor.getXMLParserClassName()); + Document document = factory.createDocument( + null, new ByteArrayInputStream(data)); + Integer width = null; + Integer height = null; + String strWidth = document.getDocumentElement().getAttribute("width"); + String strHeight = document.getDocumentElement().getAttribute("height"); + if (StringUtils.isNotEmpty(strWidth) && StringUtils.isNotEmpty(strHeight)) { + width = Integer.parseInt(strWidth); + height = Integer.parseInt(strHeight); + } else { + String viewBox = document.getDocumentElement().getAttribute("viewBox"); + if (StringUtils.isNotEmpty(viewBox)) { + String[] viewBoxValues = viewBox.split(" "); + if (viewBoxValues.length > 3) { + width = Integer.parseInt(viewBoxValues[2]); + height = Integer.parseInt(viewBoxValues[3]); + } + } + } + if (width == null) { + UserAgent agent = new UserAgentAdapter(); + DocumentLoader loader= new DocumentLoader(agent); + BridgeContext context = new BridgeContext(agent, loader); + context.setDynamic(true); + GVTBuilder builder= new GVTBuilder(); + GraphicsNode root = builder.build(context, document); + var bounds = root.getPrimitiveBounds(); + if (bounds != null) { + width = (int) bounds.getWidth(); + height = (int) bounds.getHeight(); + } + } ProcessedImage image = new ProcessedImage(); - image.setWidth(0); - image.setHeight(0); + image.setMediaType(mediaType); + image.setWidth(width == null ? 0 : width); + image.setHeight(height == null ? 0 : height); image.setData(data); image.setSize(data.length); diff --git a/ui-ngx/src/app/core/http/image.service.ts b/ui-ngx/src/app/core/http/image.service.ts index f52f55eb27..699582eb38 100644 --- a/ui-ngx/src/app/core/http/image.service.ts +++ b/ui-ngx/src/app/core/http/image.service.ts @@ -41,12 +41,13 @@ export class ImageService { ) { } - public uploadImage(file: File, config?: RequestConfig): Observable { + public uploadImage(file: File, title: string, config?: RequestConfig): Observable { if (!config) { config = {}; } const formData = new FormData(); formData.append('file', file); + formData.append('title', title); return this.http.post('/api/image', formData, defaultHttpUploadOptions(config.ignoreLoading, config.ignoreErrors, config.resendRequest)); } @@ -133,7 +134,7 @@ export class ImageService { } public deleteImage(type: ImageResourceType, key: string, config?: RequestConfig) { - return this.http.delete(`${IMAGES_URL_PREFIX}${type}/${encodeURIComponent}`, defaultHttpOptionsFromConfig(config)); + return this.http.delete(`${IMAGES_URL_PREFIX}/${type}/${encodeURIComponent(key)}`, defaultHttpOptionsFromConfig(config)); } } diff --git a/ui-ngx/src/app/core/services/menu.service.ts b/ui-ngx/src/app/core/services/menu.service.ts index d48b0619ca..aa622af51b 100644 --- a/ui-ngx/src/app/core/services/menu.service.ts +++ b/ui-ngx/src/app/core/services/menu.service.ts @@ -143,6 +143,13 @@ export class MenuService { } ] }, + { + id: 'images', + name: 'image.gallery', + type: 'link', + path: '/resources/images', + icon: 'filter' + }, { id: 'resources_library', name: 'resource.resources-library', diff --git a/ui-ngx/src/app/modules/home/components/grid/scroll-grid.component.ts b/ui-ngx/src/app/modules/home/components/grid/scroll-grid.component.ts index 7044fc040e..b2b6e79fe9 100644 --- a/ui-ngx/src/app/modules/home/components/grid/scroll-grid.component.ts +++ b/ui-ngx/src/app/modules/home/components/grid/scroll-grid.component.ts @@ -108,4 +108,8 @@ export class ScrollGridComponent implements OnInit, AfterViewInit, OnChang trackByItem(index: number, item: T): T { return item; } + + public update() { + this.dataSource.update(); + } } diff --git a/ui-ngx/src/app/modules/home/components/home-components.module.ts b/ui-ngx/src/app/modules/home/components/home-components.module.ts index 46e9f15275..4e3a52e9f1 100644 --- a/ui-ngx/src/app/modules/home/components/home-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/home-components.module.ts @@ -182,6 +182,8 @@ import { ExportWidgetsBundleDialogComponent } from '@home/components/import-export/export-widgets-bundle-dialog.component'; import { ScrollGridComponent } from '@home/components/grid/scroll-grid.component'; +import { ImageGalleryComponent } from '@home/components/image/image-gallery.component'; +import { UploadImageDialogComponent } from '@home/components/image/upload-image-dialog.component'; @NgModule({ declarations: @@ -327,7 +329,9 @@ import { ScrollGridComponent } from '@home/components/grid/scroll-grid.component RateLimitsTextComponent, RateLimitsDetailsDialogComponent, SendNotificationButtonComponent, - ScrollGridComponent + ScrollGridComponent, + ImageGalleryComponent, + UploadImageDialogComponent ], imports: [ CommonModule, @@ -466,7 +470,9 @@ import { ScrollGridComponent } from '@home/components/grid/scroll-grid.component RateLimitsTextComponent, RateLimitsDetailsDialogComponent, SendNotificationButtonComponent, - ScrollGridComponent + ScrollGridComponent, + ImageGalleryComponent, + UploadImageDialogComponent ], providers: [ WidgetComponentService, diff --git a/ui-ngx/src/app/modules/home/components/image/image-gallery.component.html b/ui-ngx/src/app/modules/home/components/image/image-gallery.component.html new file mode 100644 index 0000000000..ebb4a40507 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/image/image-gallery.component.html @@ -0,0 +1,269 @@ + +
+
+ +
+ image.gallery +
+
+ +
+
+ +
+
+
+
+ +
+ + + +
+ +
+
+ +
+ + +   + + + +
+
+ +
+ + {{ translate.get('image.selected-images', {count: dataSource.selection.selected.length}) | async }} + + + +
+
+
+
+ + + + + + + + + + + + + + + {{ image.title }} + + + + {{ 'image.name' | translate }} + + {{ image.title }} + + + + {{ 'image.created-time' | translate }} + + {{ image.createdTime | date:'yyyy-MM-dd HH:mm:ss' }} + + + + {{ 'image.resolution' | translate }} + + {{ image.descriptor.width }}x{{ image.descriptor.height }} + + + + {{ 'image.size' | translate }} + + {{ image.descriptor.size | fileSize }} + + + + {{ 'image.system' | translate }} + + {{isSystem(image) ? 'check_box' : 'check_box_outline_blank'}} + + + + + + +
+ + + + +
+
+
+ + +
+ + + + {{ 'common.loading' | translate }} +
+ + +
+
+ + +
+
+
+ +
+ + {{ 'common.loading' | translate }} + + +
+
+ +
+
+
image.no-images
+
+
+ +
+
+
+ {{ item.title }} +
+
+
+
+ {{ item.title }} +
+
sys
+
+
+
{{ item.descriptor.width }}x{{ item.descriptor.height }}
+ +
{{ item.descriptor.size | fileSize }}
+
+
+
+
+ +
TODO
+
diff --git a/ui-ngx/src/app/modules/home/components/image/image-gallery.component.scss b/ui-ngx/src/app/modules/home/components/image/image-gallery.component.scss new file mode 100644 index 0000000000..fe48ba197f --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/image/image-gallery.component.scss @@ -0,0 +1,182 @@ +/** + * Copyright © 2016-2023 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. + */ +$tb-button-selected-color: rgb(255, 110, 64) !default; + +.tb-images { + .tb-images-content { + width: 100%; + height: 100%; + background: #fff; + overflow: hidden; + + &.tb-outlined-border { + box-shadow: 0 0 0 0 rgb(0 0 0 / 20%), 0 0 0 0 rgb(0 0 0 / 14%), 0 0 0 0 rgb(0 0 0 / 12%); + border: solid 1px #e0e0e0; + border-radius: 4px; + } + + .tb-images-title { + padding-right: 20px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .tb-images-view-type-toolbar { + height: 55px; + min-height: 55px; + padding-right: 16px; + background-color: transparent; + box-shadow: none; + + .tb-toolbar-button { + height: 48px; + button.mat-mdc-icon-button { + margin: 0; + } + &.tb-selected { + background-color: rgba(255, 255, 255, .15); + border-bottom: $tb-button-selected-color solid 4px; + + button.mat-mdc-icon-button { + margin-bottom: -4px; + + .mat-icon { + color: $tb-button-selected-color; + fill: $tb-button-selected-color; + } + } + } + } + } + + .table-container, tb-scroll-grid { + position: relative; + } + + .tb-no-images { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + } + + .tb-image-card { + height: 100%; + border-radius: 8px; + padding: 8px; + display: flex; + gap: 8px; + flex-direction: column; + cursor: pointer; + transition: box-shadow 0.2s; + + .tb-image-preview-container { + position: relative; + .tb-image-preview-spacer { + margin-top: 100%; + } + .tb-image-preview { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + } + } + + .tb-image-details { + flex: 1; + display: flex; + flex-direction: column; + align-items: flex-start; + justify-content: space-between; + gap: 4px; + align-self: stretch; + .tb-image-title-container { + display: flex; + align-items: center; + gap: 8px; + align-self: stretch; + .tb-image-title { + flex: 1; + overflow: hidden; + text-overflow: ellipsis; + -webkit-line-clamp: 2; + display: -webkit-box; + -webkit-box-orient: vertical; + color: rgba(0, 0, 0, 0.76); + font-size: 12px; + font-style: normal; + font-weight: 500; + line-height: 16px; + } + .tb-image-sys { + padding: 1px 4px; + border-radius: 4px; + background: rgba(236, 236, 236, 0.64); + color: rgba(0, 0, 0, 0.54); + font-size: 11px; + font-style: normal; + font-weight: 500; + line-height: 16px; + letter-spacing: 0.017px; + } + } + .tb-image-info-container { + display: flex; + align-items: center; + gap: 8px; + align-self: stretch; + color: rgba(0, 0, 0, 0.38); + font-size: 10px; + font-style: normal; + font-weight: 400; + line-height: 16px; + .mat-divider.mat-divider-vertical { + height: 16px; + } + } + } + + &:hover { + box-shadow: 0 0 28px 0 rgba(0, 0, 0, 0.24); + } + } + + .table-container { + overflow: auto; + + .mat-sort-header-sorted .mat-sort-header-arrow { + opacity: 1 !important; + } + .mat-mdc-cell.mat-column-preview { + width: 50px; + height: 50px; + padding: 2px 12px; + display: block; + } + } + + .tb-image-preview { + width: 50px; + height: 50px; + object-fit: contain; + border-radius: 4px; + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/image/image-gallery.component.ts b/ui-ngx/src/app/modules/home/components/image/image-gallery.component.ts new file mode 100644 index 0000000000..6b1b931d7a --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/image/image-gallery.component.ts @@ -0,0 +1,545 @@ +/// +/// Copyright © 2016-2023 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 { CollectionViewer, DataSource, SelectionModel } from '@angular/cdk/collections'; +import { ImageResourceInfo, imageResourceType } from '@shared/models/resource.models'; +import { BehaviorSubject, forkJoin, merge, Observable, of, ReplaySubject, Subject, Subscription } from 'rxjs'; +import { emptyPageData, PageData } from '@shared/models/page/page-data'; +import { ImageService } from '@core/http/image.service'; +import { TranslateService } from '@ngx-translate/core'; +import { PageLink, PageQueryParam } from '@shared/models/page/page-link'; +import { catchError, debounceTime, distinctUntilChanged, map, skip, takeUntil, tap } from 'rxjs/operators'; +import { + AfterViewInit, + ChangeDetectorRef, + Component, + ElementRef, + Input, + OnDestroy, + OnInit, + ViewChild, + ViewEncapsulation +} from '@angular/core'; +import { PageComponent } from '@shared/components/page.component'; +import { MatPaginator } from '@angular/material/paginator'; +import { MatSort, SortDirection } from '@angular/material/sort'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { UtilsService } from '@core/services/utils.service'; +import { DialogService } from '@core/services/dialog.service'; +import { FormBuilder } from '@angular/forms'; +import { Direction, SortOrder } from '@shared/models/page/sort-order'; +import { ResizeObserver } from '@juggle/resize-observer'; +import { hidePageSizePixelValue } from '@shared/models/constants'; +import { coerceBoolean } from '@shared/decorators/coercion'; +import { ActivatedRoute, QueryParamsHandling, Router } from '@angular/router'; +import { isEqual, isNotEmptyStr } from '@core/utils'; +import { BaseData, HasId } from '@shared/models/base-data'; +import { NULL_UUID } from '@shared/models/id/has-uuid'; +import { getCurrentAuthUser } from '@core/auth/auth.selectors'; +import { Authority } from '@shared/models/authority.enum'; +import { GridEntitiesFetchFunction, ScrollGridColumns } from '@home/models/datasource/scroll-grid-datasource'; +import { WidgetsBundle } from '@shared/models/widgets-bundle.model'; +import { ScrollGridComponent } from '@home/components/grid/scroll-grid.component'; +import { + AddWidgetDialogComponent, + AddWidgetDialogData +} from '@home/components/dashboard-page/add-widget-dialog.component'; +import { Widget } from '@shared/models/widget.models'; +import { MatDialog } from '@angular/material/dialog'; +import { UploadImageDialogComponent } from '@home/components/image/upload-image-dialog.component'; + +@Component({ + selector: 'tb-image-gallery', + templateUrl: './image-gallery.component.html', + styleUrls: ['./image-gallery.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class ImageGalleryComponent extends PageComponent implements OnInit, OnDestroy, AfterViewInit { + + @Input() + @coerceBoolean() + pageMode = true; + + @Input() + mode: 'list' | 'grid' = 'list'; + + @ViewChild('searchInput') searchInputField: ElementRef; + + @ViewChild(MatPaginator) paginator: MatPaginator; + @ViewChild(MatSort) sort: MatSort; + + @ViewChild(ScrollGridComponent) gridComponent: ScrollGridComponent; + + defaultPageSize = 10; + defaultSortOrder: SortOrder = { property: 'createdTime', direction: Direction.DESC }; + hidePageSize = false; + + displayedColumns: string[]; + pageSizeOptions: number[]; + pageLink: PageLink; + + textSearchMode = false; + + dataSource: ImagesDatasource; + + textSearch = this.fb.control('', {nonNullable: true}); + + gridColumns: ScrollGridColumns = { + columns: 2, + breakpoints: { + 'screen and (min-width: 2320px)': 10, + 'screen and (min-width: 2000px)': 8, + 'gt-lg': 7, + 'screen and (min-width: 1600px)': 6, + 'gt-md': 5, + 'screen and (min-width: 1120px)': 4, + 'gt-xs': 3 + } + }; + + gridImagesFetchFunction: GridEntitiesFetchFunction; + gridImagesFilter = ''; + + authUser = getCurrentAuthUser(this.store); + + private updateDataSubscription: Subscription; + + private widgetResize$: ResizeObserver; + private destroy$ = new Subject(); + private destroyListMode$: Subject; + + constructor(protected store: Store, + private route: ActivatedRoute, + private router: Router, + private dialog: MatDialog, + public translate: TranslateService, + private imageService: ImageService, + private dialogService: DialogService, + private elementRef: ElementRef, + private cd: ChangeDetectorRef, + private fb: FormBuilder) { + super(store); + + this.gridImagesFetchFunction = (pageSize, page, filter) => { + const pageLink = new PageLink(pageSize, page, filter, { + property: 'createdTime', + direction: Direction.DESC + }); + return this.imageService.getImages(pageLink); + }; + } + + ngOnInit(): void { + this.displayedColumns = ['select', 'preview', 'title', 'createdTime', 'resolution', 'size', 'system', 'actions']; + let sortOrder: SortOrder = this.defaultSortOrder; + this.pageSizeOptions = [this.defaultPageSize, this.defaultPageSize * 2, this.defaultPageSize * 3]; + const routerQueryParams: PageQueryParam = this.route.snapshot.queryParams; + if (this.pageMode) { + if (routerQueryParams.hasOwnProperty('direction') + || routerQueryParams.hasOwnProperty('property')) { + sortOrder = { + property: routerQueryParams?.property || this.defaultSortOrder.property, + direction: routerQueryParams?.direction || this.defaultSortOrder.direction + }; + } + } + this.pageLink = new PageLink(this.defaultPageSize, 0, null, sortOrder); + if (this.pageMode) { + if (routerQueryParams.hasOwnProperty('page')) { + this.pageLink.page = Number(routerQueryParams.page); + } + if (routerQueryParams.hasOwnProperty('pageSize')) { + this.pageLink.pageSize = Number(routerQueryParams.pageSize); + } + const textSearchParam = routerQueryParams.textSearch; + if (isNotEmptyStr(textSearchParam)) { + const decodedTextSearch = decodeURI(textSearchParam); + this.textSearchMode = true; + this.pageLink.textSearch = decodedTextSearch.trim(); + this.textSearch.setValue(decodedTextSearch, {emitEvent: false}); + } + } + if (this.mode === 'list') { + this.dataSource = new ImagesDatasource(this.imageService); + } + } + + ngOnDestroy(): void { + if (this.widgetResize$) { + this.widgetResize$.disconnect(); + } + if (this.destroyListMode$) { + this.destroyListMode$.next(); + this.destroyListMode$.complete(); + } + this.destroy$.next(); + this.destroy$.complete(); + } + + ngAfterViewInit() { + this.textSearch.valueChanges.pipe( + debounceTime(150), + distinctUntilChanged((prev, current) => + ((this.mode === 'list' ? this.pageLink.textSearch : this.gridImagesFilter) ?? '') === current.trim()), + takeUntil(this.destroy$) + ).subscribe(value => { + if (this.mode === 'list') { + if (this.pageMode) { + const queryParams: PageQueryParam = { + textSearch: isNotEmptyStr(value) ? encodeURI(value) : null, + page: null + }; + this.updatedRouterParamsAndData(queryParams); + } else { + this.pageLink.textSearch = isNotEmptyStr(value) ? value.trim() : null; + this.paginator.pageIndex = 0; + this.updateData(); + } + } else { + this.gridImagesFilter = isNotEmptyStr(value) ? value.trim() : null; + this.cd.markForCheck(); + } + }); + this.updateMode(); + } + + public setMode(targetMode: 'list' | 'grid') { + if (this.mode !== targetMode) { + if (this.widgetResize$) { + this.widgetResize$.disconnect(); + this.widgetResize$ = null; + } + if (this.destroyListMode$) { + this.destroyListMode$.next(); + this.destroyListMode$.complete(); + this.destroyListMode$ = null; + } + this.mode = targetMode; + if (this.mode === 'list') { + this.dataSource = new ImagesDatasource(this.imageService); + } + setTimeout(() => { + this.updateMode(); + }); + } + } + + private updateMode() { + if (this.mode === 'list') { + this.initListMode(); + } else { + this.initGridMode(); + } + } + + private initListMode() { + this.destroyListMode$ = new Subject(); + this.widgetResize$ = new ResizeObserver(() => { + const showHidePageSize = this.elementRef.nativeElement.offsetWidth < hidePageSizePixelValue; + if (showHidePageSize !== this.hidePageSize) { + this.hidePageSize = showHidePageSize; + this.cd.markForCheck(); + } + }); + this.widgetResize$.observe(this.elementRef.nativeElement); + if (this.pageMode) { + this.route.queryParams.pipe( + skip(1), + takeUntil(this.destroyListMode$) + ).subscribe((params: PageQueryParam) => { + this.paginator.pageIndex = Number(params.page) || 0; + this.paginator.pageSize = Number(params.pageSize) || this.defaultPageSize; + this.sort.active = params.property || this.defaultSortOrder.property; + this.sort.direction = (params.direction || this.defaultSortOrder.direction).toLowerCase() as SortDirection; + const textSearchParam = params.textSearch; + if (isNotEmptyStr(textSearchParam)) { + const decodedTextSearch = decodeURI(textSearchParam); + this.textSearchMode = true; + this.pageLink.textSearch = decodedTextSearch.trim(); + this.textSearch.setValue(decodedTextSearch, {emitEvent: false}); + } else { + this.pageLink.textSearch = null; + this.textSearch.reset('', {emitEvent: false}); + } + this.updateData(); + }); + } + this.updatePaginationSubscriptions(); + this.updateData(); + } + + private initGridMode() { + + } + + private updatePaginationSubscriptions() { + if (this.updateDataSubscription) { + this.updateDataSubscription.unsubscribe(); + this.updateDataSubscription = null; + } + const sortSubscription$: Observable = this.sort.sortChange.asObservable().pipe( + map((data) => { + const direction = data.direction.toUpperCase(); + const queryParams: PageQueryParam = { + direction: (this.defaultSortOrder.direction === direction ? null : direction) as Direction, + property: this.defaultSortOrder.property === data.active ? null : data.active + }; + queryParams.page = null; + this.paginator.pageIndex = 0; + return queryParams; + }) + ); + const paginatorSubscription$ = this.paginator.page.asObservable().pipe( + map((data) => ({ + page: data.pageIndex === 0 ? null : data.pageIndex, + pageSize: data.pageSize === this.defaultPageSize ? null : data.pageSize + })) + ); + this.updateDataSubscription = (merge(sortSubscription$, paginatorSubscription$) as Observable).pipe( + takeUntil(this.destroyListMode$) + ).subscribe(queryParams => this.updatedRouterParamsAndData(queryParams)); + } + + clearSelection() { + this.dataSource.selection.clear(); + this.cd.detectChanges(); + } + + updateData() { + if (this.mode === 'list') { + this.pageLink.page = this.paginator.pageIndex; + this.pageLink.pageSize = this.paginator.pageSize; + if (this.sort.active) { + this.pageLink.sortOrder = { + property: this.sort.active, + direction: Direction[this.sort.direction.toUpperCase()] + }; + } else { + this.pageLink.sortOrder = null; + } + this.dataSource.loadEntities(this.pageLink); + } else { + this.gridComponent.update(); + } + } + + enterFilterMode() { + this.textSearchMode = true; + setTimeout(() => { + this.searchInputField.nativeElement.focus(); + this.searchInputField.nativeElement.setSelectionRange(0, 0); + }, 10); + } + + exitFilterMode() { + this.textSearchMode = false; + this.textSearch.reset(); + } + + trackByEntityId(index: number, entity: BaseData) { + return entity.id.id; + } + + isSystem(image?: ImageResourceInfo): boolean { + return image?.tenantId?.id === NULL_UUID; + } + + deleteEnabled(image?: ImageResourceInfo): boolean { + return this.authUser.authority === Authority.SYS_ADMIN || !this.isSystem(image); + } + + deleteImage($event: Event, image: ImageResourceInfo) { + if ($event) { + $event.stopPropagation(); + } + const title = this.translate.instant('image.delete-image-title', {imageTitle: image.title}); + const content = this.translate.instant('image.delete-image-text'); + this.dialogService.confirm(title, content, + this.translate.instant('action.no'), + this.translate.instant('action.yes')).subscribe((result) => { + if (result) { + this.imageService.deleteImage(imageResourceType(image), image.resourceKey).subscribe( + () => { + this.updateData(); + } + ); + } + }); + } + + deleteImages($event: Event) { + if ($event) { + $event.stopPropagation(); + } + const selectedImages = this.dataSource.selection.selected; + if (selectedImages && selectedImages.length) { + const title = this.translate.instant('image.delete-images-title', {count: selectedImages.length}); + const content = this.translate.instant('image.delete-images-text'); + this.dialogService.confirm(title, content, + this.translate.instant('action.no'), + this.translate.instant('action.yes')).subscribe((result) => { + if (result) { + const tasks = selectedImages.map((image) => + this.imageService.deleteImage(imageResourceType(image), image.resourceKey)); + forkJoin(tasks).subscribe( + () => { + this.updateData(); + } + ); + } + }); + } + } + + downloadImage($event, image: ImageResourceInfo) { + if ($event) { + $event.stopPropagation(); + } + this.imageService.downloadImage(imageResourceType(image), image.resourceKey).subscribe(); + } + + exportImage($event, image: ImageResourceInfo) { + if ($event) { + $event.stopPropagation(); + } + // TODO: + } + + importImage(): void { + // TODO: + } + + uploadImage(): void { + this.dialog.open(UploadImageDialogComponent, { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'] + }).afterClosed().subscribe((result) => { + if (result) { + this.updateData(); + } + }); + } + + editImage($event, image: ImageResourceInfo) { + if ($event) { + $event.stopPropagation(); + } + // TODO: + } + + protected updatedRouterParamsAndData(queryParams: object, queryParamsHandling: QueryParamsHandling = 'merge') { + if (this.pageMode) { + this.router.navigate([], { + relativeTo: this.route, + queryParams, + queryParamsHandling + }); + if (queryParamsHandling === '' && isEqual(this.route.snapshot.queryParams, queryParams)) { + this.updateData(); + } + } else { + this.updateData(); + } + } + +} + +class ImagesDatasource implements DataSource { + private entitiesSubject = new BehaviorSubject([]); + private pageDataSubject = new BehaviorSubject>(emptyPageData()); + + public pageData$ = this.pageDataSubject.asObservable(); + + public selection = new SelectionModel(true, []); + + public dataLoading = true; + + constructor(private imageService: ImageService) { + } + + connect(collectionViewer: CollectionViewer): + Observable> { + return this.entitiesSubject.asObservable(); + } + + disconnect(collectionViewer: CollectionViewer): void { + this.entitiesSubject.complete(); + this.pageDataSubject.complete(); + } + + reset() { + const pageData = emptyPageData(); + this.entitiesSubject.next(pageData.data); + this.pageDataSubject.next(pageData); + } + + loadEntities(pageLink: PageLink): Observable> { + this.dataLoading = true; + const result = new ReplaySubject>(); + this.fetchEntities(pageLink).pipe( + tap(() => { + this.selection.clear(); + }), + catchError(() => of(emptyPageData())), + ).subscribe( + (pageData) => { + this.entitiesSubject.next(pageData.data); + this.pageDataSubject.next(pageData); + result.next(pageData); + this.dataLoading = false; + } + ); + return result; + } + + fetchEntities(pageLink: PageLink): Observable> { + return this.imageService.getImages(pageLink); + } + + isAllSelected(): Observable { + const numSelected = this.selection.selected.length; + return this.entitiesSubject.pipe( + map((entities) => numSelected === entities.length) + ); + } + + isEmpty(): Observable { + return this.entitiesSubject.pipe( + map((entities) => !entities.length) + ); + } + + total(): Observable { + return this.pageDataSubject.pipe( + map((pageData) => pageData.totalElements) + ); + } + + masterToggle() { + const entities = this.entitiesSubject.getValue(); + const numSelected = this.selection.selected.length; + if (numSelected === entities.length) { + this.selection.clear(); + } else { + entities.forEach(row => { + this.selection.select(row); + }); + } + } + +} diff --git a/ui-ngx/src/app/modules/home/components/image/upload-image-dialog.component.html b/ui-ngx/src/app/modules/home/components/image/upload-image-dialog.component.html new file mode 100644 index 0000000000..91a668daab --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/image/upload-image-dialog.component.html @@ -0,0 +1,61 @@ + +
+ +

{{ 'image.upload-image' | translate }}

+ + +
+ + +
+
+
+ + + + image.name + + + {{ 'image.name-required' | translate }} + + +
+
+
+ + +
+
diff --git a/ui-ngx/src/app/modules/home/components/image/upload-image-dialog.component.ts b/ui-ngx/src/app/modules/home/components/image/upload-image-dialog.component.ts new file mode 100644 index 0000000000..46a2843f7a --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/image/upload-image-dialog.component.ts @@ -0,0 +1,90 @@ +/// +/// Copyright © 2016-2023 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, OnInit, SkipSelf } from '@angular/core'; +import { ErrorStateMatcher } from '@angular/material/core'; +import { MatDialogRef } from '@angular/material/dialog'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { + FormGroupDirective, + NgForm, + UntypedFormBuilder, + UntypedFormControl, + UntypedFormGroup, + Validators +} from '@angular/forms'; +import { DialogComponent } from '@shared/components/dialog.component'; +import { Router } from '@angular/router'; +import { ImageService } from '@core/http/image.service'; + +@Component({ + selector: 'tb-upload-image-dialog', + templateUrl: './upload-image-dialog.component.html', + providers: [{provide: ErrorStateMatcher, useExisting: UploadImageDialogComponent}], + styleUrls: [] +}) +export class UploadImageDialogComponent extends + DialogComponent implements OnInit, ErrorStateMatcher { + + uploadImageFormGroup: UntypedFormGroup; + + submitted = false; + + constructor(protected store: Store, + protected router: Router, + private imageService: ImageService, + @SkipSelf() private errorStateMatcher: ErrorStateMatcher, + public dialogRef: MatDialogRef, + public fb: UntypedFormBuilder) { + super(store, router, dialogRef); + } + + ngOnInit(): void { + this.uploadImageFormGroup = this.fb.group({ + file: [null, [Validators.required]], + title: [null, [Validators.required]] + }); + } + + imageFileNameChanged(fileName: string) { + const titleControl = this.uploadImageFormGroup.get('title'); + if (!titleControl.value || !titleControl.touched) { + titleControl.setValue(fileName); + } + } + + isErrorState(control: UntypedFormControl | null, form: FormGroupDirective | NgForm | null): boolean { + const originalErrorState = this.errorStateMatcher.isErrorState(control, form); + const customErrorState = !!(control && control.invalid && this.submitted); + return originalErrorState || customErrorState; + } + + cancel(): void { + this.dialogRef.close(false); + } + + upload(): void { + this.submitted = true; + const file: File = this.uploadImageFormGroup.get('file').value; + const title: string = this.uploadImageFormGroup.get('title').value; + this.imageService.uploadImage(file, title).subscribe( + () => { + this.dialogRef.close(true); + } + ); + } +} diff --git a/ui-ngx/src/app/modules/home/models/datasource/scroll-grid-datasource.ts b/ui-ngx/src/app/modules/home/models/datasource/scroll-grid-datasource.ts index c653ed0121..83087eadc0 100644 --- a/ui-ngx/src/app/modules/home/models/datasource/scroll-grid-datasource.ts +++ b/ui-ngx/src/app/modules/home/models/datasource/scroll-grid-datasource.ts @@ -90,6 +90,10 @@ export class ScrollGridDatasource extends DataSource<(T | GridCellType)[]> public updateFilter(filter: F) { this.filter = filter; + this.update(); + } + + public update() { if (this.active) { const prevLength = this._rows.length; this._reset(); diff --git a/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts b/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts index 5d2727533c..7be45724e8 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts @@ -39,6 +39,7 @@ import { TwoFactorAuthSettingsComponent } from '@home/pages/admin/two-factor-aut import { widgetsLibraryRoutes } from '@home/pages/widget/widget-library-routing.module'; import { RouterTabsComponent } from '@home/components/router-tabs.component'; import { auditLogsRoutes } from '@home/pages/audit-log/audit-log-routing.module'; +import { ImageGalleryComponent } from '@home/components/image/image-gallery.component'; @Injectable() export class OAuth2LoginProcessingUrlResolver implements Resolve { @@ -71,6 +72,25 @@ const routes: Routes = [ } }, ...widgetsLibraryRoutes, + { + path: 'images', + data: { + breadcrumb: { + label: 'image.gallery', + icon: 'filter' + } + }, + children: [ + { + path: '', + component: ImageGalleryComponent, + data: { + auth: [Authority.TENANT_ADMIN, Authority.SYS_ADMIN], + title: 'image.gallery', + }, + } + ] + }, { path: 'resources-library', data: { diff --git a/ui-ngx/src/app/shared/components/image-input.component.ts b/ui-ngx/src/app/shared/components/image-input.component.ts index 026b64a413..8d41532405 100644 --- a/ui-ngx/src/app/shared/components/image-input.component.ts +++ b/ui-ngx/src/app/shared/components/image-input.component.ts @@ -14,7 +14,16 @@ /// limitations under the License. /// -import { AfterViewInit, ChangeDetectorRef, Component, forwardRef, Input, OnDestroy, ViewChild } from '@angular/core'; +import { + AfterViewInit, + ChangeDetectorRef, + Component, EventEmitter, + forwardRef, + Input, + OnDestroy, + Output, + ViewChild +} from '@angular/core'; import { PageComponent } from '@shared/components/page.component'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; @@ -27,6 +36,7 @@ import { UtilsService } from '@core/services/utils.service'; import { DialogService } from '@core/services/dialog.service'; import { TranslateService } from '@ngx-translate/core'; import { FileSizePipe } from '@shared/pipe/file-size.pipe'; +import { coerceBoolean } from '@shared/decorators/coercion'; @Component({ selector: 'tb-image-input', @@ -74,7 +84,16 @@ export class ImageInputComponent extends PageComponent implements AfterViewInit, @Input() inputId = this.utils.guid(); + @Input() + @coerceBoolean() + resultAsFile = false; + + @Output() + fileNameChanged = new EventEmitter(); + imageUrl: string; + file: File; + fileName: string; safeImageUrl: SafeUrl; @ViewChild('flow', {static: true}) @@ -97,7 +116,9 @@ export class ImageInputComponent extends PageComponent implements AfterViewInit, ngAfterViewInit() { this.autoUploadSubscription = this.flow.events$.subscribe(event => { if (event.type === 'fileAdded') { - const file = (event.event[0] as flowjs.FlowFile).file; + const flowFile = event.event[0] as flowjs.FlowFile; + const file = flowFile.file; + const fileName = flowFile.name; if (this.maxSizeByte && this.maxSizeByte < file.size) { this.dialog.alert( this.translate.instant('dashboard.cannot-upload-file'), @@ -108,10 +129,12 @@ export class ImageInputComponent extends PageComponent implements AfterViewInit, return false; } const reader = new FileReader(); - reader.onload = (loadEvent) => { + reader.onload = (_loadEvent) => { if (typeof reader.result === 'string' && reader.result.startsWith('data:image/')) { this.imageUrl = reader.result; this.safeImageUrl = this.sanitizer.bypassSecurityTrustUrl(this.imageUrl); + this.file = file; + this.fileName = fileName; this.updateModel(); } }; @@ -146,12 +169,19 @@ export class ImageInputComponent extends PageComponent implements AfterViewInit, private updateModel() { this.cd.markForCheck(); - this.propagateChange(this.imageUrl); + if (this.resultAsFile) { + this.propagateChange(this.file); + } else { + this.propagateChange(this.imageUrl); + } + this.fileNameChanged.emit(this.fileName); } clearImage() { this.imageUrl = null; this.safeImageUrl = null; + this.file = null; + this.fileName = null; this.updateModel(); } } 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 d043b7d86f..98e7334e58 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -77,7 +77,8 @@ "show-more": "Show more", "dont-show-again": "Do not show again", "see-documentation": "See documentation", - "clear": "Clear" + "clear": "Clear", + "upload": "Upload" }, "aggregation": { "aggregation": "Aggregation", @@ -2942,6 +2943,31 @@ "browse-file": "Browse file", "browse-files": "Browse files" }, + "image": { + "gallery": "Image gallery", + "search": "Search image", + "selected-images": "{ count, plural, =1 {1 image} other {# images} } selected", + "created-time": "Created time", + "name": "Name", + "name-required": "Name is required.", + "resolution": "Resolution", + "size": "Size", + "system": "System", + "download-image": "Download image", + "export-image": "Export image", + "import-image": "Import image", + "upload-image": "Upload image", + "edit-image": "Edit image", + "no-images": "No images found", + "delete-image": "Delete image", + "delete-image-title": "Are you sure you want to delete image '{{imageTitle}}'?", + "delete-image-text": "Be careful, after the confirmation image will become unrecoverable.", + "delete-images-title": "Are you sure you want to delete { count, plural, =1 {1 image} other {# images} }?", + "delete-images-text": "Be careful, after the confirmation all selected images will be removed and all related data will become unrecoverable.", + "list-mode": "List view", + "grid-mode": "Grid view", + "image-preview": "Image preview" + }, "image-input": { "drop-images-or": "Drag and drop an images or", "drag-and-drop": "Drag & Drop", From 31aa1d25e030243dd2599913396e3f148699d274 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Tue, 10 Oct 2023 16:22:57 +0200 Subject: [PATCH 179/252] added fst stats --- .../server/service/stats/FstStats.java | 29 +++++ .../src/main/resources/thingsboard.yml | 6 +- .../server/common/data/FSTUtils.java | 122 +++++++++++++++++- 3 files changed, 153 insertions(+), 4 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/stats/FstStats.java diff --git a/application/src/main/java/org/thingsboard/server/service/stats/FstStats.java b/application/src/main/java/org/thingsboard/server/service/stats/FstStats.java new file mode 100644 index 0000000000..d66b9fd138 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/stats/FstStats.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.stats; + +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.FSTUtils; + +@Service +public class FstStats { + + @Scheduled(initialDelayString = "${fst.stats.printInterval}", fixedDelayString = "${fst.stats.printInterval}") + public void printStats() { + FSTUtils.printStats(); + } +} diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index ecc89c84c5..bba7800a28 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -1680,4 +1680,8 @@ management: web: exposure: # Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics). - include: '${METRICS_ENDPOINTS_EXPOSE:info}' \ No newline at end of file + include: '${METRICS_ENDPOINTS_EXPOSE:info}' + +fst: + stats: + printInterval: "${FST_STATS_PRINT_INTERVAL:600000}" # 10 min by default \ No newline at end of file diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/FSTUtils.java b/common/data/src/main/java/org/thingsboard/server/common/data/FSTUtils.java index 83447d7a6b..e70c6f29c6 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/FSTUtils.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/FSTUtils.java @@ -15,21 +15,137 @@ */ package org.thingsboard.server.common.data; +import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.nustaq.serialization.FSTConfiguration; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.Collectors; + @Slf4j public class FSTUtils { public static final FSTConfiguration CONFIG = FSTConfiguration.createDefaultConfiguration(); + private static final ConcurrentHashMap STATS = new ConcurrentHashMap<>(); + @SuppressWarnings("unchecked") public static T decode(byte[] byteArray) { - return byteArray != null && byteArray.length > 0 ? (T) CONFIG.asObject(byteArray) : null; + long startTime = System.nanoTime(); + T result = byteArray != null && byteArray.length > 0 ? (T) CONFIG.asObject(byteArray) : null; + long endTime = System.nanoTime(); + + if (log.isDebugEnabled() && result != null) { + String className = result.getClass().getSimpleName(); + STATS.computeIfAbsent(className, k -> new Stats()).incrementDecode(endTime - startTime); + } + + return result; + } + + public static byte[] encode(T msg) { + long startTime = System.nanoTime(); + byte[] result = CONFIG.asByteArray(msg); + long endTime = System.nanoTime(); + + if (log.isDebugEnabled() && msg != null) { + String className = msg.getClass().getSimpleName(); + STATS.computeIfAbsent(className, k -> new Stats()).incrementEncode(endTime - startTime); + } + + return result; + } + + public static void printStats() { + if (log.isDebugEnabled()) { + List topDecode = STATS.entrySet().stream() + .filter(e -> e.getValue().getAvgDecodeTime() > 0) + .sorted((e1, e2) -> Long.compare(e2.getValue().getAvgDecodeTime(), e1.getValue().getAvgDecodeTime())) + .limit(5) + .map(Map.Entry::getKey) + .collect(Collectors.toList()); + + List topEncode = STATS.entrySet().stream() + .filter(e -> e.getValue().getAvgEncodeTime() > 0) + .sorted((e1, e2) -> Long.compare(e2.getValue().getAvgEncodeTime(), e1.getValue().getAvgEncodeTime())) + .limit(5) + .map(Map.Entry::getKey) + .collect(Collectors.toList()); + + List topDecodeCount = STATS.entrySet().stream() + .filter(e -> e.getValue().getDecodeCount().get() > 0) + .sorted((e1, e2) -> Long.compare(e2.getValue().getDecodeCount().get(), e1.getValue().getDecodeCount().get())) + .limit(5) + .map(Map.Entry::getKey) + .collect(Collectors.toList()); + + List topEncodeCount = STATS.entrySet().stream() + .filter(e -> e.getValue().getEncodeCount().get() > 0) + .sorted((e1, e2) -> Long.compare(e2.getValue().getEncodeCount().get(), e1.getValue().getEncodeCount().get())) + .limit(5) + .map(Map.Entry::getKey) + .collect(Collectors.toList()); + + for (Map.Entry entry : STATS.entrySet()) { + Stats stats = entry.getValue(); + if (stats.isNotEmpty()) { + log.debug("[FST stats] [{}] {}", entry.getKey(), stats); + stats.reset(); + } + } + + log.debug("[FST stats] Top 5 slowest 'decode' {}", topDecode); + log.debug("[FST stats] Top 5 slowest 'encode' {}", topEncode); + log.debug("[FST stats] Top 5 'decode' count {}", topDecodeCount); + log.debug("[FST stats] Top 5 'encode' count {}", topEncodeCount); + } } - public static byte[] encode(T msq) { - return CONFIG.asByteArray(msq); + @Data + private static class Stats { + private final AtomicLong encodeCount = new AtomicLong(); + private final AtomicLong decodeCount = new AtomicLong(); + private final AtomicLong totalEncodeTime = new AtomicLong(); + private final AtomicLong totalDecodeTime = new AtomicLong(); + + private void incrementEncode(long time) { + encodeCount.incrementAndGet(); + totalEncodeTime.addAndGet(time); + } + + private void incrementDecode(long time) { + decodeCount.incrementAndGet(); + totalDecodeTime.addAndGet(time); + } + + private boolean isNotEmpty() { + return encodeCount.get() > 0 || decodeCount.get() > 0; + } + + private long getAvgEncodeTime() { + long count = encodeCount.get(); + return count > 0 ? totalEncodeTime.get() / count : 0; + } + + private long getAvgDecodeTime() { + long count = decodeCount.get(); + return count > 0 ? totalDecodeTime.get() / count : 0; + } + + private void reset() { + encodeCount.set(0); + decodeCount.set(0); + totalEncodeTime.set(0); + totalDecodeTime.set(0); + } + + @Override + public String toString() { + return String.format("decodeCount [%d] avgDecodeTime [%d] encodedCount [%d] avgEncodeTime [%d]", decodeCount.get(), getAvgDecodeTime(), encodeCount.get(), getAvgEncodeTime()); + } } } From b61e6d878c7555f68e2c2a275baecc9e0227bbad Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Tue, 21 Nov 2023 12:03:07 +0200 Subject: [PATCH 180/252] Implement force delete of the image --- .../server/controller/ImageController.java | 11 +-- .../controller/WidgetTypeController.java | 1 + .../resource/DefaultTbImageService.java | 11 +-- .../service/resource/TbImageService.java | 3 +- .../server/dao/resource/ImageService.java | 3 +- .../server/dao/resource/ResourceService.java | 2 + .../common/data/TbImageDeleteResult.java | 32 +++++++++ .../server/dao/ImageContainerDao.java | 29 ++++++++ .../server/dao/asset/AssetDao.java | 2 + .../server/dao/asset/AssetProfileDao.java | 4 +- .../dao/dashboard/DashboardInfoDao.java | 7 +- .../server/dao/device/DeviceProfileDao.java | 4 +- .../server/dao/resource/BaseImageService.java | 69 ++++++++++++++++++- .../dao/resource/BaseResourceService.java | 10 ++- .../dao/sql/asset/AssetProfileRepository.java | 10 +++ .../dao/sql/asset/JpaAssetProfileDao.java | 12 ++++ .../dashboard/DashboardInfoRepository.java | 13 ++++ .../sql/dashboard/JpaDashboardInfoDao.java | 12 +++- .../sql/device/DeviceProfileRepository.java | 10 +++ .../dao/sql/device/JpaDeviceProfileDao.java | 13 ++++ .../dao/sql/widget/JpaWidgetTypeDao.java | 10 +++ .../dao/sql/widget/JpaWidgetsBundleDao.java | 10 +++ .../sql/widget/WidgetTypeInfoRepository.java | 13 ++++ .../sql/widget/WidgetsBundleRepository.java | 6 ++ .../server/dao/widget/WidgetTypeDao.java | 3 +- .../server/dao/widget/WidgetsBundleDao.java | 4 +- .../dao/sql/widget/JpaWidgetTypeDaoTest.java | 23 +++++++ 27 files changed, 307 insertions(+), 20 deletions(-) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/TbImageDeleteResult.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/ImageContainerDao.java diff --git a/application/src/main/java/org/thingsboard/server/controller/ImageController.java b/application/src/main/java/org/thingsboard/server/controller/ImageController.java index 1a1b4e3b46..25d04bec5f 100644 --- a/application/src/main/java/org/thingsboard/server/controller/ImageController.java +++ b/application/src/main/java/org/thingsboard/server/controller/ImageController.java @@ -50,14 +50,13 @@ import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.dao.resource.ImageService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.resource.ImageCacheKey; +import org.thingsboard.server.common.data.TbImageDeleteResult; import org.thingsboard.server.service.resource.TbImageService; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; -import java.time.Duration; import java.util.concurrent.TimeUnit; -import java.util.function.Supplier; import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION; @@ -174,10 +173,12 @@ public class ImageController extends BaseController { @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @DeleteMapping(IMAGE_URL) - public void deleteImage(@PathVariable String type, - @PathVariable String key) throws ThingsboardException { + public ResponseEntity deleteImage(@PathVariable String type, + @PathVariable String key, + @RequestParam(name = "force", required = false) boolean force) throws ThingsboardException { TbResourceInfo imageInfo = checkImageInfo(type, key, Operation.DELETE); - tbImageService.delete(imageInfo, getCurrentUser()); + TbImageDeleteResult result = tbImageService.delete(imageInfo, getCurrentUser(), force); + return (result.isSuccess() ? ResponseEntity.ok() : ResponseEntity.badRequest()).body(result); } private ResponseEntity downloadIfChanged(String type, String key, String etag, boolean preview) throws ThingsboardException, JsonProcessingException { diff --git a/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java b/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java index f90a196edf..8d475a211d 100644 --- a/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java +++ b/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java @@ -369,6 +369,7 @@ public class WidgetTypeController extends AutoCommitController { String typeFqn = fqn.substring(scopeQualifier.length() + 1); WidgetType widgetType = widgetTypeService.findWidgetTypeByTenantIdAndFqn(tenantId, typeFqn); checkNotNull(widgetType); + accessControlService.checkPermission(getCurrentUser(), Resource.WIDGET_TYPE, Operation.READ, widgetType.getId(), widgetType); return widgetType; } diff --git a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java index 0820965544..f24b11afd7 100644 --- a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java +++ b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java @@ -17,10 +17,10 @@ package org.thingsboard.server.service.resource; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; -import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.TbImageDeleteResult; import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.TbResourceInfo; import org.thingsboard.server.common.data.User; @@ -90,12 +90,15 @@ public class DefaultTbImageService extends AbstractTbEntityService implements Tb } @Override - public void delete(TbResourceInfo imageInfo, User user) { + public TbImageDeleteResult delete(TbResourceInfo imageInfo, User user, boolean force) { TenantId tenantId = imageInfo.getTenantId(); TbResourceId imageId = imageInfo.getId(); try { - imageService.deleteImage(tenantId, imageId); - notificationEntityService.logEntityAction(tenantId, imageId, imageInfo, ActionType.DELETED, user, imageId.toString()); + TbImageDeleteResult result = imageService.deleteImage(imageInfo, force); + if (result.isSuccess()) { + notificationEntityService.logEntityAction(tenantId, imageId, imageInfo, ActionType.DELETED, user, imageId.toString()); + } + return result; } catch (Exception e) { notificationEntityService.logEntityAction(tenantId, imageId, ActionType.DELETED, user, e, imageId.toString()); throw e; diff --git a/application/src/main/java/org/thingsboard/server/service/resource/TbImageService.java b/application/src/main/java/org/thingsboard/server/service/resource/TbImageService.java index 3db512528a..2e2f95e4c7 100644 --- a/application/src/main/java/org/thingsboard/server/service/resource/TbImageService.java +++ b/application/src/main/java/org/thingsboard/server/service/resource/TbImageService.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.service.resource; +import org.thingsboard.server.common.data.TbImageDeleteResult; import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.TbResourceInfo; import org.thingsboard.server.common.data.User; @@ -25,7 +26,7 @@ public interface TbImageService { TbResourceInfo save(TbResourceInfo imageInfo, User user); - void delete(TbResourceInfo imageInfo, User user); + TbImageDeleteResult delete(TbResourceInfo imageInfo, User user, boolean force); String getETag(ImageCacheKey imageCacheKey); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ImageService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ImageService.java index f294083290..82779fca21 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ImageService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ImageService.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.dao.resource; +import org.thingsboard.server.common.data.TbImageDeleteResult; import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.TbResourceInfo; import org.thingsboard.server.common.data.id.TbResourceId; @@ -40,7 +41,7 @@ public interface ImageService { byte[] getImagePreview(TenantId tenantId, TbResourceId imageId); - void deleteImage(TenantId tenantId, TbResourceId imageId); + TbImageDeleteResult deleteImage(TbResourceInfo imageInfo, boolean force); List findSimilarImagesByTenantIdAndKeyStartingWith(TenantId tenantId, byte[] data, String imageKeyStartingWith); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java index 8614b74502..b4cda2d733 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java @@ -56,6 +56,8 @@ public interface ResourceService extends EntityDaoService { void deleteResource(TenantId tenantId, TbResourceId resourceId); + void deleteResource(TenantId tenantId, TbResourceId resourceId, boolean force); + void deleteResourcesByTenantId(TenantId tenantId); long sumDataSizeByTenantId(TenantId tenantId); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/TbImageDeleteResult.java b/common/data/src/main/java/org/thingsboard/server/common/data/TbImageDeleteResult.java new file mode 100644 index 0000000000..22f01c83a9 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/TbImageDeleteResult.java @@ -0,0 +1,32 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.common.data; + +import lombok.Builder; +import lombok.Data; +import org.thingsboard.server.common.data.id.HasId; + +import java.util.List; +import java.util.Map; + +@Data +@Builder +public class TbImageDeleteResult { + + private boolean success; + private Map>> affectedEntities; + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/ImageContainerDao.java b/dao/src/main/java/org/thingsboard/server/dao/ImageContainerDao.java new file mode 100644 index 0000000000..162ce75dc0 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/ImageContainerDao.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.dao; + +import org.thingsboard.server.common.data.id.HasId; +import org.thingsboard.server.common.data.id.TenantId; + +import java.util.List; + +public interface ImageContainerDao> { + + List findByTenantAndImageLink(TenantId tenantId, String imageUrl, int limit); + + List findByImageLink(String imageUrl, int limit); + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java index 5e7c3ff455..37d5dcea7c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java @@ -24,8 +24,10 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.util.TbPair; +import org.thingsboard.server.common.data.widget.WidgetsBundle; import org.thingsboard.server.dao.Dao; import org.thingsboard.server.dao.ExportableEntityDao; +import org.thingsboard.server.dao.ImageContainerDao; import org.thingsboard.server.dao.TenantEntityDao; import java.util.List; diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileDao.java b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileDao.java index b7fc5fedc6..bffe9e86e9 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileDao.java @@ -21,12 +21,14 @@ import org.thingsboard.server.common.data.id.AssetProfileId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.widget.WidgetsBundle; import org.thingsboard.server.dao.Dao; import org.thingsboard.server.dao.ExportableEntityDao; +import org.thingsboard.server.dao.ImageContainerDao; import java.util.UUID; -public interface AssetProfileDao extends Dao, ExportableEntityDao { +public interface AssetProfileDao extends Dao, ExportableEntityDao, ImageContainerDao { AssetProfileInfo findAssetProfileInfoById(TenantId tenantId, UUID assetProfileId); diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardInfoDao.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardInfoDao.java index 887c39109e..1d1cf36fff 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardInfoDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardInfoDao.java @@ -16,16 +16,20 @@ package org.thingsboard.server.dao.dashboard; import org.thingsboard.server.common.data.DashboardInfo; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.widget.WidgetTypeInfo; import org.thingsboard.server.dao.Dao; +import org.thingsboard.server.dao.ImageContainerDao; +import java.util.List; import java.util.UUID; /** * The Interface DashboardInfoDao. */ -public interface DashboardInfoDao extends Dao { +public interface DashboardInfoDao extends Dao, ImageContainerDao { /** * Find dashboards by tenantId and page link. @@ -78,4 +82,5 @@ public interface DashboardInfoDao extends Dao { DashboardInfo findFirstByTenantIdAndName(UUID tenantId, String name); String findTitleById(UUID tenantId, UUID dashboardId); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileDao.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileDao.java index c221002413..d3a13d82bf 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileDao.java @@ -17,16 +17,18 @@ package org.thingsboard.server.dao.device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfileInfo; +import org.thingsboard.server.common.data.asset.AssetProfileInfo; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.Dao; import org.thingsboard.server.dao.ExportableEntityDao; +import org.thingsboard.server.dao.ImageContainerDao; import java.util.UUID; -public interface DeviceProfileDao extends Dao, ExportableEntityDao { +public interface DeviceProfileDao extends Dao, ExportableEntityDao, ImageContainerDao { DeviceProfileInfo findDeviceProfileInfoById(TenantId tenantId, UUID deviceProfileId); diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java index 500fe37d33..e7469038a3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java @@ -18,23 +18,40 @@ package org.thingsboard.server.dao.resource; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.thingsboard.common.util.RegexUtils; +import org.thingsboard.server.common.data.DashboardInfo; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ImageDescriptor; import org.thingsboard.server.common.data.ResourceType; +import org.thingsboard.server.common.data.TbImageDeleteResult; import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.TbResourceInfo; import org.thingsboard.server.common.data.TbResourceInfoFilter; +import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.widget.WidgetTypeInfo; +import org.thingsboard.server.dao.ImageContainerDao; +import org.thingsboard.server.dao.asset.AssetProfileDao; +import org.thingsboard.server.dao.dashboard.DashboardInfoDao; +import org.thingsboard.server.dao.device.DeviceProfileDao; +import org.thingsboard.server.dao.service.Validator; import org.thingsboard.server.dao.service.validator.ResourceDataValidator; import org.thingsboard.server.dao.util.ImageUtils; import org.thingsboard.server.dao.util.ImageUtils.ProcessedImage; +import org.thingsboard.server.dao.widget.WidgetTypeDao; +import org.thingsboard.server.dao.widget.WidgetsBundleDao; +import javax.annotation.PostConstruct; +import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.regex.Pattern; @@ -43,10 +60,34 @@ import java.util.regex.Pattern; @Slf4j public class BaseImageService extends BaseResourceService implements ImageService { + private static final int MAX_ENTITIES_TO_FIND = 10; + @Autowired + private AssetProfileDao assetProfileDao; + @Autowired + private DeviceProfileDao deviceProfileDao; + @Autowired + private WidgetsBundleDao widgetsBundleDao; + @Autowired + private WidgetTypeDao widgetTypeDao; + @Autowired + private DashboardInfoDao dashboardInfoDao; + + private final Map> imageContainerDaoMap = new HashMap<>(); + public BaseImageService(TbResourceDao resourceDao, TbResourceInfoDao resourceInfoDao, ResourceDataValidator resourceValidator) { super(resourceDao, resourceInfoDao, resourceValidator); } + @PostConstruct + public void init() { + imageContainerDaoMap.put(EntityType.WIDGET_TYPE, widgetTypeDao); + imageContainerDaoMap.put(EntityType.WIDGETS_BUNDLE, widgetsBundleDao); + imageContainerDaoMap.put(EntityType.DEVICE_PROFILE, deviceProfileDao); + imageContainerDaoMap.put(EntityType.ASSET_PROFILE, assetProfileDao); + imageContainerDaoMap.put(EntityType.DASHBOARD, dashboardInfoDao); + } + + @Transactional @Override public TbResourceInfo saveImage(TbResource image) throws Exception { @@ -147,8 +188,32 @@ public class BaseImageService extends BaseResourceService implements ImageServic } @Override - public void deleteImage(TenantId tenantId, TbResourceId imageId) { - deleteResource(tenantId, imageId); + public TbImageDeleteResult deleteImage(TbResourceInfo imageInfo, boolean force) { + var tenantId = imageInfo.getTenantId(); + var imageId = imageInfo.getId(); + log.trace("Executing deleteImage [{}] [{}]", tenantId, imageId); + Validator.validateId(imageId, INCORRECT_RESOURCE_ID + imageId); + TbImageDeleteResult.TbImageDeleteResultBuilder result = TbImageDeleteResult.builder(); + boolean success = true; + if (!force) { + var link = imageInfo.getLink(); + Map>> affectedEntities = new HashMap<>(); + imageContainerDaoMap.forEach((entityType, imageContainerDao) -> { + var entities = tenantId.isSysTenantId() ? imageContainerDao.findByImageLink(link, MAX_ENTITIES_TO_FIND) : + imageContainerDao.findByTenantAndImageLink(tenantId, link, MAX_ENTITIES_TO_FIND); + if (!entities.isEmpty()) { + affectedEntities.put(entityType.name(), entities); + } + }); + if (!affectedEntities.isEmpty()) { + success = false; + result.affectedEntities(affectedEntities); + } + } + if (success) { + deleteResource(tenantId, imageId, force); + } + return result.success(success).build(); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java index 13ed98b461..d935409afb 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java @@ -145,13 +145,21 @@ public class BaseResourceService extends AbstractCachedEntityService findAllTenantResourcesByTenantId(TbResourceInfoFilter filter, PageLink pageLink) { TenantId tenantId = filter.getTenantId(); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetProfileRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetProfileRepository.java index f23478e6d5..6c66363ed1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetProfileRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetProfileRepository.java @@ -23,7 +23,9 @@ import org.springframework.data.repository.query.Param; import org.thingsboard.server.common.data.asset.AssetProfileInfo; import org.thingsboard.server.dao.ExportableEntityRepository; import org.thingsboard.server.dao.model.sql.AssetProfileEntity; +import org.thingsboard.server.dao.model.sql.WidgetsBundleEntity; +import java.util.List; import java.util.UUID; public interface AssetProfileRepository extends JpaRepository, ExportableEntityRepository { @@ -60,4 +62,12 @@ public interface AssetProfileRepository extends JpaRepository findByTenantAndImageLink(@Param("tenantId") UUID tenantId, @Param("imageLink") String imageLink, Pageable page); + + @Query("SELECT new org.thingsboard.server.common.data.asset.AssetProfileInfo(a.id, a.tenantId, a.name, a.image, a.defaultDashboardId) " + + "FROM AssetProfileEntity a WHERE a.image = :imageLink") + List findByImageLink(@Param("imageLink") String imageLink, Pageable page); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetProfileDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetProfileDao.java index e34df2f21c..4d6c8c0406 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetProfileDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetProfileDao.java @@ -16,6 +16,7 @@ package org.thingsboard.server.dao.sql.asset; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageRequest; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; @@ -31,6 +32,7 @@ import org.thingsboard.server.dao.asset.AssetProfileDao; import org.thingsboard.server.dao.model.sql.AssetProfileEntity; import org.thingsboard.server.dao.sql.JpaAbstractDao; +import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.UUID; @@ -118,6 +120,16 @@ public class JpaAssetProfileDao extends JpaAbstractDao findByTenantAndImageLink(TenantId tenantId, String imageLink, int limit) { + return assetProfileRepository.findByTenantAndImageLink(tenantId.getId(), imageLink, PageRequest.of(0, limit)); + } + + @Override + public List findByImageLink(String imageLink, int limit) { + return assetProfileRepository.findByImageLink(imageLink, PageRequest.of(0, limit)); + } + @Override public EntityType getEntityType() { return EntityType.ASSET_PROFILE; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/DashboardInfoRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/DashboardInfoRepository.java index bddda18563..a92da55c03 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/DashboardInfoRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/DashboardInfoRepository.java @@ -22,6 +22,7 @@ import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.thingsboard.server.dao.model.sql.DashboardInfoEntity; +import java.util.List; import java.util.UUID; /** @@ -74,4 +75,16 @@ public interface DashboardInfoRepository extends JpaRepository findByTenantAndImageLink(@Param("tenantId") UUID tenantId, @Param("imageLink") String imageLink, @Param("lmt") int lmt); + + @Query(nativeQuery = true, + value = "SELECT * FROM dashboard d WHERE d.image = :imageLink or d.configuration ILIKE CONCAT('%', :imageLink, '%') limit :lmt" + ) + List findByImageLink(@Param("imageLink") String imageLink, @Param("lmt") int lmt); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardInfoDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardInfoDao.java index b988048db8..c9318db61e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardInfoDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardInfoDao.java @@ -20,6 +20,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.DashboardInfo; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.SortOrder; @@ -31,7 +32,6 @@ import org.thingsboard.server.dao.util.SqlDao; import java.util.ArrayList; import java.util.List; -import java.util.Objects; import java.util.UUID; /** @@ -123,4 +123,14 @@ public class JpaDashboardInfoDao extends JpaAbstractDao findByTenantAndImageLink(TenantId tenantId, String imageLink, int limit) { + return DaoUtil.convertDataList(dashboardInfoRepository.findByTenantAndImageLink(tenantId.getId(), imageLink, limit)); + } + + @Override + public List findByImageLink(String imageLink, int limit) { + return DaoUtil.convertDataList(dashboardInfoRepository.findByImageLink(imageLink, limit)); + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceProfileRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceProfileRepository.java index 295b64cd89..996e29f81f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceProfileRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceProfileRepository.java @@ -22,9 +22,11 @@ import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.thingsboard.server.common.data.DeviceProfileInfo; import org.thingsboard.server.common.data.DeviceTransportType; +import org.thingsboard.server.common.data.asset.AssetProfileInfo; import org.thingsboard.server.dao.ExportableEntityRepository; import org.thingsboard.server.dao.model.sql.DeviceProfileEntity; +import java.util.List; import java.util.UUID; public interface DeviceProfileRepository extends JpaRepository, ExportableEntityRepository { @@ -68,6 +70,14 @@ public interface DeviceProfileRepository extends JpaRepository findByTenantAndImageLink(@Param("tenantId") UUID tenantId, @Param("imageLink") String imageLink, Pageable page); + + @Query("SELECT new org.thingsboard.server.common.data.DeviceProfileInfo(d.id, d.tenantId, d.name, d.image, d.defaultDashboardId, d.type, d.transportType) " + + "FROM DeviceProfileEntity d WHERE d.image = :imageLink") + List findByImageLink(@Param("imageLink") String imageLink, Pageable page); + @Query("SELECT externalId FROM DeviceProfileEntity WHERE id = :id") UUID getExternalIdById(@Param("id") UUID id); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java index 7b334330fd..201545d164 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java @@ -16,6 +16,7 @@ package org.thingsboard.server.dao.sql.device; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageRequest; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; @@ -24,6 +25,7 @@ import org.thingsboard.server.common.data.DeviceProfileInfo; import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.asset.AssetProfileInfo; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; @@ -34,6 +36,7 @@ import org.thingsboard.server.dao.model.sql.DeviceProfileEntity; import org.thingsboard.server.dao.sql.JpaAbstractDao; import org.thingsboard.server.dao.util.SqlDao; +import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.UUID; @@ -136,6 +139,16 @@ public class JpaDeviceProfileDao extends JpaAbstractDao findByTenantAndImageLink(TenantId tenantId, String imageLink, int limit) { + return deviceProfileRepository.findByTenantAndImageLink(tenantId.getId(), imageLink, PageRequest.of(0, limit)); + } + + @Override + public List findByImageLink(String imageLink, int limit) { + return deviceProfileRepository.findByImageLink(imageLink, PageRequest.of(0, limit)); + } + @Override public EntityType getEntityType() { return EntityType.DEVICE_PROFILE; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDao.java index f1878e1de8..d105943a80 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDao.java @@ -236,6 +236,16 @@ public class JpaWidgetTypeDao extends JpaAbstractDao findByTenantAndImageLink(TenantId tenantId, String imageUrl, int limit) { + return DaoUtil.convertDataList(widgetTypeInfoRepository.findByTenantAndImageUrl(tenantId.getId(), imageUrl, limit)); + } + + @Override + public List findByImageLink(String imageUrl, int limit) { + return DaoUtil.convertDataList(widgetTypeInfoRepository.findByImageUrl(imageUrl, limit)); + } + @Override public EntityType getEntityType() { return EntityType.WIDGET_TYPE; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/widget/JpaWidgetsBundleDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/widget/JpaWidgetsBundleDao.java index 2475da8fb7..27759864d0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/widget/JpaWidgetsBundleDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/widget/JpaWidgetsBundleDao.java @@ -146,6 +146,16 @@ public class JpaWidgetsBundleDao extends JpaAbstractDao findByTenantAndImageLink(TenantId tenantId, String imageUrl, int limit) { + return DaoUtil.convertDataList(widgetsBundleRepository.findByTenantAndImageUrl(tenantId.getId(), imageUrl, limit)); + } + + @Override + public List findByImageLink(String imageUrl, int limit) { + return DaoUtil.convertDataList(widgetsBundleRepository.findByImageUrl(imageUrl, limit)); + } + @Override public EntityType getEntityType() { return EntityType.WIDGETS_BUNDLE; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetTypeInfoRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetTypeInfoRepository.java index f9114a9e40..e9dcca7914 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetTypeInfoRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetTypeInfoRepository.java @@ -194,4 +194,17 @@ public interface WidgetTypeInfoRepository extends JpaRepository widgetTypes, Pageable pageable); + + @Query(nativeQuery = true, + value = "SELECT * FROM widget_type_info_view wti WHERE wti.id IN " + + "(select id from widget_type where tenant_id = :tenantId " + + "and (image = :imageLink or descriptor ILIKE CONCAT('%', :imageLink, '%')) limit :lmt)" + ) + List findByTenantAndImageUrl(@Param("tenantId") UUID tenantId, @Param("imageLink") String imageLink, @Param("lmt") int lmt); + + @Query(nativeQuery = true, + value = "SELECT * FROM widget_type_info_view wti WHERE wti.id IN " + + "(select id from widget_type where image = :imageLink or descriptor ILIKE CONCAT('%', :imageLink, '%') limit :lmt)" + ) + List findByImageUrl(@Param("imageLink") String imageLink, @Param("lmt") int lmt); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetsBundleRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetsBundleRepository.java index 9db39aab8f..64440fb1f4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetsBundleRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetsBundleRepository.java @@ -21,6 +21,7 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.thingsboard.server.dao.ExportableEntityRepository; +import org.thingsboard.server.dao.model.sql.WidgetTypeInfoEntity; import org.thingsboard.server.dao.model.sql.WidgetsBundleEntity; import java.util.List; @@ -133,4 +134,9 @@ public interface WidgetsBundleRepository extends JpaRepository findByTenantAndImageUrl(@Param("tenantId") UUID tenantId, @Param("imageLink") String imageLink, @Param("lmt") int lmt); + + @Query(nativeQuery = true, value = "SELECT * FROM widgets_bundle wb WHERE wb.image = :imageLink limit :lmt") + List findByImageUrl(@Param("imageLink") String imageLink, @Param("lmt") int lmt); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeDao.java b/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeDao.java index 55fb5bcff4..23e25d12ec 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeDao.java @@ -26,6 +26,7 @@ import org.thingsboard.server.common.data.widget.WidgetTypeInfo; import org.thingsboard.server.common.data.widget.WidgetsBundleWidget; import org.thingsboard.server.dao.Dao; import org.thingsboard.server.dao.ExportableEntityDao; +import org.thingsboard.server.dao.ImageContainerDao; import java.util.List; import java.util.UUID; @@ -33,7 +34,7 @@ import java.util.UUID; /** * The Interface WidgetTypeDao. */ -public interface WidgetTypeDao extends Dao, ExportableEntityDao { +public interface WidgetTypeDao extends Dao, ExportableEntityDao, ImageContainerDao { /** * Save or update widget type object diff --git a/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleDao.java b/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleDao.java index e83da6cc8e..939f9e43e5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleDao.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.dao.widget; +import org.thingsboard.server.common.data.DashboardInfo; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.WidgetsBundleId; import org.thingsboard.server.common.data.page.PageData; @@ -22,13 +23,14 @@ import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.widget.WidgetsBundle; import org.thingsboard.server.dao.Dao; import org.thingsboard.server.dao.ExportableEntityDao; +import org.thingsboard.server.dao.ImageContainerDao; import java.util.UUID; /** * The Interface WidgetsBundleDao. */ -public interface WidgetsBundleDao extends Dao, ExportableEntityDao { +public interface WidgetsBundleDao extends Dao, ExportableEntityDao, ImageContainerDao { /** * Save or update widgets bundle object diff --git a/dao/src/test/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDaoTest.java index e6d46bb942..0a466d033c 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDaoTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDaoTest.java @@ -110,6 +110,7 @@ public class JpaWidgetTypeDaoTest extends AbstractJpaDaoTest { widgetType.setName("WIDGET_TYPE_" + number); widgetType.setDescription("WIDGET_TYPE_DESCRIPTION" + number); widgetType.setFqn("FQN_" + number); + widgetType.setImage("/image/system/logo.png"); var descriptor = JacksonUtil.newObjectNode(); descriptor.put("type", number % 2 == 0 ? "latest" : "static"); widgetType.setDescriptor(descriptor); @@ -124,6 +125,7 @@ public class JpaWidgetTypeDaoTest extends AbstractJpaDaoTest { widgetType.setName("WIDGET_TYPE_" + number); widgetType.setDescription("WIDGET_TYPE_DESCRIPTION" + number); widgetType.setFqn("FQN_" + number); + widgetType.setImage("/image/tenant/logo.png"); var descriptor = JacksonUtil.newObjectNode(); descriptor.put("type", number % 2 == 0 ? "latest" : "static"); widgetType.setDescriptor(descriptor); @@ -332,4 +334,25 @@ public class JpaWidgetTypeDaoTest extends AbstractJpaDaoTest { WidgetType widgetType = widgetTypeDao.findByTenantIdAndFqn(TenantId.SYS_TENANT_ID.getId(), "FQN_0"); assertEquals(result.getId(), widgetType.getId()); } + + @Test + public void testFindByTenantAndImageLink() { + var result = widgetTypeDao.findByImageLink("/image/system/logo.png", 5); + assertEquals(3, result.size()); + result = widgetTypeDao.findByImageLink("/image/system/logo2.png", 5); + assertEquals(0, result.size()); + } + + @Test + public void testFindByImageLink() { + TenantId tenantId = new TenantId(UUID.randomUUID()); + WidgetTypeDetails details = createAndSaveWidgetType(tenantId, 0, new String[]{"a"}); + details.setDescriptor(JacksonUtil.newObjectNode().put("bg", "/image/tenant/widget.png")); + widgetTypeDao.save(tenantId, details); + var result = widgetTypeDao.findByTenantAndImageLink(tenantId, "/image/tenant/widget.png", 3); + assertEquals(1, result.size()); + result = widgetTypeDao.findByTenantAndImageLink(tenantId, "/image/tenant/widget2.png", 3); + assertEquals(0, result.size()); + widgetTypeDao.removeById(tenantId, details.getUuidId()); + } } From eabc5d4a49ccfd51d42a786a711ada4c0db844b4 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Tue, 21 Nov 2023 12:55:14 +0200 Subject: [PATCH 181/252] Minor improvement --- .../common/data/TbImageDeleteResult.java | 2 +- .../server/dao/resource/BaseImageService.java | 30 +++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/TbImageDeleteResult.java b/common/data/src/main/java/org/thingsboard/server/common/data/TbImageDeleteResult.java index 22f01c83a9..9782b2b58f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/TbImageDeleteResult.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/TbImageDeleteResult.java @@ -27,6 +27,6 @@ import java.util.Map; public class TbImageDeleteResult { private boolean success; - private Map>> affectedEntities; + private Map>> references; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java index e7469038a3..559e220b48 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.dao.resource; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; @@ -22,7 +23,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.thingsboard.common.util.RegexUtils; -import org.thingsboard.server.common.data.DashboardInfo; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ImageDescriptor; import org.thingsboard.server.common.data.ResourceType; @@ -35,7 +35,6 @@ import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; -import org.thingsboard.server.common.data.widget.WidgetTypeInfo; import org.thingsboard.server.dao.ImageContainerDao; import org.thingsboard.server.dao.asset.AssetProfileDao; import org.thingsboard.server.dao.dashboard.DashboardInfoDao; @@ -48,7 +47,6 @@ import org.thingsboard.server.dao.widget.WidgetTypeDao; import org.thingsboard.server.dao.widget.WidgetsBundleDao; import javax.annotation.PostConstruct; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -61,21 +59,23 @@ import java.util.regex.Pattern; public class BaseImageService extends BaseResourceService implements ImageService { private static final int MAX_ENTITIES_TO_FIND = 10; - @Autowired - private AssetProfileDao assetProfileDao; - @Autowired - private DeviceProfileDao deviceProfileDao; - @Autowired - private WidgetsBundleDao widgetsBundleDao; - @Autowired - private WidgetTypeDao widgetTypeDao; - @Autowired - private DashboardInfoDao dashboardInfoDao; + private final AssetProfileDao assetProfileDao; + private final DeviceProfileDao deviceProfileDao; + private final WidgetsBundleDao widgetsBundleDao; + private final WidgetTypeDao widgetTypeDao; + private final DashboardInfoDao dashboardInfoDao; private final Map> imageContainerDaoMap = new HashMap<>(); - public BaseImageService(TbResourceDao resourceDao, TbResourceInfoDao resourceInfoDao, ResourceDataValidator resourceValidator) { + public BaseImageService(TbResourceDao resourceDao, TbResourceInfoDao resourceInfoDao, ResourceDataValidator resourceValidator, + AssetProfileDao assetProfileDao, DeviceProfileDao deviceProfileDao, WidgetsBundleDao widgetsBundleDao, + WidgetTypeDao widgetTypeDao, DashboardInfoDao dashboardInfoDao) { super(resourceDao, resourceInfoDao, resourceValidator); + this.assetProfileDao = assetProfileDao; + this.deviceProfileDao = deviceProfileDao; + this.widgetsBundleDao = widgetsBundleDao; + this.widgetTypeDao = widgetTypeDao; + this.dashboardInfoDao = dashboardInfoDao; } @PostConstruct @@ -207,7 +207,7 @@ public class BaseImageService extends BaseResourceService implements ImageServic }); if (!affectedEntities.isEmpty()) { success = false; - result.affectedEntities(affectedEntities); + result.references(affectedEntities); } } if (success) { From 678ac153247fa479a0e16de0557857ab3f64a63d Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Tue, 21 Nov 2023 14:08:52 +0200 Subject: [PATCH 182/252] UI: Implement image edit/update. --- .../server/controller/ImageController.java | 1 + ui-ngx/src/app/core/http/image.service.ts | 2 +- .../home/components/home-components.module.ts | 7 +- .../image/image-dialog.component.html | 94 +++++++++++ .../image/image-dialog.component.scss | 83 ++++++++++ .../image/image-dialog.component.ts | 147 ++++++++++++++++++ .../image/image-gallery.component.html | 4 +- .../image/image-gallery.component.ts | 37 +++-- .../image/upload-image-dialog.component.html | 9 +- .../image/upload-image-dialog.component.ts | 54 +++++-- .../components/image-input.component.html | 5 +- .../components/image-input.component.scss | 29 +++- .../components/image-input.component.ts | 25 ++- ui-ngx/src/app/shared/pipe/image.pipe.ts | 7 +- .../assets/locale/locale.constant-en_US.json | 4 +- 15 files changed, 468 insertions(+), 40 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/components/image/image-dialog.component.html create mode 100644 ui-ngx/src/app/modules/home/components/image/image-dialog.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/image/image-dialog.component.ts diff --git a/application/src/main/java/org/thingsboard/server/controller/ImageController.java b/application/src/main/java/org/thingsboard/server/controller/ImageController.java index bfaad1c38d..42881c2cbe 100644 --- a/application/src/main/java/org/thingsboard/server/controller/ImageController.java +++ b/application/src/main/java/org/thingsboard/server/controller/ImageController.java @@ -113,6 +113,7 @@ public class ImageController extends BaseController { TbResourceInfo imageInfo = checkImageInfo(type, key, Operation.WRITE); TbResource image = new TbResource(imageInfo); image.setData(file.getBytes()); + image.setFileName(file.getOriginalFilename()); image.updateDescriptor(ImageDescriptor.class, descriptor -> { descriptor.setMediaType(file.getContentType()); return descriptor; diff --git a/ui-ngx/src/app/core/http/image.service.ts b/ui-ngx/src/app/core/http/image.service.ts index 699582eb38..e13acb0b3e 100644 --- a/ui-ngx/src/app/core/http/image.service.ts +++ b/ui-ngx/src/app/core/http/image.service.ts @@ -58,7 +58,7 @@ export class ImageService { } const formData = new FormData(); formData.append('file', file); - return this.http.post(`${IMAGES_URL_PREFIX}/${type}/${encodeURIComponent(key)}`, formData, + return this.http.put(`${IMAGES_URL_PREFIX}/${type}/${encodeURIComponent(key)}`, formData, defaultHttpUploadOptions(config.ignoreLoading, config.ignoreErrors, config.resendRequest)); } diff --git a/ui-ngx/src/app/modules/home/components/home-components.module.ts b/ui-ngx/src/app/modules/home/components/home-components.module.ts index 4e3a52e9f1..bb6c47c924 100644 --- a/ui-ngx/src/app/modules/home/components/home-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/home-components.module.ts @@ -184,6 +184,7 @@ import { import { ScrollGridComponent } from '@home/components/grid/scroll-grid.component'; import { ImageGalleryComponent } from '@home/components/image/image-gallery.component'; import { UploadImageDialogComponent } from '@home/components/image/upload-image-dialog.component'; +import { ImageDialogComponent } from '@home/components/image/image-dialog.component'; @NgModule({ declarations: @@ -331,7 +332,8 @@ import { UploadImageDialogComponent } from '@home/components/image/upload-image- SendNotificationButtonComponent, ScrollGridComponent, ImageGalleryComponent, - UploadImageDialogComponent + UploadImageDialogComponent, + ImageDialogComponent ], imports: [ CommonModule, @@ -472,7 +474,8 @@ import { UploadImageDialogComponent } from '@home/components/image/upload-image- SendNotificationButtonComponent, ScrollGridComponent, ImageGalleryComponent, - UploadImageDialogComponent + UploadImageDialogComponent, + ImageDialogComponent ], providers: [ WidgetComponentService, diff --git a/ui-ngx/src/app/modules/home/components/image/image-dialog.component.html b/ui-ngx/src/app/modules/home/components/image/image-dialog.component.html new file mode 100644 index 0000000000..23ef4dcb2a --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/image/image-dialog.component.html @@ -0,0 +1,94 @@ + +
+ +

{{ (readonly ? 'image.image-details' : 'image.edit-image') | translate }}

+ + +
+ + +
+
+
+ + image.name + + + {{ 'image.name-required' | translate }} + + +
+
+
image.image-preview
+
+
+ +
+
+
{{ image.descriptor.width }}x{{ image.descriptor.height }}
+ +
{{ image.descriptor.size | fileSize }}
+
+
+ + + +
+
+
+
+
+
+ + +
+
diff --git a/ui-ngx/src/app/modules/home/components/image/image-dialog.component.scss b/ui-ngx/src/app/modules/home/components/image/image-dialog.component.scss new file mode 100644 index 0000000000..813974e541 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/image/image-dialog.component.scss @@ -0,0 +1,83 @@ +/** + * Copyright © 2016-2023 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 { + .tb-image-container { + width: fit-content; + border-radius: 4px; + border: 1px solid rgba(0, 0, 0, 0.05); + padding: 12px; + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 12px; + + .tb-image-content { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 8px; + .tb-image-preview-title { + color: rgba(0, 0, 0, 0.54); + font-size: 13px; + font-style: normal; + font-weight: 500; + line-height: 16px; + } + .tb-image-preview-container { + position: relative; + width: 100%; + height: 100%; + border: 1px solid rgba(0, 0, 0, 0.12); + border-radius: 4px; + background: #fff; + display: flex; + align-items: center; + justify-content: center; + z-index: 1; + .tb-image-preview-spacer { + margin-top: 100%; + } + .tb-image-preview { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + object-fit: contain; + } + } + .tb-image-preview-details { + display: flex; + align-items: center; + gap: 8px; + color: rgba(0, 0, 0, 0.38); + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 20px; + .mat-divider.mat-divider-vertical { + height: 20px; + } + } + .tb-image-actions { + display: flex; + align-items: center; + gap: 8px; + color: rgba(0,0,0,0.54); + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/image/image-dialog.component.ts b/ui-ngx/src/app/modules/home/components/image/image-dialog.component.ts new file mode 100644 index 0000000000..72b2bbf85c --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/image/image-dialog.component.ts @@ -0,0 +1,147 @@ +/// +/// Copyright © 2016-2023 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, Inject, OnInit, SkipSelf } from '@angular/core'; +import { ErrorStateMatcher } from '@angular/material/core'; +import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { + FormGroupDirective, + NgForm, + UntypedFormBuilder, + UntypedFormControl, + UntypedFormGroup, + Validators +} from '@angular/forms'; +import { DialogComponent } from '@shared/components/dialog.component'; +import { Router } from '@angular/router'; +import { ImageService } from '@core/http/image.service'; +import { ImageResourceInfo, imageResourceType } from '@shared/models/resource.models'; +import { + UploadImageDialogComponent, + UploadImageDialogData +} from '@home/components/image/upload-image-dialog.component'; +import { UrlHolder } from '@shared/pipe/image.pipe'; + +export interface ImageDialogData { + readonly: boolean; + image: ImageResourceInfo; +} + +@Component({ + selector: 'tb-image-dialog', + templateUrl: './image-dialog.component.html', + providers: [{provide: ErrorStateMatcher, useExisting: ImageDialogComponent}], + styleUrls: ['./image-dialog.component.scss'] +}) +export class ImageDialogComponent extends + DialogComponent implements OnInit, ErrorStateMatcher { + + image: ImageResourceInfo; + + readonly: boolean; + + imageFormGroup: UntypedFormGroup; + + submitted = false; + + imageChanged = false; + + imagePreviewData: UrlHolder; + + constructor(protected store: Store, + protected router: Router, + private imageService: ImageService, + private dialog: MatDialog, + @Inject(MAT_DIALOG_DATA) private data: ImageDialogData, + @SkipSelf() private errorStateMatcher: ErrorStateMatcher, + public dialogRef: MatDialogRef, + public fb: UntypedFormBuilder) { + super(store, router, dialogRef); + this.image = data.image; + this.readonly = data.readonly; + this.imagePreviewData = { + url: this.image.link + }; + } + + ngOnInit(): void { + this.imageFormGroup = this.fb.group({ + title: [this.image.title, [Validators.required]] + }); + if (this.data.readonly) { + this.imageFormGroup.disable(); + } + } + + isErrorState(control: UntypedFormControl | null, form: FormGroupDirective | NgForm | null): boolean { + const originalErrorState = this.errorStateMatcher.isErrorState(control, form); + const customErrorState = !!(control && control.invalid && this.submitted); + return originalErrorState || customErrorState; + } + + cancel(): void { + this.dialogRef.close(this.imageChanged); + } + + downloadImage($event) { + if ($event) { + $event.stopPropagation(); + } + this.imageService.downloadImage(imageResourceType(this.image), this.image.resourceKey).subscribe(); + } + + exportImage($event) { + if ($event) { + $event.stopPropagation(); + } + // TODO: + } + + updateImage($event): void { + if ($event) { + $event.stopPropagation(); + } + this.dialog.open(UploadImageDialogComponent, { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: { + image: this.image + } + }).afterClosed().subscribe((result) => { + if (result) { + this.imageChanged = true; + this.image = result; + this.imagePreviewData = { + url: this.image.link + }; + } + }); + } + + save(): void { + this.submitted = true; + const title: string = this.imageFormGroup.get('title').value; + const image = {...this.image, ...{title}}; + this.imageService.updateImageInfo(image).subscribe( + () => { + this.dialogRef.close(true); + } + ); + } +} diff --git a/ui-ngx/src/app/modules/home/components/image/image-gallery.component.html b/ui-ngx/src/app/modules/home/components/image/image-gallery.component.html index ebb4a40507..44bf4a4fd5 100644 --- a/ui-ngx/src/app/modules/home/components/image/image-gallery.component.html +++ b/ui-ngx/src/app/modules/home/components/image/image-gallery.component.html @@ -110,7 +110,7 @@
- @@ -185,7 +185,7 @@ (click)="editImage($event, image)"> edit - diff --git a/ui-ngx/src/app/modules/home/components/image/upload-image-dialog.component.ts b/ui-ngx/src/app/modules/home/components/image/upload-image-dialog.component.ts index 46a2843f7a..841f64c883 100644 --- a/ui-ngx/src/app/modules/home/components/image/upload-image-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/image/upload-image-dialog.component.ts @@ -14,9 +14,9 @@ /// limitations under the License. /// -import { Component, OnInit, SkipSelf } from '@angular/core'; +import { Component, Inject, OnInit, SkipSelf } from '@angular/core'; import { ErrorStateMatcher } from '@angular/material/core'; -import { MatDialogRef } from '@angular/material/dialog'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { @@ -30,6 +30,11 @@ import { import { DialogComponent } from '@shared/components/dialog.component'; import { Router } from '@angular/router'; import { ImageService } from '@core/http/image.service'; +import { ImageResourceInfo, imageResourceType } from '@shared/models/resource.models'; + +export interface UploadImageDialogData { + image?: ImageResourceInfo; +} @Component({ selector: 'tb-upload-image-dialog', @@ -38,32 +43,40 @@ import { ImageService } from '@core/http/image.service'; styleUrls: [] }) export class UploadImageDialogComponent extends - DialogComponent implements OnInit, ErrorStateMatcher { + DialogComponent implements OnInit, ErrorStateMatcher { uploadImageFormGroup: UntypedFormGroup; + uploadImage = true; + submitted = false; constructor(protected store: Store, protected router: Router, private imageService: ImageService, + @Inject(MAT_DIALOG_DATA) public data: UploadImageDialogData, @SkipSelf() private errorStateMatcher: ErrorStateMatcher, - public dialogRef: MatDialogRef, + public dialogRef: MatDialogRef, public fb: UntypedFormBuilder) { super(store, router, dialogRef); } ngOnInit(): void { + this.uploadImage = !this.data?.image; this.uploadImageFormGroup = this.fb.group({ - file: [null, [Validators.required]], - title: [null, [Validators.required]] + file: [this.data?.image?.link, [Validators.required]] }); + if (this.uploadImage) { + this.uploadImageFormGroup.addControl('title', this.fb.control(null, [Validators.required])); + } } imageFileNameChanged(fileName: string) { - const titleControl = this.uploadImageFormGroup.get('title'); - if (!titleControl.value || !titleControl.touched) { - titleControl.setValue(fileName); + if (this.uploadImage) { + const titleControl = this.uploadImageFormGroup.get('title'); + if (!titleControl.value || !titleControl.touched) { + titleControl.setValue(fileName); + } } } @@ -74,17 +87,26 @@ export class UploadImageDialogComponent extends } cancel(): void { - this.dialogRef.close(false); + this.dialogRef.close(null); } upload(): void { this.submitted = true; const file: File = this.uploadImageFormGroup.get('file').value; - const title: string = this.uploadImageFormGroup.get('title').value; - this.imageService.uploadImage(file, title).subscribe( - () => { - this.dialogRef.close(true); - } - ); + if (this.uploadImage) { + const title: string = this.uploadImageFormGroup.get('title').value; + this.imageService.uploadImage(file, title).subscribe( + (res) => { + this.dialogRef.close(res); + } + ); + } else { + const image = this.data.image; + this.imageService.updateImage(imageResourceType(image), image.resourceKey, file).subscribe( + (res) => { + this.dialogRef.close(res); + } + ); + } } } diff --git a/ui-ngx/src/app/shared/components/image-input.component.html b/ui-ngx/src/app/shared/components/image-input.component.html index 2ed771d8b2..039611bbf3 100644 --- a/ui-ngx/src/app/shared/components/image-input.component.html +++ b/ui-ngx/src/app/shared/components/image-input.component.html @@ -63,5 +63,8 @@ -
dashboard.maximum-upload-file-size
+
+
{{ fileName }}
+
dashboard.maximum-upload-file-size
+
diff --git a/ui-ngx/src/app/shared/components/image-input.component.scss b/ui-ngx/src/app/shared/components/image-input.component.scss index ee4f6451c4..36f4c59557 100644 --- a/ui-ngx/src/app/shared/components/image-input.component.scss +++ b/ui-ngx/src/app/shared/components/image-input.component.scss @@ -23,9 +23,12 @@ $previewSize: 78px !default; .tb-container { margin-top: 0; padding: 0 0 16px; + display: flex; + flex-direction: column; + gap: 8px; label.tb-title { display: block; - padding-bottom: 8px; + padding-bottom: 0px; } } @@ -72,6 +75,30 @@ $previewSize: 78px !default; max-height: $previewSize - 2px; } + .tb-image-info-container { + display: flex; + gap: 12px; + font-size: 13px; + font-style: normal; + line-height: 16px; + letter-spacing: normal; + } + + .tb-image-file-name { + width: 136px; + color: rgba(0, 0, 0, 0.54); + font-weight: 500; + overflow: hidden; + text-overflow: ellipsis; + } + + .tb-image-file-hint { + color: rgba(0, 0, 0, 0.38); + font-weight: 400; + overflow: hidden; + text-overflow: ellipsis; + } + .file-input { display: none; } diff --git a/ui-ngx/src/app/shared/components/image-input.component.ts b/ui-ngx/src/app/shared/components/image-input.component.ts index 8d41532405..21722159d4 100644 --- a/ui-ngx/src/app/shared/components/image-input.component.ts +++ b/ui-ngx/src/app/shared/components/image-input.component.ts @@ -37,6 +37,7 @@ import { DialogService } from '@core/services/dialog.service'; import { TranslateService } from '@ngx-translate/core'; import { FileSizePipe } from '@shared/pipe/file-size.pipe'; import { coerceBoolean } from '@shared/decorators/coercion'; +import { ImagePipe } from '@shared/pipe/image.pipe'; @Component({ selector: 'tb-image-input', @@ -84,16 +85,27 @@ export class ImageInputComponent extends PageComponent implements AfterViewInit, @Input() inputId = this.utils.guid(); + @Input() + @coerceBoolean() + processImageApiLink = false; + @Input() @coerceBoolean() resultAsFile = false; + @Input() + @coerceBoolean() + showFileName = false; + + @Input() + fileName: string; + @Output() fileNameChanged = new EventEmitter(); imageUrl: string; file: File; - fileName: string; + safeImageUrl: SafeUrl; @ViewChild('flow', {static: true}) @@ -106,6 +118,7 @@ export class ImageInputComponent extends PageComponent implements AfterViewInit, constructor(protected store: Store, private utils: UtilsService, private sanitizer: DomSanitizer, + private imagePipe: ImagePipe, private dialog: DialogService, private translate: TranslateService, private fileSize: FileSizePipe, @@ -161,7 +174,15 @@ export class ImageInputComponent extends PageComponent implements AfterViewInit, writeValue(value: string): void { this.imageUrl = value; if (this.imageUrl) { - this.safeImageUrl = this.sanitizer.bypassSecurityTrustUrl(this.imageUrl); + if (this.processImageApiLink) { + this.imagePipe.transform(this.imageUrl, {preview: true, ignoreLoadingImage: true}).subscribe( + (res) => { + this.safeImageUrl = res; + } + ); + } else { + this.safeImageUrl = this.sanitizer.bypassSecurityTrustUrl(this.imageUrl); + } } else { this.safeImageUrl = null; } diff --git a/ui-ngx/src/app/shared/pipe/image.pipe.ts b/ui-ngx/src/app/shared/pipe/image.pipe.ts index 24f91c500d..6cad747676 100644 --- a/ui-ngx/src/app/shared/pipe/image.pipe.ts +++ b/ui-ngx/src/app/shared/pipe/image.pipe.ts @@ -25,6 +25,10 @@ const LOADING_IMAGE_DATA_URI = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS 'LTgiPz4KPHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZlcnNpb249IjEuMSIgdmlld0JveD0iMCAw' + 'IDIwIDIwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPgo='; +export interface UrlHolder { + url?: string; +} + @Pipe({ name: 'image' }) @@ -33,10 +37,11 @@ export class ImagePipe implements PipeTransform { constructor(private imageService: ImageService, private sanitizer: DomSanitizer) { } - transform(url: string, args?: any): Observable { + transform(urlData: string | UrlHolder, args?: any): Observable { const ignoreLoadingImage = !!args?.ignoreLoadingImage; const asString = !!args?.asString; const image$ = ignoreLoadingImage ? new Subject() : new BehaviorSubject(LOADING_IMAGE_DATA_URI); + const url = (typeof urlData === 'string') ? urlData : urlData?.url; if (isDefinedAndNotNull(url)) { const preview = !!args?.preview; this.imageService.resolveImageUrl(url, preview, asString).subscribe((imageUrl) => { 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 98e7334e58..14e0beaff1 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2958,6 +2958,7 @@ "import-image": "Import image", "upload-image": "Upload image", "edit-image": "Edit image", + "image-details": "Image details", "no-images": "No images found", "delete-image": "Delete image", "delete-image-title": "Are you sure you want to delete image '{{imageTitle}}'?", @@ -2966,7 +2967,8 @@ "delete-images-text": "Be careful, after the confirmation all selected images will be removed and all related data will become unrecoverable.", "list-mode": "List view", "grid-mode": "Grid view", - "image-preview": "Image preview" + "image-preview": "Image preview", + "update-image": "Update image" }, "image-input": { "drop-images-or": "Drag and drop an images or", From d6b56177a27dd3cf139a5f8f73ea2da3eab0b800 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Tue, 21 Nov 2023 16:00:30 +0100 Subject: [PATCH 183/252] fst stats in Grafana --- .../src/main/resources/thingsboard.yml | 6 +- .../cache/RedisTbTransactionalCache.java | 12 +- .../server/common/data/FSTUtils.java | 122 +----------------- .../server/common/data/FstStatsService.java | 15 +-- .../queue/util/ProtoWithFSTService.java | 13 +- .../common/stats/FstStatsServiceImpl.java | 42 ++++++ 6 files changed, 73 insertions(+), 137 deletions(-) rename application/src/main/java/org/thingsboard/server/service/stats/FstStats.java => common/data/src/main/java/org/thingsboard/server/common/data/FstStatsService.java (59%) create mode 100644 common/stats/src/main/java/org/thingsboard/server/common/stats/FstStatsServiceImpl.java diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index bba7800a28..ecc89c84c5 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -1680,8 +1680,4 @@ management: web: exposure: # Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics). - include: '${METRICS_ENDPOINTS_EXPOSE:info}' - -fst: - stats: - printInterval: "${FST_STATS_PRINT_INTERVAL:600000}" # 10 min by default \ No newline at end of file + include: '${METRICS_ENDPOINTS_EXPOSE:info}' \ No newline at end of file diff --git a/common/cache/src/main/java/org/thingsboard/server/cache/RedisTbTransactionalCache.java b/common/cache/src/main/java/org/thingsboard/server/cache/RedisTbTransactionalCache.java index c5af954392..c2dd66dde0 100644 --- a/common/cache/src/main/java/org/thingsboard/server/cache/RedisTbTransactionalCache.java +++ b/common/cache/src/main/java/org/thingsboard/server/cache/RedisTbTransactionalCache.java @@ -17,6 +17,7 @@ package org.thingsboard.server.cache; import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.support.NullValue; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.RedisConnectionFactory; @@ -27,6 +28,7 @@ import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import org.springframework.data.redis.core.types.Expiration; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; +import org.thingsboard.server.common.data.FstStatsService; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.util.JedisClusterCRC16; @@ -44,6 +46,9 @@ public abstract class RedisTbTransactionalCache STATS = new ConcurrentHashMap<>(); - @SuppressWarnings("unchecked") public static T decode(byte[] byteArray) { - long startTime = System.nanoTime(); - T result = byteArray != null && byteArray.length > 0 ? (T) CONFIG.asObject(byteArray) : null; - long endTime = System.nanoTime(); - - if (log.isDebugEnabled() && result != null) { - String className = result.getClass().getSimpleName(); - STATS.computeIfAbsent(className, k -> new Stats()).incrementDecode(endTime - startTime); - } - - return result; - } - - public static byte[] encode(T msg) { - long startTime = System.nanoTime(); - byte[] result = CONFIG.asByteArray(msg); - long endTime = System.nanoTime(); - - if (log.isDebugEnabled() && msg != null) { - String className = msg.getClass().getSimpleName(); - STATS.computeIfAbsent(className, k -> new Stats()).incrementEncode(endTime - startTime); - } - - return result; - } - - public static void printStats() { - if (log.isDebugEnabled()) { - List topDecode = STATS.entrySet().stream() - .filter(e -> e.getValue().getAvgDecodeTime() > 0) - .sorted((e1, e2) -> Long.compare(e2.getValue().getAvgDecodeTime(), e1.getValue().getAvgDecodeTime())) - .limit(5) - .map(Map.Entry::getKey) - .collect(Collectors.toList()); - - List topEncode = STATS.entrySet().stream() - .filter(e -> e.getValue().getAvgEncodeTime() > 0) - .sorted((e1, e2) -> Long.compare(e2.getValue().getAvgEncodeTime(), e1.getValue().getAvgEncodeTime())) - .limit(5) - .map(Map.Entry::getKey) - .collect(Collectors.toList()); - - List topDecodeCount = STATS.entrySet().stream() - .filter(e -> e.getValue().getDecodeCount().get() > 0) - .sorted((e1, e2) -> Long.compare(e2.getValue().getDecodeCount().get(), e1.getValue().getDecodeCount().get())) - .limit(5) - .map(Map.Entry::getKey) - .collect(Collectors.toList()); - - List topEncodeCount = STATS.entrySet().stream() - .filter(e -> e.getValue().getEncodeCount().get() > 0) - .sorted((e1, e2) -> Long.compare(e2.getValue().getEncodeCount().get(), e1.getValue().getEncodeCount().get())) - .limit(5) - .map(Map.Entry::getKey) - .collect(Collectors.toList()); - - for (Map.Entry entry : STATS.entrySet()) { - Stats stats = entry.getValue(); - if (stats.isNotEmpty()) { - log.debug("[FST stats] [{}] {}", entry.getKey(), stats); - stats.reset(); - } - } - - log.debug("[FST stats] Top 5 slowest 'decode' {}", topDecode); - log.debug("[FST stats] Top 5 slowest 'encode' {}", topEncode); - log.debug("[FST stats] Top 5 'decode' count {}", topDecodeCount); - log.debug("[FST stats] Top 5 'encode' count {}", topEncodeCount); - } + return byteArray != null && byteArray.length > 0 ? (T) CONFIG.asObject(byteArray) : null; } - @Data - private static class Stats { - private final AtomicLong encodeCount = new AtomicLong(); - private final AtomicLong decodeCount = new AtomicLong(); - private final AtomicLong totalEncodeTime = new AtomicLong(); - private final AtomicLong totalDecodeTime = new AtomicLong(); - - private void incrementEncode(long time) { - encodeCount.incrementAndGet(); - totalEncodeTime.addAndGet(time); - } - - private void incrementDecode(long time) { - decodeCount.incrementAndGet(); - totalDecodeTime.addAndGet(time); - } - - private boolean isNotEmpty() { - return encodeCount.get() > 0 || decodeCount.get() > 0; - } - - private long getAvgEncodeTime() { - long count = encodeCount.get(); - return count > 0 ? totalEncodeTime.get() / count : 0; - } - - private long getAvgDecodeTime() { - long count = decodeCount.get(); - return count > 0 ? totalDecodeTime.get() / count : 0; - } - - private void reset() { - encodeCount.set(0); - decodeCount.set(0); - totalEncodeTime.set(0); - totalDecodeTime.set(0); - } - - @Override - public String toString() { - return String.format("decodeCount [%d] avgDecodeTime [%d] encodedCount [%d] avgEncodeTime [%d]", decodeCount.get(), getAvgDecodeTime(), encodeCount.get(), getAvgEncodeTime()); - } + public static byte[] encode(T msq) { + return CONFIG.asByteArray(msq); } } diff --git a/application/src/main/java/org/thingsboard/server/service/stats/FstStats.java b/common/data/src/main/java/org/thingsboard/server/common/data/FstStatsService.java similarity index 59% rename from application/src/main/java/org/thingsboard/server/service/stats/FstStats.java rename to common/data/src/main/java/org/thingsboard/server/common/data/FstStatsService.java index d66b9fd138..06f0d1a528 100644 --- a/application/src/main/java/org/thingsboard/server/service/stats/FstStats.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/FstStatsService.java @@ -13,17 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.stats; +package org.thingsboard.server.common.data; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.FSTUtils; +public interface FstStatsService { -@Service -public class FstStats { + void incrementEncode(Class clazz); + + void incrementDecode(Class clazz); - @Scheduled(initialDelayString = "${fst.stats.printInterval}", fixedDelayString = "${fst.stats.printInterval}") - public void printStats() { - FSTUtils.printStats(); - } } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/util/ProtoWithFSTService.java b/common/queue/src/main/java/org/thingsboard/server/queue/util/ProtoWithFSTService.java index b0d0b182e9..e35c86e070 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/util/ProtoWithFSTService.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/util/ProtoWithFSTService.java @@ -17,8 +17,10 @@ package org.thingsboard.server.queue.util; import lombok.extern.slf4j.Slf4j; import org.nustaq.serialization.FSTConfiguration; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.FSTUtils; +import org.thingsboard.server.common.data.FstStatsService; import java.util.Optional; @@ -26,12 +28,17 @@ import java.util.Optional; @Service public class ProtoWithFSTService implements DataDecodingEncodingService { + @Autowired + private FstStatsService fstStatsService; + public static final FSTConfiguration CONFIG = FSTConfiguration.createDefaultConfiguration(); @Override public Optional decode(byte[] byteArray) { try { - return Optional.ofNullable(FSTUtils.decode(byteArray)); + Optional optional = Optional.ofNullable(FSTUtils.decode(byteArray)); + optional.ifPresent(obj -> fstStatsService.incrementDecode(obj.getClass())); + return optional; } catch (IllegalArgumentException e) { log.error("Error during deserialization message, [{}]", e.getMessage()); return Optional.empty(); @@ -41,7 +48,9 @@ public class ProtoWithFSTService implements DataDecodingEncodingService { @Override public byte[] encode(T msq) { - return FSTUtils.encode(msq); + var bytes = FSTUtils.encode(msq); + fstStatsService.incrementEncode(msq.getClass()); + return bytes; } diff --git a/common/stats/src/main/java/org/thingsboard/server/common/stats/FstStatsServiceImpl.java b/common/stats/src/main/java/org/thingsboard/server/common/stats/FstStatsServiceImpl.java new file mode 100644 index 0000000000..5d78b8c36a --- /dev/null +++ b/common/stats/src/main/java/org/thingsboard/server/common/stats/FstStatsServiceImpl.java @@ -0,0 +1,42 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.common.stats; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.FstStatsService; + +import java.util.concurrent.ConcurrentHashMap; + +@Service +public class FstStatsServiceImpl implements FstStatsService { + private final ConcurrentHashMap encodeCounters = new ConcurrentHashMap<>(); + private final ConcurrentHashMap decodeCounters = new ConcurrentHashMap<>(); + + @Autowired + private StatsFactory statsFactory; + + @Override + public void incrementEncode(Class clazz) { + encodeCounters.computeIfAbsent(clazz.getSimpleName(), key -> statsFactory.createStatsCounter("fstEncode", key)).increment(); + } + + @Override + public void incrementDecode(Class clazz) { + decodeCounters.computeIfAbsent(clazz.getSimpleName(), key -> statsFactory.createStatsCounter("fstDecode", key)).increment(); + } + +} From c09caa22dca0b1272f3f0d1a04f76066d3b7a439 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Tue, 21 Nov 2023 17:43:02 +0200 Subject: [PATCH 184/252] Improvements to queries and cache cleanup --- .../main/data/upgrade/3.6.1/schema_update.sql | 6 +-- .../queue/DefaultTbCoreConsumerService.java | 16 ++++++- .../resource/DefaultTbImageService.java | 46 +++++++++++++++++-- .../service/resource/TbImageService.java | 3 ++ .../src/main/resources/thingsboard.yml | 2 +- common/cluster-api/src/main/proto/queue.proto | 7 +++ .../dashboard/DashboardInfoRepository.java | 4 +- .../sql/widget/WidgetTypeInfoRepository.java | 4 +- 8 files changed, 76 insertions(+), 12 deletions(-) diff --git a/application/src/main/data/upgrade/3.6.1/schema_update.sql b/application/src/main/data/upgrade/3.6.1/schema_update.sql index 27c7adf523..19362bb38e 100644 --- a/application/src/main/data/upgrade/3.6.1/schema_update.sql +++ b/application/src/main/data/upgrade/3.6.1/schema_update.sql @@ -27,9 +27,9 @@ $$ END IF; END; $$; -ALTER TABLE resource - ADD COLUMN IF NOT EXISTS descriptor varchar, - ADD COLUMN IF NOT EXISTS preview bytea; + +ALTER TABLE resource ADD COLUMN IF NOT EXISTS descriptor varchar; +ALTER TABLE resource ADD COLUMN IF NOT EXISTS preview bytea; ALTER TABLE resource ADD COLUMN IF NOT EXISTS external_id uuid -- RESOURCES UPDATE END diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java index 754537ef11..ac074f983b 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java @@ -82,6 +82,8 @@ import org.thingsboard.server.service.profile.TbAssetProfileCache; import org.thingsboard.server.service.profile.TbDeviceProfileCache; import org.thingsboard.server.service.queue.processing.AbstractConsumerService; import org.thingsboard.server.service.queue.processing.IdMsgPair; +import org.thingsboard.server.service.resource.ImageCacheKey; +import org.thingsboard.server.service.resource.TbImageService; import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService; import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequestActorMsg; import org.thingsboard.server.service.security.auth.jwt.settings.JwtSettingsService; @@ -140,6 +142,7 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService> usageStatsConsumer; private final TbQueueConsumer> firmwareStatesConsumer; + private final TbImageService imageService; protected volatile ExecutorService consumersExecutor; protected volatile ExecutorService usageStatsExecutor; @@ -165,7 +168,8 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService jwtSettingsService, NotificationSchedulerService notificationSchedulerService, - NotificationRuleProcessor notificationRuleProcessor) { + NotificationRuleProcessor notificationRuleProcessor, + TbImageService imageService) { super(actorContext, encodingService, tenantProfileCache, deviceProfileCache, assetProfileCache, apiUsageStateService, partitionService, eventPublisher, tbCoreQueueFactory.createToCoreNotificationsMsgConsumer(), jwtSettingsService); this.mainConsumer = tbCoreQueueFactory.createToCoreMsgConsumer(); this.usageStatsConsumer = tbCoreQueueFactory.createToUsageStatsServiceMsgConsumer(); @@ -181,6 +185,7 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService cache; - public DefaultTbImageService(ImageService imageService, - @Value("${cache.image.etag.timeToLiveInMinutes:120}") int cacheTtl, - @Value("${cache.image.etag.maxSize:200000}") int cacheMaxSize) { + public DefaultTbImageService(TbClusterService clusterService, ImageService imageService, + @Value("${cache.image.etag.timeToLiveInMinutes:44640}") int cacheTtl, + @Value("${cache.image.etag.maxSize:10000}") int cacheMaxSize) { + this.clusterService = clusterService; this.imageService = imageService; this.cache = Caffeine.newBuilder() .expireAfterAccess(cacheTtl, TimeUnit.MINUTES) @@ -60,13 +69,33 @@ public class DefaultTbImageService extends AbstractTbEntityService implements Tb cache.put(imageCacheKey, etag); } + @Override + public void evictETag(ImageCacheKey imageCacheKey) { + cache.invalidate(imageCacheKey); + } + @Override public TbResourceInfo save(TbResource image, User user) throws Exception { ActionType actionType = image.getId() == null ? ActionType.ADDED : ActionType.UPDATED; TenantId tenantId = image.getTenantId(); try { + var oldEtag = getEtag(image); TbResourceInfo savedImage = imageService.saveImage(image); notificationEntityService.logEntityAction(tenantId, savedImage.getId(), savedImage, actionType, user); + if (oldEtag.isPresent()) { + var newEtag = getEtag(savedImage); + if (newEtag.isPresent() && !oldEtag.get().equals(newEtag.get())) { + evictETag(new ImageCacheKey(image.getTenantId(), image.getResourceKey(), false)); + evictETag(new ImageCacheKey(image.getTenantId(), image.getResourceKey(), true)); + clusterService.broadcastToCore(TransportProtos.ToCoreNotificationMsg.newBuilder() + .setResourceCacheInvalidateMsg(TransportProtos.ResourceCacheInvalidateMsg.newBuilder() + .setTenantIdMSB(tenantId.getId().getMostSignificantBits()) + .setTenantIdLSB(tenantId.getId().getLeastSignificantBits()) + .setResourceKey(image.getResourceKey()) + .build()) + .build()); + } + } return savedImage; } catch (Exception e) { image.setData(null); @@ -75,6 +104,17 @@ public class DefaultTbImageService extends AbstractTbEntityService implements Tb } } + private Optional getEtag(TbResourceInfo image) throws JsonProcessingException { + var descriptor = image.getDescriptor(ImageDescriptor.class); + return Optional.ofNullable(descriptor != null ? descriptor.getEtag() : null); + } + + private Optional getPreviewEtag(TbResourceInfo image) throws JsonProcessingException { + var descriptor = image.getDescriptor(ImageDescriptor.class); + descriptor = descriptor != null ? descriptor.getPreviewDescriptor() : null; + return Optional.ofNullable(descriptor != null ? descriptor.getEtag() : null); + } + @Override public TbResourceInfo save(TbResourceInfo imageInfo, User user) { TenantId tenantId = imageInfo.getTenantId(); diff --git a/application/src/main/java/org/thingsboard/server/service/resource/TbImageService.java b/application/src/main/java/org/thingsboard/server/service/resource/TbImageService.java index 2e2f95e4c7..adcf3736f1 100644 --- a/application/src/main/java/org/thingsboard/server/service/resource/TbImageService.java +++ b/application/src/main/java/org/thingsboard/server/service/resource/TbImageService.java @@ -19,6 +19,7 @@ import org.thingsboard.server.common.data.TbImageDeleteResult; import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.TbResourceInfo; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.id.TenantId; public interface TbImageService { @@ -31,4 +32,6 @@ public interface TbImageService { String getETag(ImageCacheKey imageCacheKey); void putETag(ImageCacheKey imageCacheKey, String etag); + + void evictETag(ImageCacheKey imageCacheKey); } diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 6dde5de957..faa799b7e4 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -589,7 +589,7 @@ cache: image: etag: timeToLiveInMinutes: "${CACHE_SPECS_IMAGE_ETAGS_TTL:44640}" # Image ETags cache TTL - maxSize: "${CACHE_SPECS_IMAGE_ETAGS_MAX_SIZE:1000000}" # 0 means the cache is disabled + maxSize: "${CACHE_SPECS_IMAGE_ETAGS_MAX_SIZE:10000}" # 0 means the cache is disabled systemImagesBrowserTtlInMinutes: "${CACHE_SPECS_IMAGE_SYSTEM_BROWSER_TTL:0}" # Browser cache TTL for system images in minutes. 0 means the cache is disabled tenantImagesBrowserTtlInMinutes: "${CACHE_SPECS_IMAGE_TENANT_BROWSER_TTL:0}" # Browser cache TTL for tenant images in minutes. 0 means the cache is disabled diff --git a/common/cluster-api/src/main/proto/queue.proto b/common/cluster-api/src/main/proto/queue.proto index 0cb91faee0..57922e3177 100644 --- a/common/cluster-api/src/main/proto/queue.proto +++ b/common/cluster-api/src/main/proto/queue.proto @@ -306,6 +306,12 @@ message CoreStartupMsg { int64 ts = 3; } +message ResourceCacheInvalidateMsg { + int64 tenantIdMSB = 1; + int64 tenantIdLSB = 2; + string resourceKey = 3; +} + message LwM2MRegistrationRequestMsg { string tenantId = 1; string endpoint = 2; @@ -1273,6 +1279,7 @@ message ToCoreNotificationMsg { EdgeEventUpdateMsgProto edgeEventUpdate = 14; ToEdgeSyncRequestMsgProto toEdgeSyncRequest = 15; FromEdgeSyncResponseMsgProto fromEdgeSyncResponse = 16; + ResourceCacheInvalidateMsg resourceCacheInvalidateMsg = 17; } /* Messages that are handled by ThingsBoard RuleEngine Service */ diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/DashboardInfoRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/DashboardInfoRepository.java index a92da55c03..35c765dccf 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/DashboardInfoRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/DashboardInfoRepository.java @@ -78,12 +78,12 @@ public interface DashboardInfoRepository extends JpaRepository findByTenantAndImageLink(@Param("tenantId") UUID tenantId, @Param("imageLink") String imageLink, @Param("lmt") int lmt); @Query(nativeQuery = true, - value = "SELECT * FROM dashboard d WHERE d.image = :imageLink or d.configuration ILIKE CONCAT('%', :imageLink, '%') limit :lmt" + value = "SELECT * FROM dashboard d WHERE d.image = :imageLink or d.configuration ILIKE CONCAT('%\"', :imageLink, '\"%') limit :lmt" ) List findByImageLink(@Param("imageLink") String imageLink, @Param("lmt") int lmt); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetTypeInfoRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetTypeInfoRepository.java index e9dcca7914..83797d7baa 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetTypeInfoRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetTypeInfoRepository.java @@ -198,13 +198,13 @@ public interface WidgetTypeInfoRepository extends JpaRepository findByTenantAndImageUrl(@Param("tenantId") UUID tenantId, @Param("imageLink") String imageLink, @Param("lmt") int lmt); @Query(nativeQuery = true, value = "SELECT * FROM widget_type_info_view wti WHERE wti.id IN " + - "(select id from widget_type where image = :imageLink or descriptor ILIKE CONCAT('%', :imageLink, '%') limit :lmt)" + "(select id from widget_type where image = :imageLink or descriptor ILIKE CONCAT('%\"', :imageLink, '\"%') limit :lmt)" ) List findByImageUrl(@Param("imageLink") String imageLink, @Param("lmt") int lmt); } From 3e6f101a881cde1e587d2a9253240748a33742cb Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Tue, 21 Nov 2023 20:27:24 +0200 Subject: [PATCH 185/252] UI: Improve edit image. Improve image gallery grid view. --- ui-ngx/src/app/core/services/menu.service.ts | 7 ++ .../grid/scroll-grid.component.html | 6 +- .../components/grid/scroll-grid.component.ts | 8 ++ .../image/image-dialog.component.html | 102 ++++++++++-------- .../image/image-dialog.component.scss | 32 ++++-- .../image/image-dialog.component.ts | 52 ++++----- .../image/image-gallery.component.html | 98 ++++++++++++++--- .../image/image-gallery.component.scss | 61 +++++++++-- .../image/image-gallery.component.ts | 70 +++++++----- .../datasource/scroll-grid-datasource.ts | 22 ++++ 10 files changed, 325 insertions(+), 133 deletions(-) diff --git a/ui-ngx/src/app/core/services/menu.service.ts b/ui-ngx/src/app/core/services/menu.service.ts index aa622af51b..924d741cf6 100644 --- a/ui-ngx/src/app/core/services/menu.service.ts +++ b/ui-ngx/src/app/core/services/menu.service.ts @@ -534,6 +534,13 @@ export class MenuService { } ] }, + { + id: 'images', + name: 'image.gallery', + type: 'link', + path: '/resources/images', + icon: 'filter' + }, { id: 'resources_library', name: 'resource.resources-library', diff --git a/ui-ngx/src/app/modules/home/components/grid/scroll-grid.component.html b/ui-ngx/src/app/modules/home/components/grid/scroll-grid.component.html index 849dc2e6a9..5d6c066c94 100644 --- a/ui-ngx/src/app/modules/home/components/grid/scroll-grid.component.html +++ b/ui-ngx/src/app/modules/home/components/grid/scroll-grid.component.html @@ -16,14 +16,14 @@ --> - +
-
+
- +
diff --git a/ui-ngx/src/app/modules/home/components/grid/scroll-grid.component.ts b/ui-ngx/src/app/modules/home/components/grid/scroll-grid.component.ts index b2b6e79fe9..dcced7b3c0 100644 --- a/ui-ngx/src/app/modules/home/components/grid/scroll-grid.component.ts +++ b/ui-ngx/src/app/modules/home/components/grid/scroll-grid.component.ts @@ -112,4 +112,12 @@ export class ScrollGridComponent implements OnInit, AfterViewInit, OnChang public update() { this.dataSource.update(); } + + public updateItem(index: number, item: T) { + this.dataSource.updateItem(index, item); + } + + public deleteItem(index: number) { + this.dataSource.deleteItem(index); + } } diff --git a/ui-ngx/src/app/modules/home/components/image/image-dialog.component.html b/ui-ngx/src/app/modules/home/components/image/image-dialog.component.html index 23ef4dcb2a..6526da5392 100644 --- a/ui-ngx/src/app/modules/home/components/image/image-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/image/image-dialog.component.html @@ -15,7 +15,7 @@ limitations under the License. --> -
+

{{ (readonly ? 'image.image-details' : 'image.edit-image') | translate }}

@@ -30,65 +30,73 @@
- - image.name - - - {{ 'image.name-required' | translate }} - - +
+ + image.name + + + {{ 'image.name-required' | translate }} + + + + +
-
image.image-preview
-
-
- -
-
-
{{ image.descriptor.width }}x{{ image.descriptor.height }}
- -
{{ image.descriptor.size | fileSize }}
-
- - +
+ + +
+
+
+ +
+
+
{{ image.descriptor.width }}x{{ image.descriptor.height }}
+ +
{{ image.descriptor.size | fileSize }}
+
-
- - -
diff --git a/ui-ngx/src/app/modules/home/components/image/image-dialog.component.scss b/ui-ngx/src/app/modules/home/components/image/image-dialog.component.scss index 813974e541..f812ff4fb9 100644 --- a/ui-ngx/src/app/modules/home/components/image/image-dialog.component.scss +++ b/ui-ngx/src/app/modules/home/components/image/image-dialog.component.scss @@ -14,9 +14,24 @@ * limitations under the License. */ +@import '../../../../../scss/constants'; + :host { + .tb-image-dialog { + @media #{$mat-gt-xs} { + width: 50vh; + } + .mat-mdc-dialog-content { + max-height: 100%; + } + fieldset { + height: 100%; + display: flex; + flex-direction: column; + } + } .tb-image-container { - width: fit-content; + height: 100%; border-radius: 4px; border: 1px solid rgba(0, 0, 0, 0.05); padding: 12px; @@ -26,17 +41,12 @@ gap: 12px; .tb-image-content { + height: 100%; + width: 100%; display: flex; flex-direction: column; align-items: flex-start; gap: 8px; - .tb-image-preview-title { - color: rgba(0, 0, 0, 0.54); - font-size: 13px; - font-style: normal; - font-weight: 500; - line-height: 16px; - } .tb-image-preview-container { position: relative; width: 100%; @@ -49,7 +59,9 @@ justify-content: center; z-index: 1; .tb-image-preview-spacer { - margin-top: 100%; + @media #{$mat-gt-xs} { + margin-top: 100%; + } } .tb-image-preview { position: absolute; @@ -75,6 +87,8 @@ .tb-image-actions { display: flex; align-items: center; + align-self: stretch; + justify-content: space-between; gap: 8px; color: rgba(0,0,0,0.54); } diff --git a/ui-ngx/src/app/modules/home/components/image/image-dialog.component.ts b/ui-ngx/src/app/modules/home/components/image/image-dialog.component.ts index 72b2bbf85c..c248275b99 100644 --- a/ui-ngx/src/app/modules/home/components/image/image-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/image/image-dialog.component.ts @@ -14,19 +14,12 @@ /// limitations under the License. /// -import { ChangeDetectorRef, Component, Inject, OnInit, SkipSelf } from '@angular/core'; +import { Component, Inject, OnInit, SkipSelf } from '@angular/core'; import { ErrorStateMatcher } from '@angular/material/core'; import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; -import { - FormGroupDirective, - NgForm, - UntypedFormBuilder, - UntypedFormControl, - UntypedFormGroup, - Validators -} from '@angular/forms'; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { DialogComponent } from '@shared/components/dialog.component'; import { Router } from '@angular/router'; import { ImageService } from '@core/http/image.service'; @@ -45,11 +38,10 @@ export interface ImageDialogData { @Component({ selector: 'tb-image-dialog', templateUrl: './image-dialog.component.html', - providers: [{provide: ErrorStateMatcher, useExisting: ImageDialogComponent}], styleUrls: ['./image-dialog.component.scss'] }) export class ImageDialogComponent extends - DialogComponent implements OnInit, ErrorStateMatcher { + DialogComponent implements OnInit { image: ImageResourceInfo; @@ -57,8 +49,6 @@ export class ImageDialogComponent extends imageFormGroup: UntypedFormGroup; - submitted = false; - imageChanged = false; imagePreviewData: UrlHolder; @@ -68,8 +58,7 @@ export class ImageDialogComponent extends private imageService: ImageService, private dialog: MatDialog, @Inject(MAT_DIALOG_DATA) private data: ImageDialogData, - @SkipSelf() private errorStateMatcher: ErrorStateMatcher, - public dialogRef: MatDialogRef, + public dialogRef: MatDialogRef, public fb: UntypedFormBuilder) { super(store, router, dialogRef); this.image = data.image; @@ -88,14 +77,25 @@ export class ImageDialogComponent extends } } - isErrorState(control: UntypedFormControl | null, form: FormGroupDirective | NgForm | null): boolean { - const originalErrorState = this.errorStateMatcher.isErrorState(control, form); - const customErrorState = !!(control && control.invalid && this.submitted); - return originalErrorState || customErrorState; + cancel(): void { + this.dialogRef.close(this.imageChanged ? this.image : null); } - cancel(): void { - this.dialogRef.close(this.imageChanged); + revertInfo(): void { + this.imageFormGroup.get('title').setValue(this.image.title); + this.imageFormGroup.markAsPristine(); + } + + saveInfo(): void { + const title: string = this.imageFormGroup.get('title').value; + const image = {...this.image, ...{title}}; + this.imageService.updateImageInfo(image).subscribe( + (saved) => { + this.image = saved; + this.imageChanged = true; + this.imageFormGroup.markAsPristine(); + } + ); } downloadImage($event) { @@ -134,14 +134,4 @@ export class ImageDialogComponent extends }); } - save(): void { - this.submitted = true; - const title: string = this.imageFormGroup.get('title').value; - const image = {...this.image, ...{title}}; - this.imageService.updateImageInfo(image).subscribe( - () => { - this.dialogRef.close(true); - } - ); - } } diff --git a/ui-ngx/src/app/modules/home/components/image/image-gallery.component.html b/ui-ngx/src/app/modules/home/components/image/image-gallery.component.html index 44bf4a4fd5..1f071790be 100644 --- a/ui-ngx/src/app/modules/home/components/image/image-gallery.component.html +++ b/ui-ngx/src/app/modules/home/components/image/image-gallery.component.html @@ -19,7 +19,7 @@
- image.gallery + image.gallery
@@ -43,7 +43,7 @@
-
+
+
-
+
+ + + + + + + +
@@ -243,9 +286,40 @@
image.no-images
- -
+ +
+
+
+ + + +
+
+
+ +
{{ item.title }}
diff --git a/ui-ngx/src/app/modules/home/components/image/image-gallery.component.scss b/ui-ngx/src/app/modules/home/components/image/image-gallery.component.scss index fe48ba197f..0fe15f8e3e 100644 --- a/ui-ngx/src/app/modules/home/components/image/image-gallery.component.scss +++ b/ui-ngx/src/app/modules/home/components/image/image-gallery.component.scss @@ -77,17 +77,70 @@ $tb-button-selected-color: rgb(255, 110, 64) !default; } .tb-image-card { + position: relative; height: 100%; - border-radius: 8px; + border-radius: 4px; + box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.24); padding: 8px; display: flex; gap: 8px; flex-direction: column; cursor: pointer; - transition: box-shadow 0.2s; + + .tb-image-card-overlay { + position: absolute; + pointer-events: none; + inset: 0; + border-radius: 4px; + z-index: 2; + display: flex; + flex-direction: column; + .tb-image-card-overlay-buttons { + width: 100%; + display: flex; + justify-content: flex-end; + align-items: center; + color: rgba(0,0,0,0.78); + opacity: 0; + transition: opacity 0.5s; + } + } + + &:hover { + .tb-image-card-overlay { + .tb-image-card-overlay-buttons { + opacity: 1; + } + } + .tb-image-preview-container { + .tb-image-preview-overlay { + background: rgba(245,245,245,0.6); + backdrop-filter: blur(4px); + .mdc-button { + opacity: 1; + } + } + } + } .tb-image-preview-container { position: relative; + .tb-image-preview-overlay { + position: absolute; + inset: 0; + z-index: 1; + background: rgba(245,245,245,0); + backdrop-filter: none; + transition: all 0.5s; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + .mdc-button { + opacity: 0; + transition: opacity 0.5s; + } + } .tb-image-preview-spacer { margin-top: 100%; } @@ -152,10 +205,6 @@ $tb-button-selected-color: rgb(255, 110, 64) !default; } } } - - &:hover { - box-shadow: 0 0 28px 0 rgba(0, 0, 0, 0.24); - } } .table-container { diff --git a/ui-ngx/src/app/modules/home/components/image/image-gallery.component.ts b/ui-ngx/src/app/modules/home/components/image/image-gallery.component.ts index 2f0ee673bd..7e8cae6719 100644 --- a/ui-ngx/src/app/modules/home/components/image/image-gallery.component.ts +++ b/ui-ngx/src/app/modules/home/components/image/image-gallery.component.ts @@ -21,7 +21,7 @@ import { emptyPageData, PageData } from '@shared/models/page/page-data'; import { ImageService } from '@core/http/image.service'; import { TranslateService } from '@ngx-translate/core'; import { PageLink, PageQueryParam } from '@shared/models/page/page-link'; -import { catchError, debounceTime, distinctUntilChanged, map, skip, takeUntil, tap } from 'rxjs/operators'; +import { catchError, debounceTime, distinctUntilChanged, map, skip, take, takeUntil, tap } from 'rxjs/operators'; import { AfterViewInit, ChangeDetectorRef, @@ -38,7 +38,6 @@ import { MatPaginator } from '@angular/material/paginator'; import { MatSort, SortDirection } from '@angular/material/sort'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; -import { UtilsService } from '@core/services/utils.service'; import { DialogService } from '@core/services/dialog.service'; import { FormBuilder } from '@angular/forms'; import { Direction, SortOrder } from '@shared/models/page/sort-order'; @@ -52,19 +51,14 @@ import { NULL_UUID } from '@shared/models/id/has-uuid'; import { getCurrentAuthUser } from '@core/auth/auth.selectors'; import { Authority } from '@shared/models/authority.enum'; import { GridEntitiesFetchFunction, ScrollGridColumns } from '@home/models/datasource/scroll-grid-datasource'; -import { WidgetsBundle } from '@shared/models/widgets-bundle.model'; import { ScrollGridComponent } from '@home/components/grid/scroll-grid.component'; -import { - AddWidgetDialogComponent, - AddWidgetDialogData -} from '@home/components/dashboard-page/add-widget-dialog.component'; -import { Widget } from '@shared/models/widget.models'; import { MatDialog } from '@angular/material/dialog'; import { UploadImageDialogComponent, UploadImageDialogData } from '@home/components/image/upload-image-dialog.component'; import { ImageDialogComponent, ImageDialogData } from '@home/components/image/image-dialog.component'; +import { EntityBooleanFunction } from '@home/models/entity/entities-table-config.models'; @Component({ selector: 'tb-image-gallery', @@ -178,7 +172,7 @@ export class ImageGalleryComponent extends PageComponent implements OnInit, OnDe } } if (this.mode === 'list') { - this.dataSource = new ImagesDatasource(this.imageService); + this.dataSource = new ImagesDatasource(this.imageService, entity => !this.readonly(entity)); } } @@ -234,7 +228,7 @@ export class ImageGalleryComponent extends PageComponent implements OnInit, OnDe } this.mode = targetMode; if (this.mode === 'list') { - this.dataSource = new ImagesDatasource(this.imageService); + this.dataSource = new ImagesDatasource(this.imageService, entity => !this.readonly(entity)); } setTimeout(() => { this.updateMode(); @@ -341,6 +335,22 @@ export class ImageGalleryComponent extends PageComponent implements OnInit, OnDe } } + private imageUpdated(image: ImageResourceInfo, index = -1) { + if (this.mode === 'list') { + this.updateData(); + } else { + this.gridComponent.updateItem(index, image); + } + } + + private imageDeleted(index = -1) { + if (this.mode === 'list') { + this.updateData(); + } else { + this.gridComponent.deleteItem(index); + } + } + enterFilterMode() { this.textSearchMode = true; setTimeout(() => { @@ -366,7 +376,7 @@ export class ImageGalleryComponent extends PageComponent implements OnInit, OnDe return this.authUser.authority !== Authority.SYS_ADMIN && this.isSystem(image); } - deleteImage($event: Event, image: ImageResourceInfo) { + deleteImage($event: Event, image: ImageResourceInfo, itemIndex = -1) { if ($event) { $event.stopPropagation(); } @@ -378,7 +388,7 @@ export class ImageGalleryComponent extends PageComponent implements OnInit, OnDe if (result) { this.imageService.deleteImage(imageResourceType(image), image.resourceKey).subscribe( () => { - this.updateData(); + this.imageDeleted(itemIndex); } ); } @@ -440,12 +450,12 @@ export class ImageGalleryComponent extends PageComponent implements OnInit, OnDe }); } - editImage($event: Event, image: ImageResourceInfo) { + editImage($event: Event, image: ImageResourceInfo, itemIndex = -1) { if ($event) { $event.stopPropagation(); } this.dialog.open(ImageDialogComponent, { + ImageResourceInfo>(ImageDialogComponent, { disableClose: true, panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], data: { @@ -454,7 +464,7 @@ export class ImageGalleryComponent extends PageComponent implements OnInit, OnDe } }).afterClosed().subscribe((result) => { if (result) { - this.updateData(); + this.imageUpdated(result, itemIndex); } }); } @@ -486,7 +496,8 @@ class ImagesDatasource implements DataSource { public dataLoading = true; - constructor(private imageService: ImageService) { + constructor(private imageService: ImageService, + protected selectionEnabledFunction: EntityBooleanFunction) { } connect(collectionViewer: CollectionViewer): @@ -548,15 +559,24 @@ class ImagesDatasource implements DataSource { } masterToggle() { - const entities = this.entitiesSubject.getValue(); - const numSelected = this.selection.selected.length; - if (numSelected === entities.length) { - this.selection.clear(); - } else { - entities.forEach(row => { - this.selection.select(row); - }); - } + this.entitiesSubject.pipe( + tap((entities) => { + const numSelected = this.selection.selected.length; + if (numSelected === this.selectableEntitiesCount(entities)) { + this.selection.clear(); + } else { + entities.forEach(row => { + if (this.selectionEnabledFunction(row)) { + this.selection.select(row); + } + }); + } + }), + take(1) + ).subscribe(); } + private selectableEntitiesCount(entities: Array): number { + return entities.filter((entity) => this.selectionEnabledFunction(entity)).length; + } } diff --git a/ui-ngx/src/app/modules/home/models/datasource/scroll-grid-datasource.ts b/ui-ngx/src/app/modules/home/models/datasource/scroll-grid-datasource.ts index 83087eadc0..1298ec4f8f 100644 --- a/ui-ngx/src/app/modules/home/models/datasource/scroll-grid-datasource.ts +++ b/ui-ngx/src/app/modules/home/models/datasource/scroll-grid-datasource.ts @@ -88,6 +88,10 @@ export class ScrollGridDatasource extends DataSource<(T | GridCellType)[]> return !!this._subscription && !this._subscription.closed; } + get currentColumns(): number { + return this._columns; + } + public updateFilter(filter: F) { this.filter = filter; this.update(); @@ -123,6 +127,24 @@ export class ScrollGridDatasource extends DataSource<(T | GridCellType)[]> } } + public updateItem(index: number, item: T) { + this._data[index] = item; + this._dataUpdated(); + } + + public deleteItem(index: number) { + if (index < this._data.length) { + this._data.splice(index, 1); + this._totalElements--; + const rowsLength = this._totalElements ? Math.ceil(this._totalElements / this._columns) : 100000; + this._rows = Array.from({length: rowsLength}); + this._dataUpdated(); + if (this._hasNext) { + this._fetchDataFromRange(this._viewport.getRenderedRange()); + } + } + } + private _detectColumns(): number { if (typeof this.columns !== 'object') { return this.columns; From 414d758429aafbbe8e89cd74c1886bbe45236db3 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 22 Nov 2023 14:49:46 +0200 Subject: [PATCH 186/252] UI: Improve image grid loading. --- .../dashboard-widget-select.component.html | 4 +- .../dashboard-widget-select.component.ts | 6 +++ .../grid/scroll-grid.component.html | 3 +- .../components/grid/scroll-grid.component.ts | 52 +++++++++++++++++-- .../image/image-gallery.component.html | 10 +++- .../image/image-gallery.component.scss | 15 ++++++ .../image/image-gallery.component.ts | 7 ++- .../components/image-input.component.html | 2 +- 8 files changed, 88 insertions(+), 11 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.html b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.html index a32dc812cd..5bb677e814 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.html +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.html @@ -27,7 +27,7 @@ (itemWidth - 24) * 0.8 + 76 + }; + widgetBundlesFetchFunction: GridEntitiesFetchFunction; allWidgetsFetchFunction: GridEntitiesFetchFunction; widgetsFetchFunction: GridEntitiesFetchFunction; diff --git a/ui-ngx/src/app/modules/home/components/grid/scroll-grid.component.html b/ui-ngx/src/app/modules/home/components/grid/scroll-grid.component.html index 5d6c066c94..43b877d295 100644 --- a/ui-ngx/src/app/modules/home/components/grid/scroll-grid.component.html +++ b/ui-ngx/src/app/modules/home/components/grid/scroll-grid.component.html @@ -15,7 +15,8 @@ limitations under the License. --> - +
diff --git a/ui-ngx/src/app/modules/home/components/grid/scroll-grid.component.ts b/ui-ngx/src/app/modules/home/components/grid/scroll-grid.component.ts index dcced7b3c0..cf9f9f4cd3 100644 --- a/ui-ngx/src/app/modules/home/components/grid/scroll-grid.component.ts +++ b/ui-ngx/src/app/modules/home/components/grid/scroll-grid.component.ts @@ -15,10 +15,10 @@ /// import { - AfterViewInit, + AfterViewInit, ChangeDetectorRef, Component, Input, - OnChanges, + OnChanges, OnDestroy, OnInit, Renderer2, SimpleChanges, @@ -34,6 +34,14 @@ import { import { BreakpointObserver } from '@angular/cdk/layout'; import { isObject } from '@app/core/utils'; import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling'; +import { ResizeObserver } from '@juggle/resize-observer'; + +export type ItemSizeFunction = (itemWidth: number) => number; + +export interface ItemSizeStrategy { + defaultItemSize: number; + itemSizeFunction: ItemSizeFunction; +} @Component({ selector: 'tb-scroll-grid', @@ -41,7 +49,7 @@ import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling'; styleUrls: ['./scroll-grid.component.scss'], encapsulation: ViewEncapsulation.None }) -export class ScrollGridComponent implements OnInit, AfterViewInit, OnChanges { +export class ScrollGridComponent implements OnInit, AfterViewInit, OnChanges, OnDestroy { @ViewChild('viewport') viewport: CdkVirtualScrollViewport; @@ -56,7 +64,7 @@ export class ScrollGridComponent implements OnInit, AfterViewInit, OnChang filter: F; @Input() - itemSize = 200; + itemSize: number | ItemSizeStrategy = 200; @Input() gap = 12; @@ -75,17 +83,37 @@ export class ScrollGridComponent implements OnInit, AfterViewInit, OnChang dataSource: ScrollGridDatasource; + calculatedItemSize: number; + minBuffer: number; + maxBuffer: number; + + private contentResize$: ResizeObserver; + constructor(private breakpointObserver: BreakpointObserver, + private cd: ChangeDetectorRef, private renderer: Renderer2) { } ngOnInit(): void { + if (typeof this.itemSize === 'number') { + this.calculatedItemSize = this.itemSize; + } else { + this.calculatedItemSize = this.itemSize.defaultItemSize; + } + this.minBuffer = this.calculatedItemSize; + this.maxBuffer = this.calculatedItemSize * 2; this.dataSource = new ScrollGridDatasource(this.breakpointObserver, this.columns, this.fetchFunction, this.filter); } ngAfterViewInit() { this.renderer.setStyle(this.viewport._contentWrapper.nativeElement, 'gap', this.gap + 'px'); this.renderer.setStyle(this.viewport._contentWrapper.nativeElement, 'padding', this.gap + 'px'); + if (!(typeof this.itemSize === 'number')) { + this.contentResize$ = new ResizeObserver(() => { + this.onContentResize(); + }); + this.contentResize$.observe(this.viewport._contentWrapper.nativeElement); + } } ngOnChanges(changes: SimpleChanges): void { @@ -97,6 +125,12 @@ export class ScrollGridComponent implements OnInit, AfterViewInit, OnChang } } + ngOnDestroy() { + if (this.contentResize$) { + this.contentResize$.disconnect(); + } + } + isObject(value: any): boolean { return isObject(value); } @@ -120,4 +154,14 @@ export class ScrollGridComponent implements OnInit, AfterViewInit, OnChang public deleteItem(index: number) { this.dataSource.deleteItem(index); } + + private onContentResize() { + const contentWidth = this.viewport._contentWrapper.nativeElement.getBoundingClientRect().width; + const columns = this.dataSource.currentColumns; + const itemWidth = (contentWidth - this.gap * (columns + 1)) / columns; + this.calculatedItemSize = (this.itemSize as ItemSizeStrategy).itemSizeFunction(itemWidth); + this.minBuffer = this.calculatedItemSize; + this.maxBuffer = this.calculatedItemSize * 2; + this.cd.markForCheck(); + } } diff --git a/ui-ngx/src/app/modules/home/components/image/image-gallery.component.html b/ui-ngx/src/app/modules/home/components/image/image-gallery.component.html index 1f071790be..bae808aaaf 100644 --- a/ui-ngx/src/app/modules/home/components/image/image-gallery.component.html +++ b/ui-ngx/src/app/modules/home/components/image/image-gallery.component.html @@ -260,7 +260,7 @@
-
TODO
+
+
+
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/image/image-gallery.component.scss b/ui-ngx/src/app/modules/home/components/image/image-gallery.component.scss index 0fe15f8e3e..0216445bee 100644 --- a/ui-ngx/src/app/modules/home/components/image/image-gallery.component.scss +++ b/ui-ngx/src/app/modules/home/components/image/image-gallery.component.scss @@ -153,6 +153,7 @@ $tb-button-selected-color: rgb(255, 110, 64) !default; } .tb-image-details { + min-height: 52px; flex: 1; display: flex; flex-direction: column; @@ -205,6 +206,14 @@ $tb-button-selected-color: rgb(255, 110, 64) !default; } } } + &.loading-cell { + .tb-image-preview-container, .tb-image-details { + background: linear-gradient(110deg, #ececec 8%, #f5f5f5 18%, #ececec 33%); + border-radius: 5px; + background-size: 200% 100%; + animation: 1s shine linear infinite; + } + } } .table-container { @@ -229,3 +238,9 @@ $tb-button-selected-color: rgb(255, 110, 64) !default; } } } + +@keyframes shine { + to { + background-position-x: -200%; + } +} diff --git a/ui-ngx/src/app/modules/home/components/image/image-gallery.component.ts b/ui-ngx/src/app/modules/home/components/image/image-gallery.component.ts index 7e8cae6719..4d28f0f810 100644 --- a/ui-ngx/src/app/modules/home/components/image/image-gallery.component.ts +++ b/ui-ngx/src/app/modules/home/components/image/image-gallery.component.ts @@ -51,7 +51,7 @@ import { NULL_UUID } from '@shared/models/id/has-uuid'; import { getCurrentAuthUser } from '@core/auth/auth.selectors'; import { Authority } from '@shared/models/authority.enum'; import { GridEntitiesFetchFunction, ScrollGridColumns } from '@home/models/datasource/scroll-grid-datasource'; -import { ScrollGridComponent } from '@home/components/grid/scroll-grid.component'; +import { ItemSizeStrategy, ScrollGridComponent } from '@home/components/grid/scroll-grid.component'; import { MatDialog } from '@angular/material/dialog'; import { UploadImageDialogComponent, @@ -112,6 +112,11 @@ export class ImageGalleryComponent extends PageComponent implements OnInit, OnDe gridImagesFetchFunction: GridEntitiesFetchFunction; gridImagesFilter = ''; + gridImagesItemSizeStrategy: ItemSizeStrategy = { + defaultItemSize: 200, + itemSizeFunction: itemWidth => itemWidth + 72 + }; + authUser = getCurrentAuthUser(this.store); private updateDataSubscription: Subscription; diff --git a/ui-ngx/src/app/shared/components/image-input.component.html b/ui-ngx/src/app/shared/components/image-input.component.html index 039611bbf3..96f674a745 100644 --- a/ui-ngx/src/app/shared/components/image-input.component.html +++ b/ui-ngx/src/app/shared/components/image-input.component.html @@ -64,7 +64,7 @@
-
{{ fileName }}
+
{{ fileName }}
dashboard.maximum-upload-file-size
From 3322957630744dd471a9c8923f1af6cefc6b067f Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Wed, 22 Nov 2023 15:45:14 +0200 Subject: [PATCH 187/252] Import Export of images. Search without system images --- .../controller/ControllerConstants.java | 2 + .../server/controller/ImageController.java | 40 ++++++++++++++++++- .../resource/DefaultTbImageService.java | 6 +++ .../server/common/data/ImageExportData.java | 33 +++++++++++++++ .../server/common/data/TbResourceInfo.java | 2 +- .../server/dao/resource/BaseImageService.java | 4 +- 6 files changed, 82 insertions(+), 5 deletions(-) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/ImageExportData.java diff --git a/application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java b/application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java index e6b4235cf9..ee24c7755c 100644 --- a/application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java +++ b/application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java @@ -142,6 +142,8 @@ public class ControllerConstants { protected static final String RESOURCE_INFO_DESCRIPTION = "Resource Info is a lightweight object that includes main information about the Resource excluding the heavyweight data. "; protected static final String RESOURCE_DESCRIPTION = "Resource is a heavyweight object that includes main information about the Resource and also data. "; + protected static final String RESOURCE_INCLUDE_SYSTEM_IMAGES_DESCRIPTION = "Use 'true' to include system images. Disabled by default. Ignored for requests by users with system administrator authority."; + protected static final String RESOURCE_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'substring' filter based on the resource title."; protected static final String RESOURCE_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, title, resourceType, tenantId"; protected static final String RESOURCE_TYPE_PROPERTY_ALLOWABLE_VALUES = "LWM2M_MODEL, JKS, PKCS_12, JS_MODULE"; diff --git a/application/src/main/java/org/thingsboard/server/controller/ImageController.java b/application/src/main/java/org/thingsboard/server/controller/ImageController.java index f3e9183053..3bfd342cad 100644 --- a/application/src/main/java/org/thingsboard/server/controller/ImageController.java +++ b/application/src/main/java/org/thingsboard/server/controller/ImageController.java @@ -26,6 +26,7 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.util.Base64Utils; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -38,6 +39,7 @@ import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import org.thingsboard.server.common.data.ImageDescriptor; +import org.thingsboard.server.common.data.ImageExportData; import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.TbResource; @@ -60,6 +62,7 @@ import java.util.concurrent.TimeUnit; import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION; +import static org.thingsboard.server.controller.ControllerConstants.RESOURCE_INCLUDE_SYSTEM_IMAGES_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.RESOURCE_SORT_PROPERTY_ALLOWABLE_VALUES; import static org.thingsboard.server.controller.ControllerConstants.RESOURCE_TEXT_SEARCH_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_ALLOWABLE_VALUES; @@ -140,6 +143,39 @@ public class ImageController extends BaseController { return downloadIfChanged(type, key, etag, false); } + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @GetMapping(value = IMAGE_URL + "/export") + public ImageExportData exportImage(@PathVariable String type, @PathVariable String key) throws Exception { + TbResourceInfo imageInfo = checkImageInfo(type, key, Operation.READ); + ImageDescriptor descriptor = imageInfo.getDescriptor(ImageDescriptor.class); + byte[] data = imageService.getImageData(imageInfo.getTenantId(), imageInfo.getId()); + return new ImageExportData(descriptor.getMediaType(), imageInfo.getFileName(), imageInfo.getTitle(), imageInfo.getResourceKey(), Base64Utils.encodeToString(data)); + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @PutMapping("/api/image/import") + public TbResourceInfo importImage(@RequestBody ImageExportData imageData) throws Exception { + SecurityUser user = getCurrentUser(); + TbResource image = new TbResource(); + image.setTenantId(user.getTenantId()); + accessControlService.checkPermission(user, Resource.TB_RESOURCE, Operation.CREATE, null, image); + + image.setFileName(imageData.getFileName()); + if (StringUtils.isNotEmpty(imageData.getTitle())) { + image.setTitle(imageData.getTitle()); + } else { + image.setTitle(imageData.getFileName()); + } + image.setResourceKey(imageData.getResourceKey()); + image.setResourceType(ResourceType.IMAGE); + ImageDescriptor descriptor = new ImageDescriptor(); + descriptor.setMediaType(imageData.getMediaType()); + image.setDescriptorValue(descriptor); + image.setData(Base64Utils.decodeFromString(imageData.getData())); + return tbImageService.save(image, user); + + } + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @GetMapping(value = IMAGE_URL + "/preview", produces = "image/png") public ResponseEntity downloadImagePreview(@PathVariable String type, @@ -161,6 +197,8 @@ public class ImageController extends BaseController { @RequestParam int pageSize, @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true) @RequestParam int page, + @ApiParam(value = RESOURCE_INCLUDE_SYSTEM_IMAGES_DESCRIPTION) + @RequestParam(required = false) boolean includeSystemImages, @ApiParam(value = RESOURCE_TEXT_SEARCH_DESCRIPTION) @RequestParam(required = false) String textSearch, @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = RESOURCE_SORT_PROPERTY_ALLOWABLE_VALUES) @@ -170,7 +208,7 @@ public class ImageController extends BaseController { // PE: generic permission PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); TenantId tenantId = getTenantId(); - if (getCurrentUser().getAuthority() == Authority.SYS_ADMIN) { + if (getCurrentUser().getAuthority() == Authority.SYS_ADMIN || includeSystemImages) { return checkNotNull(imageService.getImagesByTenantId(tenantId, pageLink)); } else { return checkNotNull(imageService.getAllImagesByTenantId(tenantId, pageLink)); diff --git a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java index 5f1e665ac4..822b46bc1d 100644 --- a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java +++ b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java @@ -80,6 +80,12 @@ public class DefaultTbImageService extends AbstractTbEntityService implements Tb TenantId tenantId = image.getTenantId(); try { var oldEtag = getEtag(image); + if (image.getId() == null && StringUtils.isNotEmpty(image.getResourceKey())) { + var existingImage = imageService.getImageInfoByTenantIdAndKey(tenantId, image.getResourceKey()); + if (existingImage != null) { + image.setId(existingImage.getId()); + } + } TbResourceInfo savedImage = imageService.saveImage(image); notificationEntityService.logEntityAction(tenantId, savedImage.getId(), savedImage, actionType, user); if (oldEtag.isPresent()) { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/ImageExportData.java b/common/data/src/main/java/org/thingsboard/server/common/data/ImageExportData.java new file mode 100644 index 0000000000..d1b33f202a --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ImageExportData.java @@ -0,0 +1,33 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.common.data; + +import io.swagger.annotations.ApiModel; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; + +@ApiModel +@Slf4j +@Data +public class ImageExportData { + + private final String mediaType; + private final String fileName; + private final String title; + private final String resourceKey; + private final String data; + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java index 61df84af44..ad292fdf67 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java @@ -109,7 +109,7 @@ public class TbResourceInfo extends BaseData implements HasName, H @JsonProperty(access = JsonProperty.Access.READ_ONLY) public String getLink() { if (resourceType == ResourceType.IMAGE) { - return "/api/images/" + (tenantId.isSysTenantId() ? "system" : "tenant") + "/" + resourceKey; + return "/api/images/" + ((tenantId == null || !tenantId.isSysTenantId()) ? "tenant" : "system") + "/" + resourceKey; // tenantId is null in case of export to git } return null; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java index 559e220b48..fa06ba6e95 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java @@ -15,11 +15,9 @@ */ package org.thingsboard.server.dao.resource; -import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.thingsboard.common.util.RegexUtils; @@ -91,7 +89,7 @@ public class BaseImageService extends BaseResourceService implements ImageServic @Transactional @Override public TbResourceInfo saveImage(TbResource image) throws Exception { - if (image.getId() == null) { + if (image.getId() == null && StringUtils.isEmpty(image.getResourceKey())) { image.setResourceKey(getUniqueKey(image.getTenantId(), image.getFileName())); } resourceValidator.validate(image, TbResourceInfo::getTenantId); From 3f2633fba55e718873712cfcf4d0ba7db19a157b Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Thu, 23 Nov 2023 12:22:15 +0200 Subject: [PATCH 188/252] extended widgetTypes access to customer user authority --- .../server/controller/WidgetTypeController.java | 4 ++-- .../controller/WidgetTypeControllerTest.java | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java b/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java index f90a196edf..0109a327e8 100644 --- a/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java +++ b/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java @@ -216,7 +216,7 @@ public class WidgetTypeController extends AutoCommitController { @ApiOperation(value = "Get all Widget types for specified Bundle (getBundleWidgetTypes)", notes = "Returns an array of Widget Type objects that belong to specified Widget Bundle." + WIDGET_TYPE_DESCRIPTION + " " + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/widgetTypes", params = {"widgetsBundleId"}, method = RequestMethod.GET) @ResponseBody public List getBundleWidgetTypes( @@ -248,7 +248,7 @@ public class WidgetTypeController extends AutoCommitController { @ApiOperation(value = "Get all Widget types details for specified Bundle (getBundleWidgetTypes)", notes = "Returns an array of Widget Type Details objects that belong to specified Widget Bundle." + WIDGET_TYPE_DETAILS_DESCRIPTION + " " + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/widgetTypesDetails", params = {"widgetsBundleId"}, method = RequestMethod.GET) @ResponseBody public List getBundleWidgetTypesDetails( diff --git a/application/src/test/java/org/thingsboard/server/controller/WidgetTypeControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/WidgetTypeControllerTest.java index 9cc9fd0596..553b0d6490 100644 --- a/application/src/test/java/org/thingsboard/server/controller/WidgetTypeControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/WidgetTypeControllerTest.java @@ -190,6 +190,20 @@ public class WidgetTypeControllerTest extends AbstractControllerTest { Collections.sort(loadedWidgetTypes, idComparator); Assert.assertEquals(widgetTypes, loadedWidgetTypes); + + loginCustomerUser(); + + List loadedWidgetTypes2 = doGetTyped("/api/widgetTypes?widgetsBundleId={widgetsBundleId}", + new TypeReference<>(){}, widgetsBundle.getId().getId().toString()); + Collections.sort(loadedWidgetTypes2, idComparator); + Assert.assertEquals(widgetTypes, loadedWidgetTypes2); + + List loadedWidgetTypes3 = doGetTyped("/api/widgetTypesDetails?widgetsBundleId={widgetsBundleId}", + new TypeReference<>(){}, widgetsBundle.getId().getId().toString()); + List widgetTypes3 = loadedWidgetTypes3.stream().map(WidgetType::new).collect(Collectors.toList()); + Collections.sort(widgetTypes3, idComparator); + Assert.assertEquals(widgetTypes3, loadedWidgetTypes); + } @Test From 12fafa40f534b327931f981a924efab169fe3ee0 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 23 Nov 2023 17:56:04 +0200 Subject: [PATCH 189/252] UI: Images import/export suppoer. Implement flow to delete images used by other entities. --- ui-ngx/src/app/core/http/image.service.ts | 16 +- .../components/details-panel.component.html | 2 +- .../entity/entity-details-page.component.html | 2 +- .../entity/entity-details-page.component.ts | 6 +- .../home/components/home-components.module.ts | 10 +- .../image/image-gallery.component.html | 8 +- .../image/image-gallery.component.ts | 218 +++++++++--------- .../image/image-references.component.html | 49 ++++ .../image/image-references.component.ts | 171 ++++++++++++++ .../components/image/images-datasource.ts | 130 +++++++++++ .../image/images-in-use-dialog.component.html | 82 +++++++ .../image/images-in-use-dialog.component.scss | 39 ++++ .../image/images-in-use-dialog.component.ts | 112 +++++++++ .../import-export/import-export.service.ts | 44 +++- .../widget/widget-library-routing.module.ts | 48 ++++ .../pages/widget/widget-type.component.html | 6 + .../widget-types-table-config.resolver.ts | 20 +- .../widget/widgets-bundle.component.html | 6 + .../widgets-bundles-table-config.resolver.ts | 16 +- .../shared/components/popover.component.ts | 3 + .../app/shared/components/popover.service.ts | 11 +- ui-ngx/src/app/shared/models/alarm.models.ts | 3 +- ui-ngx/src/app/shared/models/asset.models.ts | 6 +- .../src/app/shared/models/customer.model.ts | 3 +- .../src/app/shared/models/dashboard.models.ts | 3 +- ui-ngx/src/app/shared/models/device.models.ts | 8 +- ui-ngx/src/app/shared/models/edge.models.ts | 3 +- .../app/shared/models/entity-type.models.ts | 4 +- .../app/shared/models/entity-view.models.ts | 3 +- ui-ngx/src/app/shared/models/entity.models.ts | 5 + .../app/shared/models/notification.models.ts | 9 +- .../app/shared/models/ota-package.models.ts | 3 +- ui-ngx/src/app/shared/models/queue.models.ts | 3 +- .../src/app/shared/models/resource.models.ts | 42 +++- .../app/shared/models/rule-chain.models.ts | 3 +- ui-ngx/src/app/shared/models/user.model.ts | 3 +- ui-ngx/src/app/shared/models/widget.models.ts | 3 +- .../app/shared/models/widgets-bundle.model.ts | 3 +- .../assets/locale/locale.constant-en_US.json | 17 +- 39 files changed, 959 insertions(+), 164 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/components/image/image-references.component.html create mode 100644 ui-ngx/src/app/modules/home/components/image/image-references.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/image/images-datasource.ts create mode 100644 ui-ngx/src/app/modules/home/components/image/images-in-use-dialog.component.html create mode 100644 ui-ngx/src/app/modules/home/components/image/images-in-use-dialog.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/image/images-in-use-dialog.component.ts diff --git a/ui-ngx/src/app/core/http/image.service.ts b/ui-ngx/src/app/core/http/image.service.ts index e13acb0b3e..cde43b0d8b 100644 --- a/ui-ngx/src/app/core/http/image.service.ts +++ b/ui-ngx/src/app/core/http/image.service.ts @@ -25,7 +25,7 @@ import { ImageResourceInfo, imageResourceType, ImageResourceType, - IMAGES_URL_PREFIX, isImageResourceUrl + IMAGES_URL_PREFIX, isImageResourceUrl, ImageExportData } from '@shared/models/resource.models'; import { catchError, map, switchMap } from 'rxjs/operators'; import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; @@ -133,8 +133,18 @@ export class ImageService { ); } - public deleteImage(type: ImageResourceType, key: string, config?: RequestConfig) { - return this.http.delete(`${IMAGES_URL_PREFIX}/${type}/${encodeURIComponent(key)}`, defaultHttpOptionsFromConfig(config)); + public deleteImage(type: ImageResourceType, key: string, force = false, config?: RequestConfig) { + return this.http.delete(`${IMAGES_URL_PREFIX}/${type}/${encodeURIComponent(key)}?force=${force}`, defaultHttpOptionsFromConfig(config)); + } + + public exportImage(type: ImageResourceType, key: string, config?: RequestConfig): Observable { + return this.http.get(`${IMAGES_URL_PREFIX}/${type}/${encodeURIComponent(key)}/export`, + defaultHttpOptionsFromConfig(config)); + } + + public importImage(imageData: ImageExportData, config?: RequestConfig): Observable { + return this.http.put('/api/image/import', + imageData, defaultHttpOptionsFromConfig(config)); } } diff --git a/ui-ngx/src/app/modules/home/components/details-panel.component.html b/ui-ngx/src/app/modules/home/components/details-panel.component.html index 748723197f..bbb0e2006e 100644 --- a/ui-ngx/src/app/modules/home/components/details-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/details-panel.component.html @@ -56,7 +56,7 @@ - -
+ + + + + + + + + + + + + + {{ image.title }} + + + + + + {{ 'image.name' | translate }} + + + {{ translate.get('image.selected-images', {count: dataSource.selection.selected.length}) | async }} + + + + {{ image.title }} + + + + + + + + + + +
+
+ +
+
+ + +
+ + +
+
diff --git a/ui-ngx/src/app/modules/home/components/image/images-in-use-dialog.component.scss b/ui-ngx/src/app/modules/home/components/image/images-in-use-dialog.component.scss new file mode 100644 index 0000000000..a53c97404d --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/image/images-in-use-dialog.component.scss @@ -0,0 +1,39 @@ +/** + * Copyright © 2016-2023 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 { + .tb-images-in-use-content { + display: flex; + flex-direction: column; + &.multiple { + gap: 16px; + } + .table-container { + overflow: auto; + .mat-mdc-cell.mat-column-preview { + width: 50px; + height: 50px; + padding: 2px 12px; + display: block; + } + } + .tb-image-preview { + width: 50px; + height: 50px; + object-fit: contain; + border-radius: 4px; + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/image/images-in-use-dialog.component.ts b/ui-ngx/src/app/modules/home/components/image/images-in-use-dialog.component.ts new file mode 100644 index 0000000000..9e1e7708c9 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/image/images-in-use-dialog.component.ts @@ -0,0 +1,112 @@ +/// +/// Copyright © 2016-2023 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, Inject, OnInit, Renderer2, ViewContainerRef } from '@angular/core'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { DialogComponent } from '@shared/components/dialog.component'; +import { Router } from '@angular/router'; +import { ImageReferences, ImageResourceInfo, ImageResourceInfoWithReferences } from '@shared/models/resource.models'; +import { ImagesDatasource } from '@home/components/image/images-datasource'; +import { MatButton } from '@angular/material/button'; +import { TbPopoverService } from '@shared/components/popover.service'; +import { ImageReferencesComponent } from '@home/components/image/image-references.component'; +import { TranslateService } from '@ngx-translate/core'; + +export interface ImagesInUseDialogData { + multiple: boolean; + images: ImageResourceInfoWithReferences[]; +} + +@Component({ + selector: 'tb-images-in-use-dialog', + templateUrl: './images-in-use-dialog.component.html', + styleUrls: ['./images-in-use-dialog.component.scss'] +}) +export class ImagesInUseDialogComponent extends + DialogComponent implements OnInit { + + title: string; + message: string; + + references: ImageReferences; + + dataSource: ImagesDatasource; + + constructor(protected store: Store, + protected router: Router, + @Inject(MAT_DIALOG_DATA) public data: ImagesInUseDialogData, + public dialogRef: MatDialogRef, + public translate: TranslateService, + private renderer: Renderer2, + private viewContainerRef: ViewContainerRef, + private popoverService: TbPopoverService) { + super(store, router, dialogRef); + } + + ngOnInit(): void { + if (this.data.multiple) { + this.title = 'image.images-are-in-use'; + this.message = 'image.images-are-in-use-text'; + this.dataSource = new ImagesDatasource(null, this.data.images, entity => true); + } else { + this.title = 'image.image-is-in-use'; + this.message = 'image.image-is-in-use-text'; + this.references = this.data.images[0].references; + } + } + + cancel() { + this.dialogRef.close(null); + } + + delete() { + if (this.data.multiple) { + this.dialogRef.close(this.dataSource.selection.selected); + } else { + this.dialogRef.close(this.data.images); + } + } + + toggleShowReferences($event: Event, image: ImageResourceInfoWithReferences, referencesButton: MatButton) { + if ($event) { + $event.stopPropagation(); + } + const trigger = referencesButton._elementRef.nativeElement; + if (this.popoverService.hasPopover(trigger)) { + this.popoverService.hidePopover(trigger); + } else { + const referencesPopover = this.popoverService.displayPopover(trigger, this.renderer, + this.viewContainerRef, ImageReferencesComponent, 'top', true, null, + { + references: image.references + }, {}, {}, {}, + false, + visible => { + const addClasses = visible ? 'mdc-button--unelevated mat-mdc-unelevated-button' : 'mdc-button--outlined mat-mdc-outlined-button'; + const removeClasses = visible ? 'mdc-button--outlined mat-mdc-outlined-button' : 'mdc-button--unelevated mat-mdc-unelevated-button'; + addClasses.split(' ').forEach(clazz => { + this.renderer.addClass(trigger, clazz); + }); + removeClasses.split(' ').forEach(clazz => { + this.renderer.removeClass(trigger, clazz); + }); + }); + referencesPopover.tbComponentRef.instance.popoverComponent = referencesPopover; + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/import-export/import-export.service.ts b/ui-ngx/src/app/modules/home/components/import-export/import-export.service.ts index 620c1aaab3..1409be7c1a 100644 --- a/ui-ngx/src/app/modules/home/components/import-export/import-export.service.ts +++ b/ui-ngx/src/app/modules/home/components/import-export/import-export.service.ts @@ -21,7 +21,7 @@ import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { ActionNotificationShow } from '@core/notification/notification.actions'; import { Dashboard, DashboardLayoutId } from '@shared/models/dashboard.models'; -import { deepClone, guid, isDefined, isObject, isString, isUndefined } from '@core/utils'; +import { deepClone, guid, isDefined, isNotEmptyStr, isObject, isString, isUndefined } from '@core/utils'; import { WINDOW } from '@core/services/window.service'; import { DOCUMENT } from '@angular/common'; import { @@ -79,6 +79,8 @@ import { ExportWidgetsBundleDialogData, ExportWidgetsBundleDialogResult } from '@home/components/import-export/export-widgets-bundle-dialog.component'; +import { ImageService } from '@core/http/image.service'; +import { ImageExportData, ImageResourceInfo, ImageResourceType } from '@shared/models/resource.models'; // @dynamic @Injectable() @@ -99,12 +101,44 @@ export class ImportExportService { private deviceService: DeviceService, private assetService: AssetService, private edgeService: EdgeService, + private imageService: ImageService, private utils: UtilsService, private itembuffer: ItemBufferService, private dialog: MatDialog) { } + public exportImage(type: ImageResourceType, key: string) { + this.imageService.exportImage(type, key).subscribe( + { + next: (imageData) => { + let name = imageData.title; + name = name.toLowerCase().replace(/\W/g, '_'); + this.exportToPc(imageData, name); + }, + error: (e) => { + this.handleExportError(e, 'image.export-failed-error'); + } + } + ); + } + + public importImage(): Observable { + return this.openImportDialog('image.import-image', 'image.image-json-file').pipe( + mergeMap((imageData: ImageExportData) => { + if (!this.validateImportedImage(imageData)) { + this.store.dispatch(new ActionNotificationShow( + {message: this.translate.instant('image.invalid-image-json-file-error'), + type: 'error'})); + throw new Error('Invalid image JSON file'); + } else { + return this.imageService.importImage(imageData); + } + }), + catchError(() => of(null)) + ); + } + public exportDashboard(dashboardId: string) { this.dashboardService.getDashboard(dashboardId).subscribe( (dashboard) => { @@ -818,6 +852,14 @@ export class ImportExportService { type: 'error'})); } + private validateImportedImage(image: ImageExportData): boolean { + return !(!isNotEmptyStr(image.data) + || !isNotEmptyStr(image.title) + || !isNotEmptyStr(image.fileName) + || !isNotEmptyStr(image.mediaType) + || !isNotEmptyStr(image.resourceKey)); + } + private validateImportedDashboard(dashboard: Dashboard): boolean { if (isUndefined(dashboard.title) || isUndefined(dashboard.configuration)) { return false; diff --git a/ui-ngx/src/app/modules/home/pages/widget/widget-library-routing.module.ts b/ui-ngx/src/app/modules/home/pages/widget/widget-library-routing.module.ts index 8519b9d4d0..9b14558086 100644 --- a/ui-ngx/src/app/modules/home/pages/widget/widget-library-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/widget/widget-library-routing.module.ts @@ -31,6 +31,8 @@ import { ConfirmOnExitGuard } from '@core/guards/confirm-on-exit.guard'; import { RouterTabsComponent } from '@home/components/router-tabs.component'; import { WidgetTypesTableConfigResolver } from '@home/pages/widget/widget-types-table-config.resolver'; import { WidgetsBundleWidgetsComponent } from '@home/pages/widget/widgets-bundle-widgets.component'; +import { EntityDetailsPageComponent } from '@home/components/entity/entity-details-page.component'; +import { entityDetailsPageBreadcrumbLabelFunction } from '@home/pages/home-pages.models'; export interface WidgetEditorData { widgetTypeDetails: WidgetTypeDetails; @@ -122,6 +124,29 @@ const widgetTypesRoutes: Routes = [ entitiesTableConfig: WidgetTypesTableConfigResolver } }, + { + path: 'details', + children: [ + { + path: ':entityId', + component: EntityDetailsPageComponent, + canDeactivate: [ConfirmOnExitGuard], + data: { + breadcrumb: { + labelFunction: entityDetailsPageBreadcrumbLabelFunction, + icon: 'now_widgets' + } as BreadCrumbConfig, + auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN], + title: 'widget.widgets', + hideTabs: true, + backNavigationCommands: ['../..'] + }, + resolve: { + entitiesTableConfig: WidgetTypesTableConfigResolver + } + } + ] + }, ...widgetEditorRouter ] }, @@ -148,6 +173,29 @@ const widgetsBundlesRoutes: Routes = [ entitiesTableConfig: WidgetsBundlesTableConfigResolver } }, + { + path: 'details', + children: [ + { + path: ':entityId', + component: EntityDetailsPageComponent, + canDeactivate: [ConfirmOnExitGuard], + data: { + breadcrumb: { + labelFunction: entityDetailsPageBreadcrumbLabelFunction, + icon: 'now_widgets' + } as BreadCrumbConfig, + auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN], + title: 'widgets-bundle.widgets-bundles', + hideTabs: true, + backNavigationCommands: ['../..'] + }, + resolve: { + entitiesTableConfig: WidgetsBundlesTableConfigResolver + } + } + ] + }, { path: ':widgetsBundleId', data: { diff --git a/ui-ngx/src/app/modules/home/pages/widget/widget-type.component.html b/ui-ngx/src/app/modules/home/pages/widget/widget-type.component.html index 9db9780b70..46068bf9f8 100644 --- a/ui-ngx/src/app/modules/home/pages/widget/widget-type.component.html +++ b/ui-ngx/src/app/modules/home/pages/widget/widget-type.component.html @@ -22,6 +22,12 @@ [fxShow]="!isEdit"> {{ 'widget.edit' | translate }} + + - -
- + +
+
+ image.gallery +
+
+ +
+
+ +
+ {{ 'image.include-system-images' | translate }}
-
-
- -
- - - - + + + +
+
- - +
+ {{ 'image.include-system-images' | translate }}
diff --git a/ui-ngx/src/app/modules/home/components/image/image-gallery.component.scss b/ui-ngx/src/app/modules/home/components/image/image-gallery.component.scss index 0216445bee..0b7b5e3c65 100644 --- a/ui-ngx/src/app/modules/home/components/image/image-gallery.component.scss +++ b/ui-ngx/src/app/modules/home/components/image/image-gallery.component.scss @@ -28,6 +28,18 @@ $tb-button-selected-color: rgb(255, 110, 64) !default; border-radius: 4px; } + .mat-mdc-table-toolbar { + &.multi-row { + &.mat-toolbar-single-row { + height: 112px; + } + .mat-mdc-slide-toggle { + display: flex; + min-height: 48px; + } + } + } + .tb-images-title { padding-right: 20px; white-space: nowrap; diff --git a/ui-ngx/src/app/modules/home/components/image/image-gallery.component.ts b/ui-ngx/src/app/modules/home/components/image/image-gallery.component.ts index 1b59f34455..43938f2515 100644 --- a/ui-ngx/src/app/modules/home/components/image/image-gallery.component.ts +++ b/ui-ngx/src/app/modules/home/components/image/image-gallery.component.ts @@ -69,6 +69,11 @@ import { } from '@home/components/image/images-in-use-dialog.component'; import { ImagesDatasource } from '@home/components/image/images-datasource'; +interface GridImagesFilter { + search: string; + includeSystemImages: boolean; +} + @Component({ selector: 'tb-image-gallery', templateUrl: './image-gallery.component.html', @@ -104,6 +109,7 @@ export class ImageGalleryComponent extends PageComponent implements OnInit, OnDe dataSource: ImagesDatasource; textSearch = this.fb.control('', {nonNullable: true}); + includeSystemImages = this.fb.control(false); gridColumns: ScrollGridColumns = { columns: 2, @@ -118,8 +124,11 @@ export class ImageGalleryComponent extends PageComponent implements OnInit, OnDe } }; - gridImagesFetchFunction: GridEntitiesFetchFunction; - gridImagesFilter = ''; + gridImagesFetchFunction: GridEntitiesFetchFunction; + gridImagesFilter: GridImagesFilter = { + search: '', + includeSystemImages: false + }; gridImagesItemSizeStrategy: ItemSizeStrategy = { defaultItemSize: 200, @@ -148,16 +157,16 @@ export class ImageGalleryComponent extends PageComponent implements OnInit, OnDe super(store); this.gridImagesFetchFunction = (pageSize, page, filter) => { - const pageLink = new PageLink(pageSize, page, filter, { + const pageLink = new PageLink(pageSize, page, filter.search, { property: 'createdTime', direction: Direction.DESC }); - return this.imageService.getImages(pageLink); + return this.imageService.getImages(pageLink, filter.includeSystemImages); }; } ngOnInit(): void { - this.displayedColumns = ['select', 'preview', 'title', 'createdTime', 'resolution', 'size', 'system', 'actions']; + this.displayedColumns = this.computeDisplayedColumns(); let sortOrder: SortOrder = this.defaultSortOrder; this.pageSizeOptions = [this.defaultPageSize, this.defaultPageSize * 2, this.defaultPageSize * 3]; const routerQueryParams: PageQueryParam = this.route.snapshot.queryParams; @@ -208,7 +217,7 @@ export class ImageGalleryComponent extends PageComponent implements OnInit, OnDe this.textSearch.valueChanges.pipe( debounceTime(150), distinctUntilChanged((prev, current) => - ((this.mode === 'list' ? this.pageLink.textSearch : this.gridImagesFilter) ?? '') === current.trim()), + ((this.mode === 'list' ? this.pageLink.textSearch : this.gridImagesFilter.search) ?? '') === current.trim()), takeUntil(this.destroy$) ).subscribe(value => { if (this.mode === 'list') { @@ -224,7 +233,25 @@ export class ImageGalleryComponent extends PageComponent implements OnInit, OnDe this.updateData(); } } else { - this.gridImagesFilter = isNotEmptyStr(value) ? value.trim() : null; + this.gridImagesFilter = { + search: isNotEmptyStr(value) ? encodeURI(value) : null, + includeSystemImages: this.includeSystemImages.value + }; + this.cd.markForCheck(); + } + }); + this.includeSystemImages.valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe(value => { + this.displayedColumns = this.computeDisplayedColumns(); + this.gridImagesFilter = { + search: this.gridImagesFilter.search, + includeSystemImages: value + }; + if (this.mode === 'list') { + this.paginator.pageIndex = 0; + this.updateData(); + } else { this.cd.markForCheck(); } }); @@ -253,6 +280,19 @@ export class ImageGalleryComponent extends PageComponent implements OnInit, OnDe } } + public get isSysAdmin(): boolean { + return this.authUser.authority === Authority.SYS_ADMIN; + } + + private computeDisplayedColumns(): string[] { + const columns = ['select', 'preview', 'title', 'createdTime', 'resolution', 'size']; + if (!this.isSysAdmin && this.includeSystemImages.value) { + columns.push('system'); + } + columns.push('actions'); + return columns; + } + private updateMode() { if (this.mode === 'list') { this.initListMode(); @@ -346,7 +386,7 @@ export class ImageGalleryComponent extends PageComponent implements OnInit, OnDe } else { this.pageLink.sortOrder = null; } - this.dataSource.loadEntities(this.pageLink); + this.dataSource.loadEntities(this.pageLink, this.includeSystemImages.value); } else { this.gridComponent.update(); } @@ -386,7 +426,7 @@ export class ImageGalleryComponent extends PageComponent implements OnInit, OnDe } isSystem(image?: ImageResourceInfo): boolean { - return image?.tenantId?.id === NULL_UUID; + return !this.isSysAdmin && image?.tenantId?.id === NULL_UUID; } readonly(image?: ImageResourceInfo): boolean { diff --git a/ui-ngx/src/app/modules/home/components/image/images-datasource.ts b/ui-ngx/src/app/modules/home/components/image/images-datasource.ts index 604b96d25b..8be2f30cc0 100644 --- a/ui-ngx/src/app/modules/home/components/image/images-datasource.ts +++ b/ui-ngx/src/app/modules/home/components/image/images-datasource.ts @@ -64,10 +64,10 @@ export class ImagesDatasource implements DataSource { } } - loadEntities(pageLink: PageLink): Observable> { + loadEntities(pageLink: PageLink, includeSystemImages = false): Observable> { this.dataLoading = true; const result = new ReplaySubject>(); - this.fetchEntities(pageLink).pipe( + this.fetchEntities(pageLink, includeSystemImages).pipe( tap(() => { this.selection.clear(); }), @@ -83,8 +83,8 @@ export class ImagesDatasource implements DataSource { return result; } - fetchEntities(pageLink: PageLink): Observable> { - return this.imageService.getImages(pageLink); + fetchEntities(pageLink: PageLink, includeSystemImages = false): Observable> { + return this.imageService.getImages(pageLink, includeSystemImages); } isAllSelected(): Observable { 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 b48cd84a5e..9bd9ef140c 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2981,7 +2981,8 @@ "delete-image-in-use-text": "If you still want to delete the image, click the Delete anyway button.", "system-entities": "System entities:", "entities": "entities:", - "references": "References" + "references": "References", + "include-system-images": "Include system images" }, "image-input": { "drop-images-or": "Drag and drop an images or", From 18e9a5916c0b8f830fa424b9fd82600032e76aac Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Fri, 24 Nov 2023 13:33:41 +0200 Subject: [PATCH 191/252] Provide OOP concept for Edge entities MsgConstructor: add factory and 2 versions implementation --- .../service/edge/EdgeContextComponent.java | 2 +- .../rpc/constructor/AssetMsgConstructor.java | 71 ------- .../rpc/constructor/DeviceMsgConstructor.java | 169 ---------------- .../constructor/RuleChainMsgConstructor.java | 83 -------- .../TenantProfileMsgConstructor.java | 60 ------ .../WidgetsBundleMsgConstructor.java | 77 -------- .../alarm/AlarmMsgConstructor.java | 25 +++ .../alarm/AlarmMsgConstructorFactory.java | 45 +++++ .../AlarmMsgConstructorV1.java} | 24 +-- .../alarm/AlarmMsgConstructorV2.java | 36 ++++ .../asset/AssetMsgConstructor.java | 35 ++++ .../asset/AssetMsgConstructorFactory.java | 45 +++++ .../AssetMsgConstructorV1.java} | 47 +++-- .../asset/AssetMsgConstructorV2.java | 44 +++++ .../asset/BaseAssetMsgConstructor.java | 41 ++++ .../customer/BaseCustomerMsgConstructor.java | 31 +++ .../customer/CustomerMsgConstructor.java | 28 +++ .../CustomerMsgConstructorFactory.java | 45 +++++ .../CustomerMsgConstructorV1.java} | 26 +-- .../customer/CustomerMsgConstructorV2.java | 35 ++++ .../BaseDashboardMsgConstructor.java | 31 +++ .../dashboard/DashboardMsgConstructor.java | 28 +++ .../DashboardMsgConstructorFactory.java | 45 +++++ .../DashboardMsgConstructorV1.java} | 26 +-- .../dashboard/DashboardMsgConstructorV2.java | 35 ++++ .../device/BaseDeviceMsgConstructor.java | 102 ++++++++++ .../device/DeviceMsgConstructor.java | 38 ++++ .../device/DeviceMsgConstructorFactory.java | 45 +++++ .../DeviceMsgConstructorV1.java} | 75 +++++-- .../device/DeviceMsgConstructorV2.java | 51 +++++ .../{ => edge}/EdgeMsgConstructor.java | 2 +- .../BaseEntityViewMsgConstructor.java | 31 +++ .../entityview/EntityViewMsgConstructor.java | 28 +++ .../EntityViewMsgConstructorFactory.java | 45 +++++ .../EntityViewMsgConstructorV1.java} | 26 +-- .../EntityViewMsgConstructorV2.java | 35 ++++ .../ota/BaseOtaPackageMsgConstructor.java | 31 +++ .../ota/OtaPackageMsgConstructor.java | 28 +++ .../ota/OtaPackageMsgConstructorFactory.java | 45 +++++ .../OtaPackageMsgConstructorV1.java} | 26 +-- .../ota/OtaPackageMsgConstructorV2.java | 35 ++++ .../queue/BaseQueueMsgConstructor.java | 31 +++ .../queue/QueueMsgConstructor.java | 28 +++ .../queue/QueueMsgConstructorFactory.java | 45 +++++ .../QueueMsgConstructorV1.java} | 24 +-- .../queue/QueueMsgConstructorV2.java | 35 ++++ .../relation/RelationMsgConstructor.java | 25 +++ .../RelationMsgConstructorFactory.java | 45 +++++ .../RelationMsgConstructorV1.java} | 16 +- .../relation/RelationMsgConstructorV2.java | 34 ++++ .../resource/BaseResourceMsgConstructor.java | 31 +++ .../resource/ResourceMsgConstructor.java | 28 +++ .../ResourceMsgConstructorFactory.java | 45 +++++ .../ResourceMsgConstructorV1.java} | 29 +-- .../resource/ResourceMsgConstructorV2.java | 35 ++++ ... => BaseRuleChainMetadataConstructor.java} | 11 -- .../rule/BaseRuleChainMsgConstructor.java | 44 +++++ .../RuleChainMetadataConstructorFactory.java | 6 +- .../RuleChainMetadataConstructorV330.java | 2 + .../RuleChainMetadataConstructorV340.java | 2 + .../RuleChainMetadataConstructorV362.java | 29 +++ .../rule/RuleChainMsgConstructor.java | 37 ++++ .../rule/RuleChainMsgConstructorFactory.java | 45 +++++ .../rule/RuleChainMsgConstructorV1.java | 45 +++++ .../rule/RuleChainMsgConstructorV2.java | 39 ++++ .../settings/AdminSettingsMsgConstructor.java | 24 +++ .../AdminSettingsMsgConstructorFactory.java | 45 +++++ .../AdminSettingsMsgConstructorV1.java} | 16 +- .../AdminSettingsMsgConstructorV2.java | 32 +++ .../EntityDataMsgConstructor.java | 5 +- .../tenant/TenantMsgConstructor.java | 30 +++ .../tenant/TenantMsgConstructorFactory.java | 45 +++++ .../TenantMsgConstructorV1.java} | 38 +++- .../tenant/TenantMsgConstructorV2.java | 41 ++++ .../user/BaseUserMsgConstructor.java | 31 +++ .../constructor/user/UserMsgConstructor.java | 32 +++ .../user/UserMsgConstructorFactory.java | 45 +++++ .../UserMsgConstructorV1.java} | 40 +--- .../user/UserMsgConstructorV2.java | 42 ++++ .../widget/BaseWidgetMsgConstructor.java | 43 ++++ .../widget/WidgetMsgConstructor.java | 38 ++++ .../widget/WidgetMsgConstructorFactory.java | 45 +++++ .../WidgetMsgConstructorV1.java} | 48 +++-- .../widget/WidgetMsgConstructorV2.java | 48 +++++ .../edge/rpc/processor/BaseEdgeProcessor.java | 80 +++----- .../processor/alarm/BaseAlarmProcessor.java | 4 +- .../processor/asset/AssetEdgeProcessor.java | 6 +- .../asset/AssetProfileEdgeProcessor.java | 4 +- .../customer/CustomerEdgeProcessor.java | 4 +- .../dashboard/DashboardEdgeProcessor.java | 4 +- .../processor/device/DeviceEdgeProcessor.java | 20 +- .../device/DeviceProfileEdgeProcessor.java | 4 +- .../entityview/EntityViewEdgeProcessor.java | 4 +- .../ota/OtaPackageEdgeProcessor.java | 4 +- .../processor/queue/QueueEdgeProcessor.java | 6 +- .../relation/RelationEdgeProcessor.java | 2 +- .../resource/ResourceEdgeProcessor.java | 4 +- .../rule/RuleChainEdgeProcessor.java | 6 +- .../settings/AdminSettingsEdgeProcessor.java | 3 +- .../processor/tenant/TenantEdgeProcessor.java | 4 +- .../tenant/TenantProfileEdgeProcessor.java | 2 +- .../rpc/processor/user/UserEdgeProcessor.java | 6 +- .../widget/WidgetBundleEdgeProcessor.java | 4 +- .../widget/WidgetTypeEdgeProcessor.java | 7 +- .../RuleChainMsgConstructorTest.java | 15 +- .../rpc/processor/BaseEdgeProcessorTest.java | 184 ++++++++++++++---- 106 files changed, 2661 insertions(+), 892 deletions(-) delete mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetMsgConstructor.java delete mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceMsgConstructor.java delete mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java delete mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/TenantProfileMsgConstructor.java delete mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetsBundleMsgConstructor.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/alarm/AlarmMsgConstructor.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/alarm/AlarmMsgConstructorFactory.java rename application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/{AlarmMsgConstructor.java => alarm/AlarmMsgConstructorV1.java} (67%) create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/alarm/AlarmMsgConstructorV2.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/AssetMsgConstructor.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/AssetMsgConstructorFactory.java rename application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/{AssetProfileMsgConstructor.java => asset/AssetMsgConstructorV1.java} (63%) create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/AssetMsgConstructorV2.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/BaseAssetMsgConstructor.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/BaseCustomerMsgConstructor.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/CustomerMsgConstructor.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/CustomerMsgConstructorFactory.java rename application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/{CustomerMsgConstructor.java => customer/CustomerMsgConstructorV1.java} (65%) create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/CustomerMsgConstructorV2.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/BaseDashboardMsgConstructor.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/DashboardMsgConstructor.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/DashboardMsgConstructorFactory.java rename application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/{DashboardMsgConstructor.java => dashboard/DashboardMsgConstructorV1.java} (60%) create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/DashboardMsgConstructorV2.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/BaseDeviceMsgConstructor.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructor.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructorFactory.java rename application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/{DeviceProfileMsgConstructor.java => device/DeviceMsgConstructorV1.java} (55%) create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructorV2.java rename application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/{ => edge}/EdgeMsgConstructor.java (96%) create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/BaseEntityViewMsgConstructor.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/EntityViewMsgConstructor.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/EntityViewMsgConstructorFactory.java rename application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/{EntityViewMsgConstructor.java => entityview/EntityViewMsgConstructorV1.java} (67%) create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/EntityViewMsgConstructorV2.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/BaseOtaPackageMsgConstructor.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/OtaPackageMsgConstructor.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/OtaPackageMsgConstructorFactory.java rename application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/{OtaPackageMsgConstructor.java => ota/OtaPackageMsgConstructorV1.java} (69%) create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/OtaPackageMsgConstructorV2.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/BaseQueueMsgConstructor.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/QueueMsgConstructor.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/QueueMsgConstructorFactory.java rename application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/{QueueMsgConstructor.java => queue/QueueMsgConstructorV1.java} (77%) create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/QueueMsgConstructorV2.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/relation/RelationMsgConstructor.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/relation/RelationMsgConstructorFactory.java rename application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/{RelationMsgConstructor.java => relation/RelationMsgConstructorV1.java} (74%) create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/relation/RelationMsgConstructorV2.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/BaseResourceMsgConstructor.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/ResourceMsgConstructor.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/ResourceMsgConstructorFactory.java rename application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/{ResourceMsgConstructor.java => resource/ResourceMsgConstructorV1.java} (57%) create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/ResourceMsgConstructorV2.java rename application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/{AbstractRuleChainMetadataConstructor.java => BaseRuleChainMetadataConstructor.java} (87%) create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/BaseRuleChainMsgConstructor.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV362.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructor.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructorFactory.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructorV1.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructorV2.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/settings/AdminSettingsMsgConstructor.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/settings/AdminSettingsMsgConstructorFactory.java rename application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/{AdminSettingsMsgConstructor.java => settings/AdminSettingsMsgConstructorV1.java} (67%) create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/settings/AdminSettingsMsgConstructorV2.java rename application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/{ => telemetry}/EntityDataMsgConstructor.java (98%) create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructor.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorFactory.java rename application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/{TenantMsgConstructor.java => tenant/TenantMsgConstructorV1.java} (62%) create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV2.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/BaseUserMsgConstructor.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/UserMsgConstructor.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/UserMsgConstructorFactory.java rename application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/{UserMsgConstructor.java => user/UserMsgConstructorV1.java} (57%) create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/UserMsgConstructorV2.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/BaseWidgetMsgConstructor.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/WidgetMsgConstructor.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/WidgetMsgConstructorFactory.java rename application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/{WidgetTypeMsgConstructor.java => widget/WidgetMsgConstructorV1.java} (67%) create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/WidgetMsgConstructorV2.java diff --git a/application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java b/application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java index 0d2b6675c9..c62bd81242 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java @@ -42,7 +42,7 @@ import org.thingsboard.server.dao.widget.WidgetTypeService; import org.thingsboard.server.dao.widget.WidgetsBundleService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.edge.rpc.EdgeEventStorageSettings; -import org.thingsboard.server.service.edge.rpc.constructor.EdgeMsgConstructor; +import org.thingsboard.server.service.edge.rpc.constructor.edge.EdgeMsgConstructor; import org.thingsboard.server.service.edge.rpc.processor.alarm.AlarmEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.asset.AssetEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.asset.AssetProfileEdgeProcessor; diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetMsgConstructor.java deleted file mode 100644 index f46ec7ab2d..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetMsgConstructor.java +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright © 2016-2023 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. - */ -package org.thingsboard.server.service.edge.rpc.constructor; - -import org.springframework.stereotype.Component; -import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.asset.Asset; -import org.thingsboard.server.common.data.id.AssetId; -import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg; -import org.thingsboard.server.gen.edge.v1.EdgeVersion; -import org.thingsboard.server.gen.edge.v1.UpdateMsgType; -import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; - -@Component -@TbCoreComponent -public class AssetMsgConstructor { - - public AssetUpdateMsg constructAssetUpdatedMsg(UpdateMsgType msgType, Asset asset, EdgeVersion edgeVersion) { - if (EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)) { - return constructDeprecatedAssetUpdateMsg(msgType, asset); - } - return AssetUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(asset)) - .setIdMSB(asset.getUuidId().getMostSignificantBits()) - .setIdLSB(asset.getUuidId().getLeastSignificantBits()).build(); - } - - private AssetUpdateMsg constructDeprecatedAssetUpdateMsg(UpdateMsgType msgType, Asset asset) { - AssetUpdateMsg.Builder builder = AssetUpdateMsg.newBuilder() - .setMsgType(msgType) - .setIdMSB(asset.getUuidId().getMostSignificantBits()) - .setIdLSB(asset.getUuidId().getLeastSignificantBits()) - .setName(asset.getName()) - .setType(asset.getType()); - if (asset.getLabel() != null) { - builder.setLabel(asset.getLabel()); - } - if (asset.getCustomerId() != null) { - builder.setCustomerIdMSB(asset.getCustomerId().getId().getMostSignificantBits()); - builder.setCustomerIdLSB(asset.getCustomerId().getId().getLeastSignificantBits()); - } - if (asset.getAssetProfileId() != null) { - builder.setAssetProfileIdMSB(asset.getAssetProfileId().getId().getMostSignificantBits()); - builder.setAssetProfileIdLSB(asset.getAssetProfileId().getId().getLeastSignificantBits()); - } - if (asset.getAdditionalInfo() != null) { - builder.setAdditionalInfo(JacksonUtil.toString(asset.getAdditionalInfo())); - } - return builder.build(); - } - - public AssetUpdateMsg constructAssetDeleteMsg(AssetId assetId) { - return AssetUpdateMsg.newBuilder() - .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) - .setIdMSB(assetId.getId().getMostSignificantBits()) - .setIdLSB(assetId.getId().getLeastSignificantBits()).build(); - } -} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceMsgConstructor.java deleted file mode 100644 index bbdfc098c5..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceMsgConstructor.java +++ /dev/null @@ -1,169 +0,0 @@ -/** - * Copyright © 2016-2023 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. - */ -package org.thingsboard.server.service.edge.rpc.constructor; - -import com.fasterxml.jackson.databind.JsonNode; -import com.google.protobuf.ByteString; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; -import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.Device; -import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.security.DeviceCredentials; -import org.thingsboard.server.gen.edge.v1.DeviceCredentialsUpdateMsg; -import org.thingsboard.server.gen.edge.v1.DeviceRpcCallMsg; -import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg; -import org.thingsboard.server.gen.edge.v1.EdgeVersion; -import org.thingsboard.server.gen.edge.v1.RpcRequestMsg; -import org.thingsboard.server.gen.edge.v1.RpcResponseMsg; -import org.thingsboard.server.gen.edge.v1.UpdateMsgType; -import org.thingsboard.server.queue.util.DataDecodingEncodingService; -import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; - -import java.util.UUID; - -@Component -@TbCoreComponent -public class DeviceMsgConstructor { - - @Autowired - private DataDecodingEncodingService dataDecodingEncodingService; - - public DeviceUpdateMsg constructDeviceUpdatedMsg(UpdateMsgType msgType, Device device, EdgeVersion edgeVersion) { - if (EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)) { - return constructDeprecatedDeviceUpdateMsg(msgType, device); - } - return DeviceUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(device)) - .setIdMSB(device.getId().getId().getMostSignificantBits()) - .setIdLSB(device.getId().getId().getLeastSignificantBits()).build(); - } - - private DeviceUpdateMsg constructDeprecatedDeviceUpdateMsg(UpdateMsgType msgType, Device device) { - DeviceUpdateMsg.Builder builder = DeviceUpdateMsg.newBuilder() - .setMsgType(msgType) - .setIdMSB(device.getId().getId().getMostSignificantBits()) - .setIdLSB(device.getId().getId().getLeastSignificantBits()) - .setName(device.getName()) - .setType(device.getType()); - if (device.getLabel() != null) { - builder.setLabel(device.getLabel()); - } - if (device.getCustomerId() != null) { - builder.setCustomerIdMSB(device.getCustomerId().getId().getMostSignificantBits()); - builder.setCustomerIdLSB(device.getCustomerId().getId().getLeastSignificantBits()); - } - if (device.getDeviceProfileId() != null) { - builder.setDeviceProfileIdMSB(device.getDeviceProfileId().getId().getMostSignificantBits()); - builder.setDeviceProfileIdLSB(device.getDeviceProfileId().getId().getLeastSignificantBits()); - } - if (device.getAdditionalInfo() != null) { - builder.setAdditionalInfo(JacksonUtil.toString(device.getAdditionalInfo())); - } - if (device.getFirmwareId() != null) { - builder.setFirmwareIdMSB(device.getFirmwareId().getId().getMostSignificantBits()) - .setFirmwareIdLSB(device.getFirmwareId().getId().getLeastSignificantBits()); - } - if (device.getSoftwareId() != null) { - builder.setSoftwareIdMSB(device.getSoftwareId().getId().getMostSignificantBits()) - .setSoftwareIdLSB(device.getSoftwareId().getId().getLeastSignificantBits()); - } - if (device.getDeviceData() != null) { - builder.setDeviceDataBytes(ByteString.copyFrom(dataDecodingEncodingService.encode(device.getDeviceData()))); - } - return builder.build(); - } - - public DeviceCredentialsUpdateMsg constructDeviceCredentialsUpdatedMsg(DeviceCredentials deviceCredentials, EdgeVersion edgeVersion) { - return EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion) - ? constructDeprecatedDeviceCredentialsUpdatedMsg(deviceCredentials) - : DeviceCredentialsUpdateMsg.newBuilder().setEntity(JacksonUtil.toString(deviceCredentials)).build(); - } - - private DeviceCredentialsUpdateMsg constructDeprecatedDeviceCredentialsUpdatedMsg(DeviceCredentials deviceCredentials) { - DeviceCredentialsUpdateMsg.Builder builder = DeviceCredentialsUpdateMsg.newBuilder() - .setDeviceIdMSB(deviceCredentials.getDeviceId().getId().getMostSignificantBits()) - .setDeviceIdLSB(deviceCredentials.getDeviceId().getId().getLeastSignificantBits()); - if (deviceCredentials.getCredentialsType() != null) { - builder.setCredentialsType(deviceCredentials.getCredentialsType().name()) - .setCredentialsId(deviceCredentials.getCredentialsId()); - } - if (deviceCredentials.getCredentialsValue() != null) { - builder.setCredentialsValue(deviceCredentials.getCredentialsValue()); - } - return builder.build(); - } - - public DeviceUpdateMsg constructDeviceDeleteMsg(DeviceId deviceId) { - return DeviceUpdateMsg.newBuilder() - .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) - .setIdMSB(deviceId.getId().getMostSignificantBits()) - .setIdLSB(deviceId.getId().getLeastSignificantBits()).build(); - } - - public DeviceRpcCallMsg constructDeviceRpcCallMsg(UUID deviceId, JsonNode body) { - DeviceRpcCallMsg.Builder builder = constructDeviceRpcMsg(deviceId, body); - if (body.has("error") || body.has("response")) { - RpcResponseMsg.Builder responseBuilder = RpcResponseMsg.newBuilder(); - if (body.has("error")) { - responseBuilder.setError(body.get("error").asText()); - } else { - responseBuilder.setResponse(body.get("response").asText()); - } - builder.setResponseMsg(responseBuilder.build()); - } else { - RpcRequestMsg.Builder requestBuilder = RpcRequestMsg.newBuilder(); - requestBuilder.setMethod(body.get("method").asText()); - requestBuilder.setParams(body.get("params").asText()); - builder.setRequestMsg(requestBuilder.build()); - } - return builder.build(); - } - - private DeviceRpcCallMsg.Builder constructDeviceRpcMsg(UUID deviceId, JsonNode body) { - DeviceRpcCallMsg.Builder builder = DeviceRpcCallMsg.newBuilder() - .setDeviceIdMSB(deviceId.getMostSignificantBits()) - .setDeviceIdLSB(deviceId.getLeastSignificantBits()) - .setRequestId(body.get("requestId").asInt()); - if (body.get("oneway") != null) { - builder.setOneway(body.get("oneway").asBoolean()); - } - if (body.get("requestUUID") != null) { - UUID requestUUID = UUID.fromString(body.get("requestUUID").asText()); - builder.setRequestUuidMSB(requestUUID.getMostSignificantBits()) - .setRequestUuidLSB(requestUUID.getLeastSignificantBits()); - } - if (body.get("expirationTime") != null) { - builder.setExpirationTime(body.get("expirationTime").asLong()); - } - if (body.get("persisted") != null) { - builder.setPersisted(body.get("persisted").asBoolean()); - } - if (body.get("retries") != null) { - builder.setRetries(body.get("retries").asInt()); - } - if (body.get("additionalInfo") != null) { - builder.setAdditionalInfo(JacksonUtil.toString(body.get("additionalInfo"))); - } - if (body.get("serviceId") != null) { - builder.setServiceId(body.get("serviceId").asText()); - } - if (body.get("sessionId") != null) { - builder.setSessionId(body.get("sessionId").asText()); - } - return builder; - } -} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java deleted file mode 100644 index 705bea0fae..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Copyright © 2016-2023 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. - */ -package org.thingsboard.server.service.edge.rpc.constructor; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; -import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.id.RuleChainId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.rule.RuleChain; -import org.thingsboard.server.common.data.rule.RuleChainMetaData; -import org.thingsboard.server.gen.edge.v1.EdgeVersion; -import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; -import org.thingsboard.server.gen.edge.v1.RuleChainUpdateMsg; -import org.thingsboard.server.gen.edge.v1.UpdateMsgType; -import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.edge.rpc.constructor.rule.RuleChainMetadataConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.rule.RuleChainMetadataConstructorFactory; -import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; - -@Component -@Slf4j -@TbCoreComponent -public class RuleChainMsgConstructor { - - public RuleChainUpdateMsg constructRuleChainUpdatedMsg(UpdateMsgType msgType, RuleChain ruleChain, boolean isRoot, EdgeVersion edgeVersion) { - boolean isTemplateRoot = ruleChain.isRoot(); - if (EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)) { - return constructDeprecatedRuleChainUpdatedMsg(msgType, ruleChain, isRoot); - } - ruleChain.setRoot(isRoot); - RuleChainUpdateMsg result = RuleChainUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(ruleChain)) - .setIdMSB(ruleChain.getId().getId().getMostSignificantBits()) - .setIdLSB(ruleChain.getId().getId().getLeastSignificantBits()).build(); - ruleChain.setRoot(isTemplateRoot); - return result; - } - - private RuleChainUpdateMsg constructDeprecatedRuleChainUpdatedMsg(UpdateMsgType msgType, RuleChain ruleChain, boolean isRoot) { - RuleChainUpdateMsg.Builder builder = RuleChainUpdateMsg.newBuilder() - .setMsgType(msgType) - .setIdMSB(ruleChain.getId().getId().getMostSignificantBits()) - .setIdLSB(ruleChain.getId().getId().getLeastSignificantBits()) - .setName(ruleChain.getName()) - .setRoot(isRoot) - .setDebugMode(ruleChain.isDebugMode()) - .setConfiguration(JacksonUtil.toString(ruleChain.getConfiguration())); - if (ruleChain.getFirstRuleNodeId() != null) { - builder.setFirstRuleNodeIdMSB(ruleChain.getFirstRuleNodeId().getId().getMostSignificantBits()) - .setFirstRuleNodeIdLSB(ruleChain.getFirstRuleNodeId().getId().getLeastSignificantBits()); - } - return builder.build(); - } - - public RuleChainMetadataUpdateMsg constructRuleChainMetadataUpdatedMsg(TenantId tenantId, - UpdateMsgType msgType, - RuleChainMetaData ruleChainMetaData, - EdgeVersion edgeVersion) { - RuleChainMetadataConstructor ruleChainMetadataConstructor - = RuleChainMetadataConstructorFactory.getByEdgeVersion(edgeVersion); - return ruleChainMetadataConstructor.constructRuleChainMetadataUpdatedMsg(tenantId, msgType, ruleChainMetaData, edgeVersion); - } - - public RuleChainUpdateMsg constructRuleChainDeleteMsg(RuleChainId ruleChainId) { - return RuleChainUpdateMsg.newBuilder() - .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) - .setIdMSB(ruleChainId.getId().getMostSignificantBits()) - .setIdLSB(ruleChainId.getId().getLeastSignificantBits()).build(); - } -} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/TenantProfileMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/TenantProfileMsgConstructor.java deleted file mode 100644 index 6b47b6859d..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/TenantProfileMsgConstructor.java +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Copyright © 2016-2023 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. - */ -package org.thingsboard.server.service.edge.rpc.constructor; - -import com.google.protobuf.ByteString; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; -import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.TenantProfile; -import org.thingsboard.server.gen.edge.v1.EdgeVersion; -import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg; -import org.thingsboard.server.gen.edge.v1.UpdateMsgType; -import org.thingsboard.server.queue.util.DataDecodingEncodingService; -import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; - -@Component -@TbCoreComponent -public class TenantProfileMsgConstructor { - - @Autowired - private DataDecodingEncodingService dataDecodingEncodingService; - - public TenantProfileUpdateMsg constructTenantProfileUpdateMsg(UpdateMsgType msgType, TenantProfile tenantProfile, EdgeVersion edgeVersion) { - if (EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)) { - ByteString profileData = EdgeVersionUtils.isEdgeVersionOlderThan(edgeVersion, EdgeVersion.V_3_6_1) ? - ByteString.empty() : ByteString.copyFrom(dataDecodingEncodingService.encode(tenantProfile.getProfileData())); - return constructDeprecatedTenantProfileUpdateMsg(msgType, tenantProfile, profileData); - } - return TenantProfileUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(tenantProfile)).build(); - } - - private TenantProfileUpdateMsg constructDeprecatedTenantProfileUpdateMsg(UpdateMsgType msgType, TenantProfile tenantProfile, ByteString profileData) { - TenantProfileUpdateMsg.Builder builder = TenantProfileUpdateMsg.newBuilder() - .setMsgType(msgType) - .setIdMSB(tenantProfile.getId().getId().getMostSignificantBits()) - .setIdLSB(tenantProfile.getId().getId().getLeastSignificantBits()) - .setName(tenantProfile.getName()) - .setDefault(tenantProfile.isDefault()) - .setIsolatedRuleChain(tenantProfile.isIsolatedTbRuleEngine()) - .setProfileDataBytes(profileData); - if (tenantProfile.getDescription() != null) { - builder.setDescription(tenantProfile.getDescription()); - } - return builder.build(); - } -} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetsBundleMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetsBundleMsgConstructor.java deleted file mode 100644 index 5093ddf554..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetsBundleMsgConstructor.java +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright © 2016-2023 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. - */ -package org.thingsboard.server.service.edge.rpc.constructor; - -import com.google.protobuf.ByteString; -import org.springframework.stereotype.Component; -import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.id.WidgetsBundleId; -import org.thingsboard.server.common.data.widget.WidgetsBundle; -import org.thingsboard.server.gen.edge.v1.EdgeVersion; -import org.thingsboard.server.gen.edge.v1.UpdateMsgType; -import org.thingsboard.server.gen.edge.v1.WidgetsBundleUpdateMsg; -import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; - -import java.nio.charset.StandardCharsets; -import java.util.List; - -@Component -@TbCoreComponent -public class WidgetsBundleMsgConstructor { - - public WidgetsBundleUpdateMsg constructWidgetsBundleUpdateMsg(UpdateMsgType msgType, WidgetsBundle widgetsBundle, List widgets, EdgeVersion edgeVersion) { - if (EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)) { - return constructDeprecatedWidgetsBundleUpdateMsg(msgType, widgetsBundle, widgets); - } - return WidgetsBundleUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(widgetsBundle)) - .setWidgets(JacksonUtil.toString(widgets)) - .setIdMSB(widgetsBundle.getId().getId().getMostSignificantBits()) - .setIdLSB(widgetsBundle.getId().getId().getLeastSignificantBits()).build(); - } - - private WidgetsBundleUpdateMsg constructDeprecatedWidgetsBundleUpdateMsg(UpdateMsgType msgType, WidgetsBundle widgetsBundle, List widgets) { - WidgetsBundleUpdateMsg.Builder builder = WidgetsBundleUpdateMsg.newBuilder() - .setMsgType(msgType) - .setIdMSB(widgetsBundle.getId().getId().getMostSignificantBits()) - .setIdLSB(widgetsBundle.getId().getId().getLeastSignificantBits()) - .setTitle(widgetsBundle.getTitle()) - .setAlias(widgetsBundle.getAlias()); - if (widgetsBundle.getImage() != null) { - builder.setImage(ByteString.copyFrom(widgetsBundle.getImage().getBytes(StandardCharsets.UTF_8))); - } - if (widgetsBundle.getDescription() != null) { - builder.setDescription(widgetsBundle.getDescription()); - } - if (widgetsBundle.getOrder() != null) { - builder.setOrder(widgetsBundle.getOrder()); - } - if (widgetsBundle.getTenantId().equals(TenantId.SYS_TENANT_ID)) { - builder.setIsSystem(true); - } - builder.setWidgets(JacksonUtil.toString(widgets)); - return builder.build(); - } - - public WidgetsBundleUpdateMsg constructWidgetsBundleDeleteMsg(WidgetsBundleId widgetsBundleId) { - return WidgetsBundleUpdateMsg.newBuilder() - .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) - .setIdMSB(widgetsBundleId.getId().getMostSignificantBits()) - .setIdLSB(widgetsBundleId.getId().getLeastSignificantBits()) - .build(); - } -} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/alarm/AlarmMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/alarm/AlarmMsgConstructor.java new file mode 100644 index 0000000000..7251c067d7 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/alarm/AlarmMsgConstructor.java @@ -0,0 +1,25 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.alarm; + +import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; + +public interface AlarmMsgConstructor { + + AlarmUpdateMsg constructAlarmUpdatedMsg(UpdateMsgType msgType, Alarm alarm, String entityName); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/alarm/AlarmMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/alarm/AlarmMsgConstructorFactory.java new file mode 100644 index 0000000000..41a13e4447 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/alarm/AlarmMsgConstructorFactory.java @@ -0,0 +1,45 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.alarm; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class AlarmMsgConstructorFactory { + + @Autowired + protected AlarmMsgConstructorV1 alarmMsgConstructorV1; + + @Autowired + protected AlarmMsgConstructorV2 alarmMsgConstructorV2; + + public AlarmMsgConstructor getAlarmMsgConstructor(EdgeVersion edgeVersion) { + switch (edgeVersion) { + case V_3_3_0: + case V_3_3_3: + case V_3_4_0: + case V_3_6_0: + case V_3_6_1: + return alarmMsgConstructorV1; + default: + return alarmMsgConstructorV2; + } + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AlarmMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/alarm/AlarmMsgConstructorV1.java similarity index 67% rename from application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AlarmMsgConstructor.java rename to application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/alarm/AlarmMsgConstructorV1.java index ef2a353e8e..b1294f5904 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AlarmMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/alarm/AlarmMsgConstructorV1.java @@ -13,33 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.edge.rpc.constructor; +package org.thingsboard.server.service.edge.rpc.constructor.alarm; import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg; -import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; @Component @TbCoreComponent -public class AlarmMsgConstructor { +public class AlarmMsgConstructorV1 implements AlarmMsgConstructor { - public AlarmUpdateMsg constructAlarmUpdatedMsg(UpdateMsgType msgType, Alarm alarm, String entityName, EdgeVersion edgeVersion) { - if (EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)) { - return constructDeprecatedAlarmUpdatedMsg(msgType, alarm, entityName); - } - return AlarmUpdateMsg.newBuilder().setMsgType(msgType) - .setEntity(JacksonUtil.toString(alarm)) - .setIdMSB(alarm.getId().getId().getMostSignificantBits()) - .setIdLSB(alarm.getId().getId().getLeastSignificantBits()).build(); - } - - private AlarmUpdateMsg constructDeprecatedAlarmUpdatedMsg(UpdateMsgType msgType, Alarm alarm, String entityName) { - AlarmUpdateMsg.Builder builder = AlarmUpdateMsg.newBuilder() + @Override + public AlarmUpdateMsg constructAlarmUpdatedMsg(UpdateMsgType msgType, Alarm alarm, String entityName) { + return AlarmUpdateMsg.newBuilder() .setMsgType(msgType) .setIdMSB(alarm.getId().getId().getMostSignificantBits()) .setIdLSB(alarm.getId().getId().getLeastSignificantBits()) @@ -56,7 +45,6 @@ public class AlarmMsgConstructor { .setDetails(JacksonUtil.toString(alarm.getDetails())) .setPropagate(alarm.isPropagate()) .setPropagateToOwner(alarm.isPropagateToOwner()) - .setPropagateToTenant(alarm.isPropagateToTenant()); - return builder.build(); + .setPropagateToTenant(alarm.isPropagateToTenant()).build(); } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/alarm/AlarmMsgConstructorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/alarm/AlarmMsgConstructorV2.java new file mode 100644 index 0000000000..9034755db6 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/alarm/AlarmMsgConstructorV2.java @@ -0,0 +1,36 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.alarm; + +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class AlarmMsgConstructorV2 implements AlarmMsgConstructor { + + @Override + public AlarmUpdateMsg constructAlarmUpdatedMsg(UpdateMsgType msgType, Alarm alarm, String entityName) { + return AlarmUpdateMsg.newBuilder().setMsgType(msgType) + .setEntity(JacksonUtil.toString(alarm)) + .setIdMSB(alarm.getId().getId().getMostSignificantBits()) + .setIdLSB(alarm.getId().getId().getLeastSignificantBits()).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/AssetMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/AssetMsgConstructor.java new file mode 100644 index 0000000000..0223351f54 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/AssetMsgConstructor.java @@ -0,0 +1,35 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.asset; + +import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.asset.AssetProfile; +import org.thingsboard.server.common.data.id.AssetId; +import org.thingsboard.server.common.data.id.AssetProfileId; +import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg; +import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; + +public interface AssetMsgConstructor { + + AssetUpdateMsg constructAssetUpdatedMsg(UpdateMsgType msgType, Asset asset); + + AssetUpdateMsg constructAssetDeleteMsg(AssetId assetId); + + AssetProfileUpdateMsg constructAssetProfileUpdatedMsg(UpdateMsgType msgType, AssetProfile assetProfile); + + AssetProfileUpdateMsg constructAssetProfileDeleteMsg(AssetProfileId assetProfileId); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/AssetMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/AssetMsgConstructorFactory.java new file mode 100644 index 0000000000..d4fa79c6c5 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/AssetMsgConstructorFactory.java @@ -0,0 +1,45 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.asset; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class AssetMsgConstructorFactory { + + @Autowired + protected AssetMsgConstructorV1 assetMsgConstructorV1; + + @Autowired + protected AssetMsgConstructorV2 assetMsgConstructorV2; + + public AssetMsgConstructor getMsgConstructorByEdgeVersion(EdgeVersion edgeVersion) { + switch (edgeVersion) { + case V_3_3_0: + case V_3_3_3: + case V_3_4_0: + case V_3_6_0: + case V_3_6_1: + return assetMsgConstructorV1; + default: + return assetMsgConstructorV2; + } + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetProfileMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/AssetMsgConstructorV1.java similarity index 63% rename from application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetProfileMsgConstructor.java rename to application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/AssetMsgConstructorV1.java index 0c6247754c..95e1e0e597 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetProfileMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/AssetMsgConstructorV1.java @@ -13,35 +13,51 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.edge.rpc.constructor; +package org.thingsboard.server.service.edge.rpc.constructor.asset; import com.google.protobuf.ByteString; import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.asset.AssetProfile; -import org.thingsboard.server.common.data.id.AssetProfileId; import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg; -import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; import java.nio.charset.StandardCharsets; @Component @TbCoreComponent -public class AssetProfileMsgConstructor { +public class AssetMsgConstructorV1 extends BaseAssetMsgConstructor { - public AssetProfileUpdateMsg constructAssetProfileUpdatedMsg(UpdateMsgType msgType, AssetProfile assetProfile, EdgeVersion edgeVersion) { - if (EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)) { - return constructDeprecatedAssetProfileUpdatedMsg(msgType, assetProfile); + @Override + public AssetUpdateMsg constructAssetUpdatedMsg(UpdateMsgType msgType, Asset asset) { + AssetUpdateMsg.Builder builder = AssetUpdateMsg.newBuilder() + .setMsgType(msgType) + .setIdMSB(asset.getUuidId().getMostSignificantBits()) + .setIdLSB(asset.getUuidId().getLeastSignificantBits()) + .setName(asset.getName()) + .setType(asset.getType()); + if (asset.getLabel() != null) { + builder.setLabel(asset.getLabel()); } - return AssetProfileUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(assetProfile)) - .setIdMSB(assetProfile.getId().getId().getMostSignificantBits()) - .setIdLSB(assetProfile.getId().getId().getLeastSignificantBits()).build(); + if (asset.getCustomerId() != null) { + builder.setCustomerIdMSB(asset.getCustomerId().getId().getMostSignificantBits()); + builder.setCustomerIdLSB(asset.getCustomerId().getId().getLeastSignificantBits()); + } + if (asset.getAssetProfileId() != null) { + builder.setAssetProfileIdMSB(asset.getAssetProfileId().getId().getMostSignificantBits()); + builder.setAssetProfileIdLSB(asset.getAssetProfileId().getId().getLeastSignificantBits()); + } + if (asset.getAdditionalInfo() != null) { + builder.setAdditionalInfo(JacksonUtil.toString(asset.getAdditionalInfo())); + } + return builder.build(); } - private AssetProfileUpdateMsg constructDeprecatedAssetProfileUpdatedMsg(UpdateMsgType msgType, AssetProfile assetProfile) { + @Override + public AssetProfileUpdateMsg constructAssetProfileUpdatedMsg(UpdateMsgType msgType, AssetProfile assetProfile) { AssetProfileUpdateMsg.Builder builder = AssetProfileUpdateMsg.newBuilder() .setMsgType(msgType) .setIdMSB(assetProfile.getId().getId().getMostSignificantBits()) @@ -67,11 +83,4 @@ public class AssetProfileMsgConstructor { } return builder.build(); } - - public AssetProfileUpdateMsg constructAssetProfileDeleteMsg(AssetProfileId assetProfileId) { - return AssetProfileUpdateMsg.newBuilder() - .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) - .setIdMSB(assetProfileId.getId().getMostSignificantBits()) - .setIdLSB(assetProfileId.getId().getLeastSignificantBits()).build(); - } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/AssetMsgConstructorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/AssetMsgConstructorV2.java new file mode 100644 index 0000000000..b7e00473b5 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/AssetMsgConstructorV2.java @@ -0,0 +1,44 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.asset; + +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.asset.AssetProfile; +import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg; +import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class AssetMsgConstructorV2 extends BaseAssetMsgConstructor { + + @Override + public AssetUpdateMsg constructAssetUpdatedMsg(UpdateMsgType msgType, Asset asset) { + return AssetUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(asset)) + .setIdMSB(asset.getUuidId().getMostSignificantBits()) + .setIdLSB(asset.getUuidId().getLeastSignificantBits()).build(); + } + + @Override + public AssetProfileUpdateMsg constructAssetProfileUpdatedMsg(UpdateMsgType msgType, AssetProfile assetProfile) { + return AssetProfileUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(assetProfile)) + .setIdMSB(assetProfile.getId().getId().getMostSignificantBits()) + .setIdLSB(assetProfile.getId().getId().getLeastSignificantBits()).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/BaseAssetMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/BaseAssetMsgConstructor.java new file mode 100644 index 0000000000..b8fe8ad870 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/BaseAssetMsgConstructor.java @@ -0,0 +1,41 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.asset; + +import org.thingsboard.server.common.data.id.AssetId; +import org.thingsboard.server.common.data.id.AssetProfileId; +import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg; +import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; + +public abstract class BaseAssetMsgConstructor implements AssetMsgConstructor { + + @Override + public AssetUpdateMsg constructAssetDeleteMsg(AssetId assetId) { + return AssetUpdateMsg.newBuilder() + .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) + .setIdMSB(assetId.getId().getMostSignificantBits()) + .setIdLSB(assetId.getId().getLeastSignificantBits()).build(); + } + + @Override + public AssetProfileUpdateMsg constructAssetProfileDeleteMsg(AssetProfileId assetProfileId) { + return AssetProfileUpdateMsg.newBuilder() + .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) + .setIdMSB(assetProfileId.getId().getMostSignificantBits()) + .setIdLSB(assetProfileId.getId().getLeastSignificantBits()).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/BaseCustomerMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/BaseCustomerMsgConstructor.java new file mode 100644 index 0000000000..6d532a5e1c --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/BaseCustomerMsgConstructor.java @@ -0,0 +1,31 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.customer; + +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.gen.edge.v1.CustomerUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; + +public abstract class BaseCustomerMsgConstructor implements CustomerMsgConstructor { + + @Override + public CustomerUpdateMsg constructCustomerDeleteMsg(CustomerId customerId) { + return CustomerUpdateMsg.newBuilder() + .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) + .setIdMSB(customerId.getId().getMostSignificantBits()) + .setIdLSB(customerId.getId().getLeastSignificantBits()).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/CustomerMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/CustomerMsgConstructor.java new file mode 100644 index 0000000000..fd9139de12 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/CustomerMsgConstructor.java @@ -0,0 +1,28 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.customer; + +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.gen.edge.v1.CustomerUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; + +public interface CustomerMsgConstructor { + + CustomerUpdateMsg constructCustomerUpdatedMsg(UpdateMsgType msgType, Customer customer); + + CustomerUpdateMsg constructCustomerDeleteMsg(CustomerId customerId); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/CustomerMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/CustomerMsgConstructorFactory.java new file mode 100644 index 0000000000..350a4b6f9d --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/CustomerMsgConstructorFactory.java @@ -0,0 +1,45 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.customer; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class CustomerMsgConstructorFactory { + + @Autowired + protected CustomerMsgConstructorV1 customerMsgConstructorV1; + + @Autowired + protected CustomerMsgConstructorV2 customerMsgConstructorV2; + + public CustomerMsgConstructor getCustomerMsgConstructor(EdgeVersion edgeVersion) { + switch (edgeVersion) { + case V_3_3_0: + case V_3_3_3: + case V_3_4_0: + case V_3_6_0: + case V_3_6_1: + return customerMsgConstructorV1; + default: + return customerMsgConstructorV2; + } + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/CustomerMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/CustomerMsgConstructorV1.java similarity index 65% rename from application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/CustomerMsgConstructor.java rename to application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/CustomerMsgConstructorV1.java index cbfb9004eb..e6bb95defa 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/CustomerMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/CustomerMsgConstructorV1.java @@ -13,32 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.edge.rpc.constructor; +package org.thingsboard.server.service.edge.rpc.constructor.customer; import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Customer; -import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.gen.edge.v1.CustomerUpdateMsg; -import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; @Component @TbCoreComponent -public class CustomerMsgConstructor { +public class CustomerMsgConstructorV1 extends BaseCustomerMsgConstructor { - public CustomerUpdateMsg constructCustomerUpdatedMsg(UpdateMsgType msgType, Customer customer, EdgeVersion edgeVersion) { - if (EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)) { - return constructDeprecatedCustomerUpdatedMsg(msgType, customer); - } - return CustomerUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(customer)) - .setIdMSB(customer.getId().getId().getMostSignificantBits()) - .setIdLSB(customer.getId().getId().getLeastSignificantBits()).build(); - } - - private CustomerUpdateMsg constructDeprecatedCustomerUpdatedMsg(UpdateMsgType msgType, Customer customer) { + @Override + public CustomerUpdateMsg constructCustomerUpdatedMsg(UpdateMsgType msgType, Customer customer) { CustomerUpdateMsg.Builder builder = CustomerUpdateMsg.newBuilder() .setMsgType(msgType) .setIdMSB(customer.getId().getId().getMostSignificantBits()) @@ -73,11 +62,4 @@ public class CustomerMsgConstructor { } return builder.build(); } - - public CustomerUpdateMsg constructCustomerDeleteMsg(CustomerId customerId) { - return CustomerUpdateMsg.newBuilder() - .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) - .setIdMSB(customerId.getId().getMostSignificantBits()) - .setIdLSB(customerId.getId().getLeastSignificantBits()).build(); - } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/CustomerMsgConstructorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/CustomerMsgConstructorV2.java new file mode 100644 index 0000000000..94764e1178 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/CustomerMsgConstructorV2.java @@ -0,0 +1,35 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.customer; + +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.gen.edge.v1.CustomerUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class CustomerMsgConstructorV2 extends BaseCustomerMsgConstructor { + + @Override + public CustomerUpdateMsg constructCustomerUpdatedMsg(UpdateMsgType msgType, Customer customer) { + return CustomerUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(customer)) + .setIdMSB(customer.getId().getId().getMostSignificantBits()) + .setIdLSB(customer.getId().getId().getLeastSignificantBits()).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/BaseDashboardMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/BaseDashboardMsgConstructor.java new file mode 100644 index 0000000000..f8e42c1461 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/BaseDashboardMsgConstructor.java @@ -0,0 +1,31 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.dashboard; + +import org.thingsboard.server.common.data.id.DashboardId; +import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; + +public abstract class BaseDashboardMsgConstructor implements DashboardMsgConstructor { + + @Override + public DashboardUpdateMsg constructDashboardDeleteMsg(DashboardId dashboardId) { + return DashboardUpdateMsg.newBuilder() + .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) + .setIdMSB(dashboardId.getId().getMostSignificantBits()) + .setIdLSB(dashboardId.getId().getLeastSignificantBits()).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/DashboardMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/DashboardMsgConstructor.java new file mode 100644 index 0000000000..e2ca09b1d3 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/DashboardMsgConstructor.java @@ -0,0 +1,28 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.dashboard; + +import org.thingsboard.server.common.data.Dashboard; +import org.thingsboard.server.common.data.id.DashboardId; +import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; + +public interface DashboardMsgConstructor { + + DashboardUpdateMsg constructDashboardUpdatedMsg(UpdateMsgType msgType, Dashboard dashboard); + + DashboardUpdateMsg constructDashboardDeleteMsg(DashboardId dashboardId); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/DashboardMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/DashboardMsgConstructorFactory.java new file mode 100644 index 0000000000..5e566297fd --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/DashboardMsgConstructorFactory.java @@ -0,0 +1,45 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.dashboard; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class DashboardMsgConstructorFactory { + + @Autowired + protected DashboardMsgConstructorV1 dashboardMsgConstructorV1; + + @Autowired + protected DashboardMsgConstructorV2 dashboardMsgConstructorV2; + + public DashboardMsgConstructor getMsgConstructorByEdgeVersion(EdgeVersion edgeVersion) { + switch (edgeVersion) { + case V_3_3_0: + case V_3_3_3: + case V_3_4_0: + case V_3_6_0: + case V_3_6_1: + return dashboardMsgConstructorV1; + default: + return dashboardMsgConstructorV2; + } + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DashboardMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/DashboardMsgConstructorV1.java similarity index 60% rename from application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DashboardMsgConstructor.java rename to application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/DashboardMsgConstructorV1.java index 82f83a32ff..6a3bb43797 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DashboardMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/DashboardMsgConstructorV1.java @@ -13,32 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.edge.rpc.constructor; +package org.thingsboard.server.service.edge.rpc.constructor.dashboard; import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Dashboard; -import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg; -import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; @Component @TbCoreComponent -public class DashboardMsgConstructor { +public class DashboardMsgConstructorV1 extends BaseDashboardMsgConstructor { - public DashboardUpdateMsg constructDashboardUpdatedMsg(UpdateMsgType msgType, Dashboard dashboard, EdgeVersion edgeVersion) { - if (EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)) { - return constructDeprecatedDashboardUpdatedMsg(msgType, dashboard); - } - return DashboardUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(dashboard)) - .setIdMSB(dashboard.getId().getId().getMostSignificantBits()) - .setIdLSB(dashboard.getId().getId().getLeastSignificantBits()).build(); - } - - private DashboardUpdateMsg constructDeprecatedDashboardUpdatedMsg(UpdateMsgType msgType, Dashboard dashboard) { + @Override + public DashboardUpdateMsg constructDashboardUpdatedMsg(UpdateMsgType msgType, Dashboard dashboard) { DashboardUpdateMsg.Builder builder = DashboardUpdateMsg.newBuilder() .setMsgType(msgType) .setIdMSB(dashboard.getId().getId().getMostSignificantBits()) @@ -57,11 +46,4 @@ public class DashboardMsgConstructor { } return builder.build(); } - - public DashboardUpdateMsg constructDashboardDeleteMsg(DashboardId dashboardId) { - return DashboardUpdateMsg.newBuilder() - .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) - .setIdMSB(dashboardId.getId().getMostSignificantBits()) - .setIdLSB(dashboardId.getId().getLeastSignificantBits()).build(); - } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/DashboardMsgConstructorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/DashboardMsgConstructorV2.java new file mode 100644 index 0000000000..0940423119 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/DashboardMsgConstructorV2.java @@ -0,0 +1,35 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.dashboard; + +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.Dashboard; +import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class DashboardMsgConstructorV2 extends BaseDashboardMsgConstructor { + + @Override + public DashboardUpdateMsg constructDashboardUpdatedMsg(UpdateMsgType msgType, Dashboard dashboard) { + return DashboardUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(dashboard)) + .setIdMSB(dashboard.getId().getId().getMostSignificantBits()) + .setIdLSB(dashboard.getId().getId().getLeastSignificantBits()).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/BaseDeviceMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/BaseDeviceMsgConstructor.java new file mode 100644 index 0000000000..21d761dc26 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/BaseDeviceMsgConstructor.java @@ -0,0 +1,102 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.device; + +import com.fasterxml.jackson.databind.JsonNode; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.DeviceProfileId; +import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg; +import org.thingsboard.server.gen.edge.v1.DeviceRpcCallMsg; +import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg; +import org.thingsboard.server.gen.edge.v1.RpcRequestMsg; +import org.thingsboard.server.gen.edge.v1.RpcResponseMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; + +import java.util.UUID; + +public abstract class BaseDeviceMsgConstructor implements DeviceMsgConstructor { + + @Override + public DeviceUpdateMsg constructDeviceDeleteMsg(DeviceId deviceId) { + return DeviceUpdateMsg.newBuilder() + .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) + .setIdMSB(deviceId.getId().getMostSignificantBits()) + .setIdLSB(deviceId.getId().getLeastSignificantBits()).build(); + } + + @Override + public DeviceProfileUpdateMsg constructDeviceProfileDeleteMsg(DeviceProfileId deviceProfileId) { + return DeviceProfileUpdateMsg.newBuilder() + .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) + .setIdMSB(deviceProfileId.getId().getMostSignificantBits()) + .setIdLSB(deviceProfileId.getId().getLeastSignificantBits()).build(); + } + + @Override + public DeviceRpcCallMsg constructDeviceRpcCallMsg(UUID deviceId, JsonNode body) { + DeviceRpcCallMsg.Builder builder = constructDeviceRpcMsg(deviceId, body); + if (body.has("error") || body.has("response")) { + RpcResponseMsg.Builder responseBuilder = RpcResponseMsg.newBuilder(); + if (body.has("error")) { + responseBuilder.setError(body.get("error").asText()); + } else { + responseBuilder.setResponse(body.get("response").asText()); + } + builder.setResponseMsg(responseBuilder.build()); + } else { + RpcRequestMsg.Builder requestBuilder = RpcRequestMsg.newBuilder(); + requestBuilder.setMethod(body.get("method").asText()); + requestBuilder.setParams(body.get("params").asText()); + builder.setRequestMsg(requestBuilder.build()); + } + return builder.build(); + } + + private DeviceRpcCallMsg.Builder constructDeviceRpcMsg(UUID deviceId, JsonNode body) { + DeviceRpcCallMsg.Builder builder = DeviceRpcCallMsg.newBuilder() + .setDeviceIdMSB(deviceId.getMostSignificantBits()) + .setDeviceIdLSB(deviceId.getLeastSignificantBits()) + .setRequestId(body.get("requestId").asInt()); + if (body.get("oneway") != null) { + builder.setOneway(body.get("oneway").asBoolean()); + } + if (body.get("requestUUID") != null) { + UUID requestUUID = UUID.fromString(body.get("requestUUID").asText()); + builder.setRequestUuidMSB(requestUUID.getMostSignificantBits()) + .setRequestUuidLSB(requestUUID.getLeastSignificantBits()); + } + if (body.get("expirationTime") != null) { + builder.setExpirationTime(body.get("expirationTime").asLong()); + } + if (body.get("persisted") != null) { + builder.setPersisted(body.get("persisted").asBoolean()); + } + if (body.get("retries") != null) { + builder.setRetries(body.get("retries").asInt()); + } + if (body.get("additionalInfo") != null) { + builder.setAdditionalInfo(JacksonUtil.toString(body.get("additionalInfo"))); + } + if (body.get("serviceId") != null) { + builder.setServiceId(body.get("serviceId").asText()); + } + if (body.get("sessionId") != null) { + builder.setSessionId(body.get("sessionId").asText()); + } + return builder; + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructor.java new file mode 100644 index 0000000000..514aeceb68 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructor.java @@ -0,0 +1,38 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.device; + +import com.fasterxml.jackson.databind.JsonNode; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.security.DeviceCredentials; +import org.thingsboard.server.gen.edge.v1.DeviceCredentialsUpdateMsg; +import org.thingsboard.server.gen.edge.v1.DeviceRpcCallMsg; +import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; + +import java.util.UUID; + +public interface DeviceMsgConstructor { + + DeviceUpdateMsg constructDeviceUpdatedMsg(UpdateMsgType msgType, Device device); + + DeviceUpdateMsg constructDeviceDeleteMsg(DeviceId deviceId); + + DeviceCredentialsUpdateMsg constructDeviceCredentialsUpdatedMsg(DeviceCredentials deviceCredentials); + + DeviceRpcCallMsg constructDeviceRpcCallMsg(UUID deviceId, JsonNode body); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructorFactory.java new file mode 100644 index 0000000000..2f14c401ea --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructorFactory.java @@ -0,0 +1,45 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.device; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class DeviceMsgConstructorFactory { + + @Autowired + protected DeviceMsgConstructorV1 deviceMsgConstructorV1; + + @Autowired + protected DeviceMsgConstructorV2 deviceMsgConstructorV2; + + public DeviceMsgConstructor getDeviceMsgConstructor(EdgeVersion edgeVersion) { + switch (edgeVersion) { + case V_3_3_0: + case V_3_3_3: + case V_3_4_0: + case V_3_6_0: + case V_3_6_1: + return deviceMsgConstructorV1; + default: + return deviceMsgConstructorV2; + } + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceProfileMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructorV1.java similarity index 55% rename from application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceProfileMsgConstructor.java rename to application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructorV1.java index 6d3e9da378..4442387b21 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceProfileMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructorV1.java @@ -13,40 +13,84 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.edge.rpc.constructor; +package org.thingsboard.server.service.edge.rpc.constructor.device; import com.google.protobuf.ByteString; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; -import org.thingsboard.server.common.data.id.DeviceProfileId; +import org.thingsboard.server.common.data.security.DeviceCredentials; +import org.thingsboard.server.gen.edge.v1.DeviceCredentialsUpdateMsg; import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg; -import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.DataDecodingEncodingService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; import java.nio.charset.StandardCharsets; @Component @TbCoreComponent -public class DeviceProfileMsgConstructor { +public class DeviceMsgConstructorV1 extends BaseDeviceMsgConstructor { @Autowired private DataDecodingEncodingService dataDecodingEncodingService; - public DeviceProfileUpdateMsg constructDeviceProfileUpdatedMsg(UpdateMsgType msgType, DeviceProfile deviceProfile, EdgeVersion edgeVersion) { - if (EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)) { - return constructDeprecatedDeviceProfileUpdatedMsg(msgType, deviceProfile); + @Override + public DeviceUpdateMsg constructDeviceUpdatedMsg(UpdateMsgType msgType, Device device) { + DeviceUpdateMsg.Builder builder = DeviceUpdateMsg.newBuilder() + .setMsgType(msgType) + .setIdMSB(device.getId().getId().getMostSignificantBits()) + .setIdLSB(device.getId().getId().getLeastSignificantBits()) + .setName(device.getName()) + .setType(device.getType()); + if (device.getLabel() != null) { + builder.setLabel(device.getLabel()); } - return DeviceProfileUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(deviceProfile)) - .setIdMSB(deviceProfile.getId().getId().getMostSignificantBits()) - .setIdLSB(deviceProfile.getId().getId().getLeastSignificantBits()).build(); + if (device.getCustomerId() != null) { + builder.setCustomerIdMSB(device.getCustomerId().getId().getMostSignificantBits()); + builder.setCustomerIdLSB(device.getCustomerId().getId().getLeastSignificantBits()); + } + if (device.getDeviceProfileId() != null) { + builder.setDeviceProfileIdMSB(device.getDeviceProfileId().getId().getMostSignificantBits()); + builder.setDeviceProfileIdLSB(device.getDeviceProfileId().getId().getLeastSignificantBits()); + } + if (device.getAdditionalInfo() != null) { + builder.setAdditionalInfo(JacksonUtil.toString(device.getAdditionalInfo())); + } + if (device.getFirmwareId() != null) { + builder.setFirmwareIdMSB(device.getFirmwareId().getId().getMostSignificantBits()) + .setFirmwareIdLSB(device.getFirmwareId().getId().getLeastSignificantBits()); + } + if (device.getSoftwareId() != null) { + builder.setSoftwareIdMSB(device.getSoftwareId().getId().getMostSignificantBits()) + .setSoftwareIdLSB(device.getSoftwareId().getId().getLeastSignificantBits()); + } + if (device.getDeviceData() != null) { + builder.setDeviceDataBytes(ByteString.copyFrom(dataDecodingEncodingService.encode(device.getDeviceData()))); + } + return builder.build(); + } + + @Override + public DeviceCredentialsUpdateMsg constructDeviceCredentialsUpdatedMsg(DeviceCredentials deviceCredentials) { + DeviceCredentialsUpdateMsg.Builder builder = DeviceCredentialsUpdateMsg.newBuilder() + .setDeviceIdMSB(deviceCredentials.getDeviceId().getId().getMostSignificantBits()) + .setDeviceIdLSB(deviceCredentials.getDeviceId().getId().getLeastSignificantBits()); + if (deviceCredentials.getCredentialsType() != null) { + builder.setCredentialsType(deviceCredentials.getCredentialsType().name()) + .setCredentialsId(deviceCredentials.getCredentialsId()); + } + if (deviceCredentials.getCredentialsValue() != null) { + builder.setCredentialsValue(deviceCredentials.getCredentialsValue()); + } + return builder.build(); } - private DeviceProfileUpdateMsg constructDeprecatedDeviceProfileUpdatedMsg(UpdateMsgType msgType, DeviceProfile deviceProfile) { + @Override + public DeviceProfileUpdateMsg constructDeviceProfileUpdatedMsg(UpdateMsgType msgType, DeviceProfile deviceProfile) { DeviceProfileUpdateMsg.Builder builder = DeviceProfileUpdateMsg.newBuilder() .setMsgType(msgType) .setIdMSB(deviceProfile.getId().getId().getMostSignificantBits()) @@ -91,11 +135,4 @@ public class DeviceProfileMsgConstructor { } return builder.build(); } - - public DeviceProfileUpdateMsg constructDeviceProfileDeleteMsg(DeviceProfileId deviceProfileId) { - return DeviceProfileUpdateMsg.newBuilder() - .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) - .setIdMSB(deviceProfileId.getId().getMostSignificantBits()) - .setIdLSB(deviceProfileId.getId().getLeastSignificantBits()).build(); - } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructorV2.java new file mode 100644 index 0000000000..189251f8c4 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructorV2.java @@ -0,0 +1,51 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.device; + +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.security.DeviceCredentials; +import org.thingsboard.server.gen.edge.v1.DeviceCredentialsUpdateMsg; +import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg; +import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class DeviceMsgConstructorV2 extends BaseDeviceMsgConstructor { + + @Override + public DeviceUpdateMsg constructDeviceUpdatedMsg(UpdateMsgType msgType, Device device) { + return DeviceUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(device)) + .setIdMSB(device.getId().getId().getMostSignificantBits()) + .setIdLSB(device.getId().getId().getLeastSignificantBits()).build(); + } + + @Override + public DeviceCredentialsUpdateMsg constructDeviceCredentialsUpdatedMsg(DeviceCredentials deviceCredentials) { + return DeviceCredentialsUpdateMsg.newBuilder().setEntity(JacksonUtil.toString(deviceCredentials)).build(); + } + + @Override + public DeviceProfileUpdateMsg constructDeviceProfileUpdatedMsg(UpdateMsgType msgType, DeviceProfile deviceProfile) { + return DeviceProfileUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(deviceProfile)) + .setIdMSB(deviceProfile.getId().getId().getMostSignificantBits()) + .setIdLSB(deviceProfile.getId().getId().getLeastSignificantBits()).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EdgeMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/edge/EdgeMsgConstructor.java similarity index 96% rename from application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EdgeMsgConstructor.java rename to application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/edge/EdgeMsgConstructor.java index 3416240b2c..c3741ee8bf 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EdgeMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/edge/EdgeMsgConstructor.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.edge.rpc.constructor; +package org.thingsboard.server.service.edge.rpc.constructor.edge; import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/BaseEntityViewMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/BaseEntityViewMsgConstructor.java new file mode 100644 index 0000000000..a037e3ee09 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/BaseEntityViewMsgConstructor.java @@ -0,0 +1,31 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.entityview; + +import org.thingsboard.server.common.data.id.EntityViewId; +import org.thingsboard.server.gen.edge.v1.EntityViewUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; + +public abstract class BaseEntityViewMsgConstructor implements EntityViewMsgConstructor { + + @Override + public EntityViewUpdateMsg constructEntityViewDeleteMsg(EntityViewId entityViewId) { + return EntityViewUpdateMsg.newBuilder() + .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) + .setIdMSB(entityViewId.getId().getMostSignificantBits()) + .setIdLSB(entityViewId.getId().getLeastSignificantBits()).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/EntityViewMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/EntityViewMsgConstructor.java new file mode 100644 index 0000000000..60a404669c --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/EntityViewMsgConstructor.java @@ -0,0 +1,28 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.entityview; + +import org.thingsboard.server.common.data.EntityView; +import org.thingsboard.server.common.data.id.EntityViewId; +import org.thingsboard.server.gen.edge.v1.EntityViewUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; + +public interface EntityViewMsgConstructor { + + EntityViewUpdateMsg constructEntityViewUpdatedMsg(UpdateMsgType msgType, EntityView entityView); + + EntityViewUpdateMsg constructEntityViewDeleteMsg(EntityViewId entityViewId); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/EntityViewMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/EntityViewMsgConstructorFactory.java new file mode 100644 index 0000000000..b2c7f3b0c3 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/EntityViewMsgConstructorFactory.java @@ -0,0 +1,45 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.entityview; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class EntityViewMsgConstructorFactory { + + @Autowired + protected EntityViewMsgConstructorV1 entityViewMsgConstructorV1; + + @Autowired + protected EntityViewMsgConstructorV2 entityViewMsgConstructorV2; + + public EntityViewMsgConstructor getEntityViewMsgConstructor(EdgeVersion edgeVersion) { + switch (edgeVersion) { + case V_3_3_0: + case V_3_3_3: + case V_3_4_0: + case V_3_6_0: + case V_3_6_1: + return entityViewMsgConstructorV1; + default: + return entityViewMsgConstructorV2; + } + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityViewMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/EntityViewMsgConstructorV1.java similarity index 67% rename from application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityViewMsgConstructor.java rename to application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/EntityViewMsgConstructorV1.java index bfa69459cc..da369ecf90 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityViewMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/EntityViewMsgConstructorV1.java @@ -13,34 +13,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.edge.rpc.constructor; +package org.thingsboard.server.service.edge.rpc.constructor.entityview; import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityView; -import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.gen.edge.v1.EdgeEntityType; -import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.EntityViewUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; @Component @TbCoreComponent -public class EntityViewMsgConstructor { +public class EntityViewMsgConstructorV1 extends BaseEntityViewMsgConstructor { - public EntityViewUpdateMsg constructEntityViewUpdatedMsg(UpdateMsgType msgType, EntityView entityView, EdgeVersion edgeVersion) { - if (EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)) { - return constructDeprecatedEntityViewUpdatedMsg(msgType, entityView); - } - return EntityViewUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(entityView)) - .setIdMSB(entityView.getId().getId().getMostSignificantBits()) - .setIdLSB(entityView.getId().getId().getLeastSignificantBits()).build(); - } - - private EntityViewUpdateMsg constructDeprecatedEntityViewUpdatedMsg(UpdateMsgType msgType, EntityView entityView) { + @Override + public EntityViewUpdateMsg constructEntityViewUpdatedMsg(UpdateMsgType msgType, EntityView entityView) { EdgeEntityType edgeEntityType = checkEntityType(entityView.getEntityId().getEntityType()); EntityViewUpdateMsg.Builder builder = EntityViewUpdateMsg.newBuilder() .setMsgType(msgType) @@ -71,11 +60,4 @@ public class EntityViewMsgConstructor { throw new RuntimeException("Unsupported entity type [" + entityType + "]"); } } - - public EntityViewUpdateMsg constructEntityViewDeleteMsg(EntityViewId entityViewId) { - return EntityViewUpdateMsg.newBuilder() - .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) - .setIdMSB(entityViewId.getId().getMostSignificantBits()) - .setIdLSB(entityViewId.getId().getLeastSignificantBits()).build(); - } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/EntityViewMsgConstructorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/EntityViewMsgConstructorV2.java new file mode 100644 index 0000000000..c1eae91097 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/EntityViewMsgConstructorV2.java @@ -0,0 +1,35 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.entityview; + +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.EntityView; +import org.thingsboard.server.gen.edge.v1.EntityViewUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class EntityViewMsgConstructorV2 extends BaseEntityViewMsgConstructor { + + @Override + public EntityViewUpdateMsg constructEntityViewUpdatedMsg(UpdateMsgType msgType, EntityView entityView) { + return EntityViewUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(entityView)) + .setIdMSB(entityView.getId().getId().getMostSignificantBits()) + .setIdLSB(entityView.getId().getId().getLeastSignificantBits()).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/BaseOtaPackageMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/BaseOtaPackageMsgConstructor.java new file mode 100644 index 0000000000..110c24caca --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/BaseOtaPackageMsgConstructor.java @@ -0,0 +1,31 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.ota; + +import org.thingsboard.server.common.data.id.OtaPackageId; +import org.thingsboard.server.gen.edge.v1.OtaPackageUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; + +public abstract class BaseOtaPackageMsgConstructor implements OtaPackageMsgConstructor { + + @Override + public OtaPackageUpdateMsg constructOtaPackageDeleteMsg(OtaPackageId otaPackageId) { + return OtaPackageUpdateMsg.newBuilder() + .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) + .setIdMSB(otaPackageId.getId().getMostSignificantBits()) + .setIdLSB(otaPackageId.getId().getLeastSignificantBits()).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/OtaPackageMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/OtaPackageMsgConstructor.java new file mode 100644 index 0000000000..17b6ea8338 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/OtaPackageMsgConstructor.java @@ -0,0 +1,28 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.ota; + +import org.thingsboard.server.common.data.OtaPackage; +import org.thingsboard.server.common.data.id.OtaPackageId; +import org.thingsboard.server.gen.edge.v1.OtaPackageUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; + +public interface OtaPackageMsgConstructor { + + OtaPackageUpdateMsg constructOtaPackageUpdatedMsg(UpdateMsgType msgType, OtaPackage otaPackage); + + OtaPackageUpdateMsg constructOtaPackageDeleteMsg(OtaPackageId otaPackageId); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/OtaPackageMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/OtaPackageMsgConstructorFactory.java new file mode 100644 index 0000000000..ae7a48028c --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/OtaPackageMsgConstructorFactory.java @@ -0,0 +1,45 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.ota; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class OtaPackageMsgConstructorFactory { + + @Autowired + protected OtaPackageMsgConstructorV1 otaPackageMsgConstructorV1; + + @Autowired + protected OtaPackageMsgConstructorV2 otaPackageMsgConstructorV2; + + public OtaPackageMsgConstructor getOtaPackageMsgConstructor(EdgeVersion edgeVersion) { + switch (edgeVersion) { + case V_3_3_0: + case V_3_3_3: + case V_3_4_0: + case V_3_6_0: + case V_3_6_1: + return otaPackageMsgConstructorV1; + default: + return otaPackageMsgConstructorV2; + } + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/OtaPackageMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/OtaPackageMsgConstructorV1.java similarity index 69% rename from application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/OtaPackageMsgConstructor.java rename to application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/OtaPackageMsgConstructorV1.java index a626574d92..5e1f297c01 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/OtaPackageMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/OtaPackageMsgConstructorV1.java @@ -13,33 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.edge.rpc.constructor; +package org.thingsboard.server.service.edge.rpc.constructor.ota; import com.google.protobuf.ByteString; import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.OtaPackage; -import org.thingsboard.server.common.data.id.OtaPackageId; -import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.OtaPackageUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; @Component @TbCoreComponent -public class OtaPackageMsgConstructor { +public class OtaPackageMsgConstructorV1 extends BaseOtaPackageMsgConstructor { - public OtaPackageUpdateMsg constructOtaPackageUpdatedMsg(UpdateMsgType msgType, OtaPackage otaPackage, EdgeVersion edgeVersion) { - if (EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)) { - return constructDeprecatedOtaPackageUpdatedMsg(msgType, otaPackage); - } - return OtaPackageUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(otaPackage)) - .setIdMSB(otaPackage.getId().getId().getMostSignificantBits()) - .setIdLSB(otaPackage.getId().getId().getLeastSignificantBits()).build(); - } - - private OtaPackageUpdateMsg constructDeprecatedOtaPackageUpdatedMsg(UpdateMsgType msgType, OtaPackage otaPackage) { + @Override + public OtaPackageUpdateMsg constructOtaPackageUpdatedMsg(UpdateMsgType msgType, OtaPackage otaPackage) { OtaPackageUpdateMsg.Builder builder = OtaPackageUpdateMsg.newBuilder() .setMsgType(msgType) .setIdMSB(otaPackage.getId().getId().getMostSignificantBits()) @@ -80,11 +69,4 @@ public class OtaPackageMsgConstructor { } return builder.build(); } - - public OtaPackageUpdateMsg constructOtaPackageDeleteMsg(OtaPackageId otaPackageId) { - return OtaPackageUpdateMsg.newBuilder() - .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) - .setIdMSB(otaPackageId.getId().getMostSignificantBits()) - .setIdLSB(otaPackageId.getId().getLeastSignificantBits()).build(); - } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/OtaPackageMsgConstructorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/OtaPackageMsgConstructorV2.java new file mode 100644 index 0000000000..69d7f12874 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/OtaPackageMsgConstructorV2.java @@ -0,0 +1,35 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.ota; + +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.OtaPackage; +import org.thingsboard.server.gen.edge.v1.OtaPackageUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class OtaPackageMsgConstructorV2 extends BaseOtaPackageMsgConstructor { + + @Override + public OtaPackageUpdateMsg constructOtaPackageUpdatedMsg(UpdateMsgType msgType, OtaPackage otaPackage) { + return OtaPackageUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(otaPackage)) + .setIdMSB(otaPackage.getId().getId().getMostSignificantBits()) + .setIdLSB(otaPackage.getId().getId().getLeastSignificantBits()).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/BaseQueueMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/BaseQueueMsgConstructor.java new file mode 100644 index 0000000000..486a143d85 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/BaseQueueMsgConstructor.java @@ -0,0 +1,31 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.queue; + +import org.thingsboard.server.common.data.id.QueueId; +import org.thingsboard.server.gen.edge.v1.QueueUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; + +public abstract class BaseQueueMsgConstructor implements QueueMsgConstructor { + + @Override + public QueueUpdateMsg constructQueueDeleteMsg(QueueId queueId) { + return QueueUpdateMsg.newBuilder() + .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) + .setIdMSB(queueId.getId().getMostSignificantBits()) + .setIdLSB(queueId.getId().getLeastSignificantBits()).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/QueueMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/QueueMsgConstructor.java new file mode 100644 index 0000000000..a7d0776136 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/QueueMsgConstructor.java @@ -0,0 +1,28 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.queue; + +import org.thingsboard.server.common.data.id.QueueId; +import org.thingsboard.server.common.data.queue.Queue; +import org.thingsboard.server.gen.edge.v1.QueueUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; + +public interface QueueMsgConstructor { + + QueueUpdateMsg constructQueueUpdatedMsg(UpdateMsgType msgType, Queue queue); + + QueueUpdateMsg constructQueueDeleteMsg(QueueId queueId); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/QueueMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/QueueMsgConstructorFactory.java new file mode 100644 index 0000000000..d250c5fd24 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/QueueMsgConstructorFactory.java @@ -0,0 +1,45 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.queue; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class QueueMsgConstructorFactory { + + @Autowired + protected QueueMsgConstructorV1 queueMsgConstructorV1; + + @Autowired + protected QueueMsgConstructorV2 queueMsgConstructorV2; + + public QueueMsgConstructor getQueueMsgConstructor(EdgeVersion edgeVersion) { + switch (edgeVersion) { + case V_3_3_0: + case V_3_3_3: + case V_3_4_0: + case V_3_6_0: + case V_3_6_1: + return queueMsgConstructorV1; + default: + return queueMsgConstructorV2; + } + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/QueueMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/QueueMsgConstructorV1.java similarity index 77% rename from application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/QueueMsgConstructor.java rename to application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/QueueMsgConstructorV1.java index d1901cdf83..8621ce1011 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/QueueMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/QueueMsgConstructorV1.java @@ -13,37 +13,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.edge.rpc.constructor; +package org.thingsboard.server.service.edge.rpc.constructor.queue; import org.springframework.stereotype.Component; -import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.id.QueueId; import org.thingsboard.server.common.data.queue.ProcessingStrategy; import org.thingsboard.server.common.data.queue.Queue; import org.thingsboard.server.common.data.queue.SubmitStrategy; -import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.ProcessingStrategyProto; import org.thingsboard.server.gen.edge.v1.QueueUpdateMsg; import org.thingsboard.server.gen.edge.v1.SubmitStrategyProto; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; @Component @TbCoreComponent -public class QueueMsgConstructor { +public class QueueMsgConstructorV1 extends BaseQueueMsgConstructor { - public QueueUpdateMsg constructQueueUpdatedMsg(UpdateMsgType msgType, Queue queue, EdgeVersion edgeVersion) { - if (EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)) { - return constructDeprecatedQueueUpdatedMsg(msgType, queue); - } - return QueueUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(queue)) - .setIdMSB(queue.getId().getId().getMostSignificantBits()) - .setIdLSB(queue.getId().getId().getLeastSignificantBits()).build(); - } - - private QueueUpdateMsg constructDeprecatedQueueUpdatedMsg(UpdateMsgType msgType, Queue queue) { - QueueUpdateMsg.Builder builder = QueueUpdateMsg.newBuilder() + @Override + public QueueUpdateMsg constructQueueUpdatedMsg(UpdateMsgType msgType, Queue queue) { + return QueueUpdateMsg.newBuilder() .setMsgType(msgType) .setIdMSB(queue.getId().getId().getMostSignificantBits()) .setIdLSB(queue.getId().getId().getLeastSignificantBits()) @@ -56,8 +45,7 @@ public class QueueMsgConstructor { .setConsumerPerPartition(queue.isConsumerPerPartition()) .setPackProcessingTimeout(queue.getPackProcessingTimeout()) .setSubmitStrategy(createSubmitStrategyProto(queue.getSubmitStrategy())) - .setProcessingStrategy(createProcessingStrategyProto(queue.getProcessingStrategy())); - return builder.build(); + .setProcessingStrategy(createProcessingStrategyProto(queue.getProcessingStrategy())).build(); } private ProcessingStrategyProto createProcessingStrategyProto(ProcessingStrategy processingStrategy) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/QueueMsgConstructorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/QueueMsgConstructorV2.java new file mode 100644 index 0000000000..1bf80eea64 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/QueueMsgConstructorV2.java @@ -0,0 +1,35 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.queue; + +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.queue.Queue; +import org.thingsboard.server.gen.edge.v1.QueueUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class QueueMsgConstructorV2 extends BaseQueueMsgConstructor { + + @Override + public QueueUpdateMsg constructQueueUpdatedMsg(UpdateMsgType msgType, Queue queue) { + return QueueUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(queue)) + .setIdMSB(queue.getId().getId().getMostSignificantBits()) + .setIdLSB(queue.getId().getId().getLeastSignificantBits()).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/relation/RelationMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/relation/RelationMsgConstructor.java new file mode 100644 index 0000000000..10d0a71830 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/relation/RelationMsgConstructor.java @@ -0,0 +1,25 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.relation; + +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; + +public interface RelationMsgConstructor { + + RelationUpdateMsg constructRelationUpdatedMsg(UpdateMsgType msgType, EntityRelation entityRelation); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/relation/RelationMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/relation/RelationMsgConstructorFactory.java new file mode 100644 index 0000000000..a17266300d --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/relation/RelationMsgConstructorFactory.java @@ -0,0 +1,45 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.relation; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class RelationMsgConstructorFactory { + + @Autowired + protected RelationMsgConstructorV1 relationMsgConstructorV1; + + @Autowired + protected RelationMsgConstructorV2 relationMsgConstructorV2; + + public RelationMsgConstructor getRelationMsgConstructor(EdgeVersion edgeVersion) { + switch (edgeVersion) { + case V_3_3_0: + case V_3_3_3: + case V_3_4_0: + case V_3_6_0: + case V_3_6_1: + return relationMsgConstructorV1; + default: + return relationMsgConstructorV2; + } + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RelationMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/relation/RelationMsgConstructorV1.java similarity index 74% rename from application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RelationMsgConstructor.java rename to application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/relation/RelationMsgConstructorV1.java index 01abb37236..394086fdd0 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RelationMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/relation/RelationMsgConstructorV1.java @@ -13,29 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.edge.rpc.constructor; +package org.thingsboard.server.service.edge.rpc.constructor.relation; import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.relation.EntityRelation; -import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; @Component @TbCoreComponent -public class RelationMsgConstructor { +public class RelationMsgConstructorV1 implements RelationMsgConstructor { - public RelationUpdateMsg constructRelationUpdatedMsg(UpdateMsgType msgType, EntityRelation entityRelation, EdgeVersion edgeVersion) { - if (EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)) { - return constructDeprecatedRelationUpdatedMsg(msgType, entityRelation); - } - return RelationUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(entityRelation)).build(); - } - - private RelationUpdateMsg constructDeprecatedRelationUpdatedMsg(UpdateMsgType msgType, EntityRelation entityRelation) { + @Override + public RelationUpdateMsg constructRelationUpdatedMsg(UpdateMsgType msgType, EntityRelation entityRelation) { RelationUpdateMsg.Builder builder = RelationUpdateMsg.newBuilder() .setMsgType(msgType) .setFromIdMSB(entityRelation.getFrom().getId().getMostSignificantBits()) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/relation/RelationMsgConstructorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/relation/RelationMsgConstructorV2.java new file mode 100644 index 0000000000..4510909490 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/relation/RelationMsgConstructorV2.java @@ -0,0 +1,34 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.relation; + +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class RelationMsgConstructorV2 implements RelationMsgConstructor { + + + @Override + public RelationUpdateMsg constructRelationUpdatedMsg(UpdateMsgType msgType, EntityRelation entityRelation) { + return RelationUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(entityRelation)).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/BaseResourceMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/BaseResourceMsgConstructor.java new file mode 100644 index 0000000000..620f71c9e7 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/BaseResourceMsgConstructor.java @@ -0,0 +1,31 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.resource; + +import org.thingsboard.server.common.data.id.TbResourceId; +import org.thingsboard.server.gen.edge.v1.ResourceUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; + +public abstract class BaseResourceMsgConstructor implements ResourceMsgConstructor { + + @Override + public ResourceUpdateMsg constructResourceDeleteMsg(TbResourceId tbResourceId) { + return ResourceUpdateMsg.newBuilder() + .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) + .setIdMSB(tbResourceId.getId().getMostSignificantBits()) + .setIdLSB(tbResourceId.getId().getLeastSignificantBits()).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/ResourceMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/ResourceMsgConstructor.java new file mode 100644 index 0000000000..b631e737e2 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/ResourceMsgConstructor.java @@ -0,0 +1,28 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.resource; + +import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.common.data.id.TbResourceId; +import org.thingsboard.server.gen.edge.v1.ResourceUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; + +public interface ResourceMsgConstructor { + + ResourceUpdateMsg constructResourceUpdatedMsg(UpdateMsgType msgType, TbResource tbResource); + + ResourceUpdateMsg constructResourceDeleteMsg(TbResourceId tbResourceId); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/ResourceMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/ResourceMsgConstructorFactory.java new file mode 100644 index 0000000000..ef32b623de --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/ResourceMsgConstructorFactory.java @@ -0,0 +1,45 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.resource; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class ResourceMsgConstructorFactory { + + @Autowired + protected ResourceMsgConstructorV1 resourceMsgConstructorV1; + + @Autowired + protected ResourceMsgConstructorV2 resourceMsgConstructorV2; + + public ResourceMsgConstructor getResourceMsgConstructor(EdgeVersion edgeVersion) { + switch (edgeVersion) { + case V_3_3_0: + case V_3_3_3: + case V_3_4_0: + case V_3_6_0: + case V_3_6_1: + return resourceMsgConstructorV1; + default: + return resourceMsgConstructorV2; + } + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ResourceMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/ResourceMsgConstructorV1.java similarity index 57% rename from application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ResourceMsgConstructor.java rename to application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/ResourceMsgConstructorV1.java index bf9a0e4521..fa92832741 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ResourceMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/ResourceMsgConstructorV1.java @@ -13,33 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.edge.rpc.constructor; +package org.thingsboard.server.service.edge.rpc.constructor.resource; import org.springframework.stereotype.Component; -import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.TbResource; -import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.ResourceUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; @Component @TbCoreComponent -public class ResourceMsgConstructor { +public class ResourceMsgConstructorV1 extends BaseResourceMsgConstructor { - public ResourceUpdateMsg constructResourceUpdatedMsg(UpdateMsgType msgType, TbResource tbResource, EdgeVersion edgeVersion) { - if (EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)) { - return constructDeprecatedResourceUpdateMsg(msgType, tbResource); - } - return ResourceUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(tbResource)) - .setIdMSB(tbResource.getId().getId().getMostSignificantBits()) - .setIdLSB(tbResource.getId().getId().getLeastSignificantBits()).build(); - } - - private ResourceUpdateMsg constructDeprecatedResourceUpdateMsg(UpdateMsgType msgType, TbResource tbResource) { + @Override + public ResourceUpdateMsg constructResourceUpdatedMsg(UpdateMsgType msgType, TbResource tbResource) { ResourceUpdateMsg.Builder builder = ResourceUpdateMsg.newBuilder() .setMsgType(msgType) .setIdMSB(tbResource.getId().getId().getMostSignificantBits()) @@ -54,16 +42,9 @@ public class ResourceMsgConstructor { if (tbResource.getEtag() != null) { builder.setEtag(tbResource.getEtag()); } - if (tbResource.getTenantId().equals(TenantId.SYS_TENANT_ID)) { + if (TenantId.SYS_TENANT_ID.equals(tbResource.getTenantId())) { builder.setIsSystem(true); } return builder.build(); } - - public ResourceUpdateMsg constructResourceDeleteMsg(TbResourceId tbResourceId) { - return ResourceUpdateMsg.newBuilder() - .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) - .setIdMSB(tbResourceId.getId().getMostSignificantBits()) - .setIdLSB(tbResourceId.getId().getLeastSignificantBits()).build(); - } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/ResourceMsgConstructorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/ResourceMsgConstructorV2.java new file mode 100644 index 0000000000..094e5636bb --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/ResourceMsgConstructorV2.java @@ -0,0 +1,35 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.resource; + +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.gen.edge.v1.ResourceUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class ResourceMsgConstructorV2 extends BaseResourceMsgConstructor { + + @Override + public ResourceUpdateMsg constructResourceUpdatedMsg(UpdateMsgType msgType, TbResource tbResource) { + return ResourceUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(tbResource)) + .setIdMSB(tbResource.getId().getId().getMostSignificantBits()) + .setIdLSB(tbResource.getId().getId().getLeastSignificantBits()).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/AbstractRuleChainMetadataConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/BaseRuleChainMetadataConstructor.java similarity index 87% rename from application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/AbstractRuleChainMetadataConstructor.java rename to application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/BaseRuleChainMetadataConstructor.java index eaab137301..77815351bc 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/AbstractRuleChainMetadataConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/BaseRuleChainMetadataConstructor.java @@ -30,7 +30,6 @@ import org.thingsboard.server.gen.edge.v1.RuleChainConnectionInfoProto; import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; import org.thingsboard.server.gen.edge.v1.RuleNodeProto; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; -import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; import java.util.ArrayList; import java.util.List; @@ -45,17 +44,7 @@ public abstract class AbstractRuleChainMetadataConstructor implements RuleChainM UpdateMsgType msgType, RuleChainMetaData ruleChainMetaData, EdgeVersion edgeVersion) { - return EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion) - ? constructDeprecatedRuleChainMetadataUpdatedMsg(tenantId, msgType, ruleChainMetaData) - : RuleChainMetadataUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(ruleChainMetaData)).build(); - } - - private RuleChainMetadataUpdateMsg constructDeprecatedRuleChainMetadataUpdatedMsg(TenantId tenantId, - UpdateMsgType msgType, - RuleChainMetaData ruleChainMetaData) { RuleChainMetadataUpdateMsg.Builder builder = RuleChainMetadataUpdateMsg.newBuilder(); - builder.setRuleChainIdMSB(ruleChainMetaData.getRuleChainId().getId().getMostSignificantBits()) - .setRuleChainIdLSB(ruleChainMetaData.getRuleChainId().getId().getLeastSignificantBits()); constructRuleChainMetadataUpdatedMsg(tenantId, builder, ruleChainMetaData); builder.setMsgType(msgType); return builder.build(); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/BaseRuleChainMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/BaseRuleChainMsgConstructor.java new file mode 100644 index 0000000000..da237de924 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/BaseRuleChainMsgConstructor.java @@ -0,0 +1,44 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.rule; + +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.rule.RuleChainMetaData; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; +import org.thingsboard.server.gen.edge.v1.RuleChainUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; + +public abstract class AbstractRuleChainMsgConstructor implements RuleChainMsgConstructor { + + @Override + public RuleChainUpdateMsg constructRuleChainDeleteMsg(RuleChainId ruleChainId) { + return RuleChainUpdateMsg.newBuilder() + .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) + .setIdMSB(ruleChainId.getId().getMostSignificantBits()) + .setIdLSB(ruleChainId.getId().getLeastSignificantBits()).build(); + } + + @Override + public RuleChainMetadataUpdateMsg constructRuleChainMetadataUpdatedMsg(TenantId tenantId, + UpdateMsgType msgType, + RuleChainMetaData ruleChainMetaData, + EdgeVersion edgeVersion) { + RuleChainMetadataConstructor ruleChainMetadataConstructor = RuleChainMetadataConstructorFactory.getByEdgeVersion(edgeVersion); + return ruleChainMetadataConstructor.constructRuleChainMetadataUpdatedMsg(tenantId, msgType, ruleChainMetaData, edgeVersion); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorFactory.java index 1c581267c4..cdc7e128d3 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorFactory.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorFactory.java @@ -25,8 +25,12 @@ public final class RuleChainMetadataConstructorFactory { return new RuleChainMetadataConstructorV330(); case V_3_3_3: case V_3_4_0: - default: + case V_3_6_0: + case V_3_6_1: return new RuleChainMetadataConstructorV340(); + case V_3_6_2: + default: + return new RuleChainMetadataConstructorV362(); } } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV330.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV330.java index 33fc39b7e7..cfa6f5edbe 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV330.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV330.java @@ -45,6 +45,8 @@ public class RuleChainMetadataConstructorV330 extends AbstractRuleChainMetadataC protected void constructRuleChainMetadataUpdatedMsg(TenantId tenantId, RuleChainMetadataUpdateMsg.Builder builder, RuleChainMetaData ruleChainMetaData) { + builder.setRuleChainIdMSB(ruleChainMetaData.getRuleChainId().getId().getMostSignificantBits()) + .setRuleChainIdLSB(ruleChainMetaData.getRuleChainId().getId().getLeastSignificantBits()); List supportedNodes = filterNodes(ruleChainMetaData.getNodes()); NavigableSet removedNodeIndexes = getRemovedNodeIndexes(ruleChainMetaData.getNodes(), ruleChainMetaData.getConnections()); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV340.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV340.java index 9a37e416db..48c36c2b4d 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV340.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV340.java @@ -29,6 +29,8 @@ public class RuleChainMetadataConstructorV340 extends AbstractRuleChainMetadataC protected void constructRuleChainMetadataUpdatedMsg(TenantId tenantId, RuleChainMetadataUpdateMsg.Builder builder, RuleChainMetaData ruleChainMetaData) { + builder.setRuleChainIdMSB(ruleChainMetaData.getRuleChainId().getId().getMostSignificantBits()) + .setRuleChainIdLSB(ruleChainMetaData.getRuleChainId().getId().getLeastSignificantBits()); builder.addAllNodes(constructNodes(ruleChainMetaData.getNodes())) .addAllConnections(constructConnections(ruleChainMetaData.getConnections())) .addAllRuleChainConnections(constructRuleChainConnections(ruleChainMetaData.getRuleChainConnections(), new TreeSet<>())); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV362.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV362.java new file mode 100644 index 0000000000..e1b1ea4d13 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV362.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.rule; + +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.rule.RuleChainMetaData; +import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; + +public class RuleChainMetadataConstructorV362 extends AbstractRuleChainMetadataConstructor { + + @Override + protected void constructRuleChainMetadataUpdatedMsg(TenantId tenantId, RuleChainMetadataUpdateMsg.Builder builder, RuleChainMetaData ruleChainMetaData) { + builder.setEntity(JacksonUtil.toString(ruleChainMetaData)); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructor.java new file mode 100644 index 0000000000..c1665ef48d --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructor.java @@ -0,0 +1,37 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.rule; + +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.common.data.rule.RuleChainMetaData; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; +import org.thingsboard.server.gen.edge.v1.RuleChainUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; + +public interface RuleChainMsgConstructor { + + RuleChainUpdateMsg constructRuleChainUpdatedMsg(UpdateMsgType msgType, RuleChain ruleChain, boolean isRoot); + + RuleChainUpdateMsg constructRuleChainDeleteMsg(RuleChainId ruleChainId); + + RuleChainMetadataUpdateMsg constructRuleChainMetadataUpdatedMsg(TenantId tenantId, + UpdateMsgType msgType, + RuleChainMetaData ruleChainMetaData, + EdgeVersion edgeVersion); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructorFactory.java new file mode 100644 index 0000000000..b989612431 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructorFactory.java @@ -0,0 +1,45 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.rule; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class RuleChainMsgConstructorFactory { + + @Autowired + protected RuleChainMsgConstructorV1 ruleChainMsgConstructorV1; + + @Autowired + protected RuleChainMsgConstructorV2 ruleChainMsgConstructorV2; + + public RuleChainMsgConstructor getRuleChainMsgConstructor(EdgeVersion edgeVersion) { + switch (edgeVersion) { + case V_3_3_0: + case V_3_3_3: + case V_3_4_0: + case V_3_6_0: + case V_3_6_1: + return ruleChainMsgConstructorV1; + default: + return ruleChainMsgConstructorV2; + } + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructorV1.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructorV1.java new file mode 100644 index 0000000000..8404b5d67d --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructorV1.java @@ -0,0 +1,45 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.rule; + +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.gen.edge.v1.RuleChainUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class RuleChainMsgConstructorV1 extends AbstractRuleChainMsgConstructor { + + @Override + public RuleChainUpdateMsg constructRuleChainUpdatedMsg(UpdateMsgType msgType, RuleChain ruleChain, boolean isRoot) { + RuleChainUpdateMsg.Builder builder = RuleChainUpdateMsg.newBuilder() + .setMsgType(msgType) + .setIdMSB(ruleChain.getId().getId().getMostSignificantBits()) + .setIdLSB(ruleChain.getId().getId().getLeastSignificantBits()) + .setName(ruleChain.getName()) + .setRoot(isRoot) + .setDebugMode(ruleChain.isDebugMode()) + .setConfiguration(JacksonUtil.toString(ruleChain.getConfiguration())); + if (ruleChain.getFirstRuleNodeId() != null) { + builder.setFirstRuleNodeIdMSB(ruleChain.getFirstRuleNodeId().getId().getMostSignificantBits()) + .setFirstRuleNodeIdLSB(ruleChain.getFirstRuleNodeId().getId().getLeastSignificantBits()); + } + return builder.build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructorV2.java new file mode 100644 index 0000000000..492ea6c40d --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructorV2.java @@ -0,0 +1,39 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.rule; + +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.gen.edge.v1.RuleChainUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class RuleChainMsgConstructorV2 extends AbstractRuleChainMsgConstructor { + + @Override + public RuleChainUpdateMsg constructRuleChainUpdatedMsg(UpdateMsgType msgType, RuleChain ruleChain, boolean isRoot) { + boolean isTemplateRoot = ruleChain.isRoot(); + ruleChain.setRoot(isRoot); + RuleChainUpdateMsg result = RuleChainUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(ruleChain)) + .setIdMSB(ruleChain.getId().getId().getMostSignificantBits()) + .setIdLSB(ruleChain.getId().getId().getLeastSignificantBits()).build(); + ruleChain.setRoot(isTemplateRoot); + return result; + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/settings/AdminSettingsMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/settings/AdminSettingsMsgConstructor.java new file mode 100644 index 0000000000..4e5aa7bf54 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/settings/AdminSettingsMsgConstructor.java @@ -0,0 +1,24 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.settings; + +import org.thingsboard.server.common.data.AdminSettings; +import org.thingsboard.server.gen.edge.v1.AdminSettingsUpdateMsg; + +public interface AdminSettingsMsgConstructor { + + AdminSettingsUpdateMsg constructAdminSettingsUpdateMsg(AdminSettings adminSettings); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/settings/AdminSettingsMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/settings/AdminSettingsMsgConstructorFactory.java new file mode 100644 index 0000000000..060c7fa3a5 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/settings/AdminSettingsMsgConstructorFactory.java @@ -0,0 +1,45 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.settings; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class AdminSettingsMsgConstructorFactory { + + @Autowired + protected AdminSettingsMsgConstructorV1 adminSettingsMsgConstructorV1; + + @Autowired + protected AdminSettingsMsgConstructorV2 adminSettingsMsgConstructorV2; + + public AdminSettingsMsgConstructor getAdminSettingsMsgConstructor(EdgeVersion edgeVersion) { + switch (edgeVersion) { + case V_3_3_0: + case V_3_3_3: + case V_3_4_0: + case V_3_6_0: + case V_3_6_1: + return adminSettingsMsgConstructorV1; + default: + return adminSettingsMsgConstructorV2; + } + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AdminSettingsMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/settings/AdminSettingsMsgConstructorV1.java similarity index 67% rename from application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AdminSettingsMsgConstructor.java rename to application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/settings/AdminSettingsMsgConstructorV1.java index e82f3553c3..cbbd9b5bb8 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AdminSettingsMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/settings/AdminSettingsMsgConstructorV1.java @@ -13,29 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.edge.rpc.constructor; +package org.thingsboard.server.service.edge.rpc.constructor.settings; import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.gen.edge.v1.AdminSettingsUpdateMsg; -import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; @Component @TbCoreComponent -public class AdminSettingsMsgConstructor { +public class AdminSettingsMsgConstructorV1 implements AdminSettingsMsgConstructor { - public AdminSettingsUpdateMsg constructAdminSettingsUpdateMsg(AdminSettings adminSettings, EdgeVersion edgeVersion) { - if (EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)) { - return constructDeprecatedAdminSettingsUpdateMsg(adminSettings); - } - return AdminSettingsUpdateMsg.newBuilder().setEntity(JacksonUtil.toString(adminSettings)).build(); - } - - private AdminSettingsUpdateMsg constructDeprecatedAdminSettingsUpdateMsg(AdminSettings adminSettings) { + @Override + public AdminSettingsUpdateMsg constructAdminSettingsUpdateMsg(AdminSettings adminSettings) { AdminSettingsUpdateMsg.Builder builder = AdminSettingsUpdateMsg.newBuilder() .setKey(adminSettings.getKey()) .setJsonValue(JacksonUtil.toString(adminSettings.getJsonValue())); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/settings/AdminSettingsMsgConstructorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/settings/AdminSettingsMsgConstructorV2.java new file mode 100644 index 0000000000..de92d2a1f9 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/settings/AdminSettingsMsgConstructorV2.java @@ -0,0 +1,32 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.settings; + +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.AdminSettings; +import org.thingsboard.server.gen.edge.v1.AdminSettingsUpdateMsg; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class AdminSettingsMsgConstructorV2 implements AdminSettingsMsgConstructor { + + @Override + public AdminSettingsUpdateMsg constructAdminSettingsUpdateMsg(AdminSettings adminSettings) { + return AdminSettingsUpdateMsg.newBuilder().setEntity(JacksonUtil.toString(adminSettings)).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityDataMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/telemetry/EntityDataMsgConstructor.java similarity index 98% rename from application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityDataMsgConstructor.java rename to application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/telemetry/EntityDataMsgConstructor.java index 47dbad231d..9a2ea2abf6 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityDataMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/telemetry/EntityDataMsgConstructor.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.edge.rpc.constructor; +package org.thingsboard.server.service.edge.rpc.constructor.telemetry; import com.google.gson.Gson; import com.google.gson.JsonArray; @@ -36,8 +36,8 @@ import org.thingsboard.server.queue.util.TbCoreComponent; import java.util.List; -@Component @Slf4j +@Component @TbCoreComponent public class EntityDataMsgConstructor { @@ -106,5 +106,4 @@ public class EntityDataMsgConstructor { } return result; } - } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructor.java new file mode 100644 index 0000000000..612703b3c6 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructor.java @@ -0,0 +1,30 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.tenant; + +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.TenantProfile; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg; +import org.thingsboard.server.gen.edge.v1.TenantUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; + +public interface TenantMsgConstructor { + + TenantUpdateMsg constructTenantUpdateMsg(UpdateMsgType msgType, Tenant tenant); + + TenantProfileUpdateMsg constructTenantProfileUpdateMsg(UpdateMsgType msgType, TenantProfile tenantProfile, EdgeVersion edgeVersion); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorFactory.java new file mode 100644 index 0000000000..753eb0dc05 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorFactory.java @@ -0,0 +1,45 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.tenant; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class TenantMsgConstructorFactory { + + @Autowired + protected TenantMsgConstructorV1 tenantMsgConstructorV1; + + @Autowired + protected TenantMsgConstructorV2 tenantMsgConstructorV2; + + public TenantMsgConstructor getTenantMsgConstructor(EdgeVersion edgeVersion) { + switch (edgeVersion) { + case V_3_3_0: + case V_3_3_3: + case V_3_4_0: + case V_3_6_0: + case V_3_6_1: + return tenantMsgConstructorV1; + default: + return tenantMsgConstructorV2; + } + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/TenantMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV1.java similarity index 62% rename from application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/TenantMsgConstructor.java rename to application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV1.java index 93bf46899e..a3f815cc18 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/TenantMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV1.java @@ -13,29 +13,31 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.edge.rpc.constructor; +package org.thingsboard.server.service.edge.rpc.constructor.tenant; +import com.google.protobuf.ByteString; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg; import org.thingsboard.server.gen.edge.v1.TenantUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.queue.util.DataDecodingEncodingService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; @Component @TbCoreComponent -public class TenantMsgConstructor { +public class TenantMsgConstructorV1 implements TenantMsgConstructor { - public TenantUpdateMsg constructTenantUpdateMsg(UpdateMsgType msgType, Tenant tenant, EdgeVersion edgeVersion) { - if (EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)) { - return constructDeprecatedTenantUpdateMsg(msgType, tenant); - } - return TenantUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(tenant)).build(); - } + @Autowired + private DataDecodingEncodingService dataDecodingEncodingService; - private TenantUpdateMsg constructDeprecatedTenantUpdateMsg(UpdateMsgType msgType, Tenant tenant) { + @Override + public TenantUpdateMsg constructTenantUpdateMsg(UpdateMsgType msgType, Tenant tenant) { TenantUpdateMsg.Builder builder = TenantUpdateMsg.newBuilder() .setMsgType(msgType) .setIdMSB(tenant.getId().getId().getMostSignificantBits()) @@ -73,4 +75,22 @@ public class TenantMsgConstructor { } return builder.build(); } + + @Override + public TenantProfileUpdateMsg constructTenantProfileUpdateMsg(UpdateMsgType msgType, TenantProfile tenantProfile, EdgeVersion edgeVersion) { + ByteString profileData = EdgeVersionUtils.isEdgeVersionOlderThan(edgeVersion, EdgeVersion.V_3_6_1) ? + ByteString.empty() : ByteString.copyFrom(dataDecodingEncodingService.encode(tenantProfile.getProfileData())); + TenantProfileUpdateMsg.Builder builder = TenantProfileUpdateMsg.newBuilder() + .setMsgType(msgType) + .setIdMSB(tenantProfile.getId().getId().getMostSignificantBits()) + .setIdLSB(tenantProfile.getId().getId().getLeastSignificantBits()) + .setName(tenantProfile.getName()) + .setDefault(tenantProfile.isDefault()) + .setIsolatedRuleChain(tenantProfile.isIsolatedTbRuleEngine()) + .setProfileDataBytes(profileData); + if (tenantProfile.getDescription() != null) { + builder.setDescription(tenantProfile.getDescription()); + } + return builder.build(); + } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV2.java new file mode 100644 index 0000000000..69531e5630 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV2.java @@ -0,0 +1,41 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.tenant; + +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.TenantProfile; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg; +import org.thingsboard.server.gen.edge.v1.TenantUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class TenantMsgConstructorV2 implements TenantMsgConstructor { + + @Override + public TenantUpdateMsg constructTenantUpdateMsg(UpdateMsgType msgType, Tenant tenant) { + return TenantUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(tenant)).build(); + } + + @Override + public TenantProfileUpdateMsg constructTenantProfileUpdateMsg(UpdateMsgType msgType, TenantProfile tenantProfile, EdgeVersion edgeVersion) { + return TenantProfileUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(tenantProfile)).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/BaseUserMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/BaseUserMsgConstructor.java new file mode 100644 index 0000000000..aef35d8f50 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/BaseUserMsgConstructor.java @@ -0,0 +1,31 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.user; + +import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.gen.edge.v1.UserUpdateMsg; + +public abstract class BaseUserMsgConstructor implements UserMsgConstructor { + + @Override + public UserUpdateMsg constructUserDeleteMsg(UserId userId) { + return UserUpdateMsg.newBuilder() + .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) + .setIdMSB(userId.getId().getMostSignificantBits()) + .setIdLSB(userId.getId().getLeastSignificantBits()).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/UserMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/UserMsgConstructor.java new file mode 100644 index 0000000000..e6e2c9572b --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/UserMsgConstructor.java @@ -0,0 +1,32 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.user; + +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.security.UserCredentials; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.gen.edge.v1.UserCredentialsUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UserUpdateMsg; + +public interface UserMsgConstructor { + + UserUpdateMsg constructUserUpdatedMsg(UpdateMsgType msgType, User user); + + UserUpdateMsg constructUserDeleteMsg(UserId userId); + + UserCredentialsUpdateMsg constructUserCredentialsUpdatedMsg(UserCredentials userCredentials); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/UserMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/UserMsgConstructorFactory.java new file mode 100644 index 0000000000..47554ce398 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/UserMsgConstructorFactory.java @@ -0,0 +1,45 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.user; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class UserMsgConstructorFactory { + + @Autowired + protected UserMsgConstructorV1 userMsgConstructorV1; + + @Autowired + protected UserMsgConstructorV2 userMsgConstructorV2; + + public UserMsgConstructor getUserMsgConstructor(EdgeVersion edgeVersion) { + switch (edgeVersion) { + case V_3_3_0: + case V_3_3_3: + case V_3_4_0: + case V_3_6_0: + case V_3_6_1: + return userMsgConstructorV1; + default: + return userMsgConstructorV2; + } + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/UserMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/UserMsgConstructorV1.java similarity index 57% rename from application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/UserMsgConstructor.java rename to application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/UserMsgConstructorV1.java index 43771a77c0..3fe635c54c 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/UserMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/UserMsgConstructorV1.java @@ -13,34 +13,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.edge.rpc.constructor; +package org.thingsboard.server.service.edge.rpc.constructor.user; import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.User; -import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.security.UserCredentials; -import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.UserCredentialsUpdateMsg; import org.thingsboard.server.gen.edge.v1.UserUpdateMsg; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; @Component @TbCoreComponent -public class UserMsgConstructor { +public class UserMsgConstructorV1 extends BaseUserMsgConstructor { - public UserUpdateMsg constructUserUpdatedMsg(UpdateMsgType msgType, User user, EdgeVersion edgeVersion) { - if (EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)) { - return constructDeprecatedUserUpdatedMsg(msgType, user); - } - return UserUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(user)) - .setIdMSB(user.getId().getId().getMostSignificantBits()) - .setIdLSB(user.getId().getId().getLeastSignificantBits()).build(); - } - - private UserUpdateMsg constructDeprecatedUserUpdatedMsg(UpdateMsgType msgType, User user) { + @Override + public UserUpdateMsg constructUserUpdatedMsg(UpdateMsgType msgType, User user) { UserUpdateMsg.Builder builder = UserUpdateMsg.newBuilder() .setMsgType(msgType) .setIdMSB(user.getId().getId().getMostSignificantBits()) @@ -63,25 +52,12 @@ public class UserMsgConstructor { return builder.build(); } - public UserUpdateMsg constructUserDeleteMsg(UserId userId) { - return UserUpdateMsg.newBuilder() - .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) - .setIdMSB(userId.getId().getMostSignificantBits()) - .setIdLSB(userId.getId().getLeastSignificantBits()).build(); - } - - public UserCredentialsUpdateMsg constructUserCredentialsUpdatedMsg(UserCredentials userCredentials, EdgeVersion edgeVersion) { - return EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion) - ? constructDeprecatedUserCredentialsUpdatedMsg(userCredentials) - : UserCredentialsUpdateMsg.newBuilder().setEntity(JacksonUtil.toString(userCredentials)).build(); - } - - private UserCredentialsUpdateMsg constructDeprecatedUserCredentialsUpdatedMsg(UserCredentials userCredentials) { - UserCredentialsUpdateMsg.Builder builder = UserCredentialsUpdateMsg.newBuilder() + @Override + public UserCredentialsUpdateMsg constructUserCredentialsUpdatedMsg(UserCredentials userCredentials) { + return UserCredentialsUpdateMsg.newBuilder() .setUserIdMSB(userCredentials.getUserId().getId().getMostSignificantBits()) .setUserIdLSB(userCredentials.getUserId().getId().getLeastSignificantBits()) .setEnabled(userCredentials.isEnabled()) - .setPassword(userCredentials.getPassword()); - return builder.build(); + .setPassword(userCredentials.getPassword()).build(); } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/UserMsgConstructorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/UserMsgConstructorV2.java new file mode 100644 index 0000000000..0b4c592712 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/UserMsgConstructorV2.java @@ -0,0 +1,42 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.user; + +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.security.UserCredentials; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.gen.edge.v1.UserCredentialsUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UserUpdateMsg; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class UserMsgConstructorV2 extends BaseUserMsgConstructor { + + @Override + public UserUpdateMsg constructUserUpdatedMsg(UpdateMsgType msgType, User user) { + return UserUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(user)) + .setIdMSB(user.getId().getId().getMostSignificantBits()) + .setIdLSB(user.getId().getId().getLeastSignificantBits()).build(); + } + + @Override + public UserCredentialsUpdateMsg constructUserCredentialsUpdatedMsg(UserCredentials userCredentials) { + return UserCredentialsUpdateMsg.newBuilder().setEntity(JacksonUtil.toString(userCredentials)).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/BaseWidgetMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/BaseWidgetMsgConstructor.java new file mode 100644 index 0000000000..6b4b3ac821 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/BaseWidgetMsgConstructor.java @@ -0,0 +1,43 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.widget; + +import org.thingsboard.server.common.data.id.WidgetTypeId; +import org.thingsboard.server.common.data.id.WidgetsBundleId; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.gen.edge.v1.WidgetTypeUpdateMsg; +import org.thingsboard.server.gen.edge.v1.WidgetsBundleUpdateMsg; + +public abstract class BaseWidgetMsgConstructor implements WidgetMsgConstructor { + + @Override + public WidgetsBundleUpdateMsg constructWidgetsBundleDeleteMsg(WidgetsBundleId widgetsBundleId) { + return WidgetsBundleUpdateMsg.newBuilder() + .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) + .setIdMSB(widgetsBundleId.getId().getMostSignificantBits()) + .setIdLSB(widgetsBundleId.getId().getLeastSignificantBits()) + .build(); + } + + @Override + public WidgetTypeUpdateMsg constructWidgetTypeDeleteMsg(WidgetTypeId widgetTypeId) { + return WidgetTypeUpdateMsg.newBuilder() + .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) + .setIdMSB(widgetTypeId.getId().getMostSignificantBits()) + .setIdLSB(widgetTypeId.getId().getLeastSignificantBits()) + .build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/WidgetMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/WidgetMsgConstructor.java new file mode 100644 index 0000000000..5d2366eef5 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/WidgetMsgConstructor.java @@ -0,0 +1,38 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.widget; + +import org.thingsboard.server.common.data.id.WidgetTypeId; +import org.thingsboard.server.common.data.id.WidgetsBundleId; +import org.thingsboard.server.common.data.widget.WidgetTypeDetails; +import org.thingsboard.server.common.data.widget.WidgetsBundle; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.gen.edge.v1.WidgetTypeUpdateMsg; +import org.thingsboard.server.gen.edge.v1.WidgetsBundleUpdateMsg; + +import java.util.List; + +public interface WidgetMsgConstructor { + + WidgetsBundleUpdateMsg constructWidgetsBundleUpdateMsg(UpdateMsgType msgType, WidgetsBundle widgetsBundle, List widgets); + + WidgetsBundleUpdateMsg constructWidgetsBundleDeleteMsg(WidgetsBundleId widgetsBundleId); + + WidgetTypeUpdateMsg constructWidgetTypeUpdateMsg(UpdateMsgType msgType, WidgetTypeDetails widgetTypeDetails, EdgeVersion edgeVersion); + + WidgetTypeUpdateMsg constructWidgetTypeDeleteMsg(WidgetTypeId widgetTypeId); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/WidgetMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/WidgetMsgConstructorFactory.java new file mode 100644 index 0000000000..ce1ae735f3 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/WidgetMsgConstructorFactory.java @@ -0,0 +1,45 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.widget; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class WidgetMsgConstructorFactory { + + @Autowired + protected WidgetMsgConstructorV1 widgetMsgConstructorV1; + + @Autowired + protected WidgetMsgConstructorV2 widgetMsgConstructorV2; + + public WidgetMsgConstructor getWidgetMsgConstructor(EdgeVersion edgeVersion) { + switch (edgeVersion) { + case V_3_3_0: + case V_3_3_3: + case V_3_4_0: + case V_3_6_0: + case V_3_6_1: + return widgetMsgConstructorV1; + default: + return widgetMsgConstructorV2; + } + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetTypeMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/WidgetMsgConstructorV1.java similarity index 67% rename from application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetTypeMsgConstructor.java rename to application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/WidgetMsgConstructorV1.java index d4e5810c04..67bdbf1f54 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetTypeMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/WidgetMsgConstructorV1.java @@ -13,35 +13,55 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.edge.rpc.constructor; +package org.thingsboard.server.service.edge.rpc.constructor.widget; +import com.google.protobuf.ByteString; import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.id.WidgetTypeId; import org.thingsboard.server.common.data.widget.WidgetTypeDetails; +import org.thingsboard.server.common.data.widget.WidgetsBundle; import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.WidgetTypeUpdateMsg; +import org.thingsboard.server.gen.edge.v1.WidgetsBundleUpdateMsg; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; +import java.nio.charset.StandardCharsets; import java.util.Arrays; +import java.util.List; @Component @TbCoreComponent -public class WidgetTypeMsgConstructor { +public class WidgetMsgConstructorV1 extends BaseWidgetMsgConstructor { - public WidgetTypeUpdateMsg constructWidgetTypeUpdateMsg(UpdateMsgType msgType, WidgetTypeDetails widgetTypeDetails, EdgeVersion edgeVersion) { - if (EdgeVersionUtils.isEdgeVersionOlderThan_3_6_2(edgeVersion)) { - return constructDeprecatedWidgetTypeUpdateMsg(msgType, widgetTypeDetails, edgeVersion); + @Override + public WidgetsBundleUpdateMsg constructWidgetsBundleUpdateMsg(UpdateMsgType msgType, WidgetsBundle widgetsBundle, List widgets) { + WidgetsBundleUpdateMsg.Builder builder = WidgetsBundleUpdateMsg.newBuilder() + .setMsgType(msgType) + .setIdMSB(widgetsBundle.getId().getId().getMostSignificantBits()) + .setIdLSB(widgetsBundle.getId().getId().getLeastSignificantBits()) + .setTitle(widgetsBundle.getTitle()) + .setAlias(widgetsBundle.getAlias()); + if (widgetsBundle.getImage() != null) { + builder.setImage(ByteString.copyFrom(widgetsBundle.getImage().getBytes(StandardCharsets.UTF_8))); } - return WidgetTypeUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(widgetTypeDetails)) - .setIdMSB(widgetTypeDetails.getId().getId().getMostSignificantBits()) - .setIdLSB(widgetTypeDetails.getId().getId().getLeastSignificantBits()).build(); + if (widgetsBundle.getDescription() != null) { + builder.setDescription(widgetsBundle.getDescription()); + } + if (widgetsBundle.getOrder() != null) { + builder.setOrder(widgetsBundle.getOrder()); + } + if (widgetsBundle.getTenantId().equals(TenantId.SYS_TENANT_ID)) { + builder.setIsSystem(true); + } + builder.setWidgets(JacksonUtil.toString(widgets)); + return builder.build(); } - private WidgetTypeUpdateMsg constructDeprecatedWidgetTypeUpdateMsg(UpdateMsgType msgType, WidgetTypeDetails widgetTypeDetails, EdgeVersion edgeVersion) { + @Override + public WidgetTypeUpdateMsg constructWidgetTypeUpdateMsg(UpdateMsgType msgType, WidgetTypeDetails widgetTypeDetails, EdgeVersion edgeVersion) { WidgetTypeUpdateMsg.Builder builder = WidgetTypeUpdateMsg.newBuilder() .setMsgType(msgType) .setIdMSB(widgetTypeDetails.getId().getId().getMostSignificantBits()) @@ -82,12 +102,4 @@ public class WidgetTypeMsgConstructor { } return builder.build(); } - - public WidgetTypeUpdateMsg constructWidgetTypeDeleteMsg(WidgetTypeId widgetTypeId) { - return WidgetTypeUpdateMsg.newBuilder() - .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) - .setIdMSB(widgetTypeId.getId().getMostSignificantBits()) - .setIdLSB(widgetTypeId.getId().getLeastSignificantBits()) - .build(); - } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/WidgetMsgConstructorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/WidgetMsgConstructorV2.java new file mode 100644 index 0000000000..aacc3c8399 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/WidgetMsgConstructorV2.java @@ -0,0 +1,48 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor.widget; + +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.widget.WidgetTypeDetails; +import org.thingsboard.server.common.data.widget.WidgetsBundle; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.gen.edge.v1.WidgetTypeUpdateMsg; +import org.thingsboard.server.gen.edge.v1.WidgetsBundleUpdateMsg; +import org.thingsboard.server.queue.util.TbCoreComponent; + +import java.util.List; + +@Component +@TbCoreComponent +public class WidgetMsgConstructorV2 extends BaseWidgetMsgConstructor { + + @Override + public WidgetsBundleUpdateMsg constructWidgetsBundleUpdateMsg(UpdateMsgType msgType, WidgetsBundle widgetsBundle, List widgets) { + return WidgetsBundleUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(widgetsBundle)) + .setWidgets(JacksonUtil.toString(widgets)) + .setIdMSB(widgetsBundle.getId().getId().getMostSignificantBits()) + .setIdLSB(widgetsBundle.getId().getId().getLeastSignificantBits()).build(); + } + + @Override + public WidgetTypeUpdateMsg constructWidgetTypeUpdateMsg(UpdateMsgType msgType, WidgetTypeDetails widgetTypeDetails, EdgeVersion edgeVersion) { + return WidgetTypeUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(widgetTypeDetails)) + .setIdMSB(widgetTypeDetails.getId().getId().getMostSignificantBits()) + .setIdLSB(widgetTypeDetails.getId().getId().getLeastSignificantBits()).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java index 8790b5d1fd..fe5fc76f2c 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java @@ -90,27 +90,23 @@ import org.thingsboard.server.queue.TbQueueCallback; import org.thingsboard.server.queue.TbQueueMsgMetadata; import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.provider.TbQueueProducerProvider; -import org.thingsboard.server.service.edge.rpc.constructor.AdminSettingsMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.AlarmMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.AssetMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.AssetProfileMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.CustomerMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.DashboardMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.DeviceMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.DeviceProfileMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.EdgeMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.EntityDataMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.EntityViewMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.OtaPackageMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.QueueMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.RelationMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.RuleChainMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.ResourceMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.TenantMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.TenantProfileMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.UserMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.WidgetTypeMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.WidgetsBundleMsgConstructor; +import org.thingsboard.server.service.edge.rpc.constructor.alarm.AlarmMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.asset.AssetMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.customer.CustomerMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.dashboard.DashboardMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.device.DeviceMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.edge.EdgeMsgConstructor; +import org.thingsboard.server.service.edge.rpc.constructor.entityview.EntityViewMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.ota.OtaPackageMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.queue.QueueMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.relation.RelationMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.resource.ResourceMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.rule.RuleChainMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.settings.AdminSettingsMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.telemetry.EntityDataMsgConstructor; +import org.thingsboard.server.service.edge.rpc.constructor.tenant.TenantMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.user.UserMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.widget.WidgetMsgConstructorFactory; import org.thingsboard.server.service.entitiy.TbNotificationEntityService; import org.thingsboard.server.service.executors.DbCallbackExecutorService; import org.thingsboard.server.service.profile.TbAssetProfileCache; @@ -253,61 +249,49 @@ public abstract class BaseEdgeProcessor { protected EntityDataMsgConstructor entityDataMsgConstructor; @Autowired - protected RuleChainMsgConstructor ruleChainMsgConstructor; + protected RuleChainMsgConstructorFactory ruleChainMsgConstructorFactory; @Autowired - protected AlarmMsgConstructor alarmMsgConstructor; + protected AlarmMsgConstructorFactory alarmMsgConstructorFactory; @Autowired - protected DeviceMsgConstructor deviceMsgConstructor; + protected DeviceMsgConstructorFactory deviceMsgConstructorFactory; @Autowired - protected AssetMsgConstructor assetMsgConstructor; + protected AssetMsgConstructorFactory assetMsgConstructorFactory; @Autowired - protected EntityViewMsgConstructor entityViewMsgConstructor; + protected EntityViewMsgConstructorFactory entityViewMsgConstructorFactory; @Autowired - protected DashboardMsgConstructor dashboardMsgConstructor; + protected DashboardMsgConstructorFactory dashboardMsgConstructorFactory; @Autowired - protected RelationMsgConstructor relationMsgConstructor; + protected RelationMsgConstructorFactory relationMsgConstructorFactory; @Autowired - protected UserMsgConstructor userMsgConstructor; + protected UserMsgConstructorFactory userMsgConstructorFactory; @Autowired - protected CustomerMsgConstructor customerMsgConstructor; + protected CustomerMsgConstructorFactory customerMsgConstructorFactory; @Autowired - protected DeviceProfileMsgConstructor deviceProfileMsgConstructor; + protected TenantMsgConstructorFactory tenantMsgConstructorFactory; @Autowired - protected AssetProfileMsgConstructor assetProfileMsgConstructor; + protected WidgetMsgConstructorFactory widgetMsgConstructorFactory; @Autowired - protected TenantMsgConstructor tenantMsgConstructor; + protected AdminSettingsMsgConstructorFactory adminSettingsMsgConstructorFactory; @Autowired - protected TenantProfileMsgConstructor tenantProfileMsgConstructor; + protected OtaPackageMsgConstructorFactory otaPackageMsgConstructorFactory; @Autowired - protected WidgetsBundleMsgConstructor widgetsBundleMsgConstructor; + protected QueueMsgConstructorFactory queueMsgConstructorFactory; @Autowired - protected WidgetTypeMsgConstructor widgetTypeMsgConstructor; - - @Autowired - protected AdminSettingsMsgConstructor adminSettingsMsgConstructor; - - @Autowired - protected OtaPackageMsgConstructor otaPackageMsgConstructor; - - @Autowired - protected QueueMsgConstructor queueMsgConstructor; - - @Autowired - protected ResourceMsgConstructor resourceMsgConstructor; + protected ResourceMsgConstructorFactory resourceMsgConstructorFactory; @Autowired protected EdgeSynchronizationManager edgeSynchronizationManager; diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java index fd53e38f4a..edc03469e0 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java @@ -132,13 +132,13 @@ public abstract class BaseAlarmProcessor extends BaseEdgeProcessor { case ALARM_CLEAR: Alarm alarm = alarmService.findAlarmById(tenantId, alarmId); if (alarm != null) { - return alarmMsgConstructor.constructAlarmUpdatedMsg(msgType, alarm, findOriginatorEntityName(tenantId, alarm), edgeVersion); + return alarmMsgConstructorFactory.getAlarmMsgConstructor(edgeVersion).constructAlarmUpdatedMsg(msgType, alarm, findOriginatorEntityName(tenantId, alarm)); } break; case DELETED: Alarm deletedAlarm = JacksonUtil.convertValue(body, Alarm.class); if (deletedAlarm != null) { - return alarmMsgConstructor.constructAlarmUpdatedMsg(msgType, deletedAlarm, findOriginatorEntityName(tenantId, deletedAlarm), edgeVersion); + return alarmMsgConstructorFactory.getAlarmMsgConstructor(edgeVersion).constructAlarmUpdatedMsg(msgType, deletedAlarm, findOriginatorEntityName(tenantId, deletedAlarm)); } } return null; diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessor.java index 886a148f5a..fb1362a9da 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessor.java @@ -121,14 +121,14 @@ public class AssetEdgeProcessor extends BaseAssetProcessor { if (asset != null && !BaseAssetService.TB_SERVICE_QUEUE.equals(asset.getType())) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); AssetUpdateMsg assetUpdateMsg = - assetMsgConstructor.constructAssetUpdatedMsg(msgType, asset, edgeVersion); + assetMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructAssetUpdatedMsg(msgType, asset); DownlinkMsg.Builder builder = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addAssetUpdateMsg(assetUpdateMsg); if (UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE.equals(msgType)) { AssetProfile assetProfile = assetProfileService.findAssetProfileById(edgeEvent.getTenantId(), asset.getAssetProfileId()); assetProfile = checkIfAssetProfileDefaultFieldsAssignedToEdge(edgeEvent.getTenantId(), edgeId, assetProfile, edgeVersion); - builder.addAssetProfileUpdateMsg(assetProfileMsgConstructor.constructAssetProfileUpdatedMsg(msgType, assetProfile, edgeVersion)); + builder.addAssetProfileUpdateMsg(assetMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructAssetProfileUpdatedMsg(msgType, assetProfile)); } downlinkMsg = builder.build(); } @@ -136,7 +136,7 @@ public class AssetEdgeProcessor extends BaseAssetProcessor { case DELETED: case UNASSIGNED_FROM_EDGE: AssetUpdateMsg assetUpdateMsg = - assetMsgConstructor.constructAssetDeleteMsg(assetId); + assetMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructAssetDeleteMsg(assetId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addAssetUpdateMsg(assetUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetProfileEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetProfileEdgeProcessor.java index d5a98b523e..76c43b0c01 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetProfileEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetProfileEdgeProcessor.java @@ -108,7 +108,7 @@ public class AssetProfileEdgeProcessor extends BaseAssetProfileProcessor { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); assetProfile = checkIfAssetProfileDefaultFieldsAssignedToEdge(edgeEvent.getTenantId(), edgeId, assetProfile, edgeVersion); AssetProfileUpdateMsg assetProfileUpdateMsg = - assetProfileMsgConstructor.constructAssetProfileUpdatedMsg(msgType, assetProfile, edgeVersion); + assetMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructAssetProfileUpdatedMsg(msgType, assetProfile); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addAssetProfileUpdateMsg(assetProfileUpdateMsg) @@ -117,7 +117,7 @@ public class AssetProfileEdgeProcessor extends BaseAssetProfileProcessor { break; case DELETED: AssetProfileUpdateMsg assetProfileUpdateMsg = - assetProfileMsgConstructor.constructAssetProfileDeleteMsg(assetProfileId); + assetMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructAssetProfileDeleteMsg(assetProfileId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addAssetProfileUpdateMsg(assetProfileUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/customer/CustomerEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/customer/CustomerEdgeProcessor.java index 04d786022f..2ece4edbb5 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/customer/CustomerEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/customer/CustomerEdgeProcessor.java @@ -58,7 +58,7 @@ public class CustomerEdgeProcessor extends BaseEdgeProcessor { if (customer != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); CustomerUpdateMsg customerUpdateMsg = - customerMsgConstructor.constructCustomerUpdatedMsg(msgType, customer, edgeVersion); + customerMsgConstructorFactory.getCustomerMsgConstructor(edgeVersion).constructCustomerUpdatedMsg(msgType, customer); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addCustomerUpdateMsg(customerUpdateMsg) @@ -67,7 +67,7 @@ public class CustomerEdgeProcessor extends BaseEdgeProcessor { break; case DELETED: CustomerUpdateMsg customerUpdateMsg = - customerMsgConstructor.constructCustomerDeleteMsg(customerId); + customerMsgConstructorFactory.getCustomerMsgConstructor(edgeVersion).constructCustomerDeleteMsg(customerId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addCustomerUpdateMsg(customerUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardEdgeProcessor.java index eec398e5ff..5c3c921cde 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardEdgeProcessor.java @@ -111,7 +111,7 @@ public class DashboardEdgeProcessor extends BaseDashboardProcessor { if (dashboard != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); DashboardUpdateMsg dashboardUpdateMsg = - dashboardMsgConstructor.constructDashboardUpdatedMsg(msgType, dashboard, edgeVersion); + dashboardMsgConstructorFactory.getDashboardMsgConstructor(edgeVersion).constructDashboardUpdatedMsg(msgType, dashboard); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addDashboardUpdateMsg(dashboardUpdateMsg) @@ -121,7 +121,7 @@ public class DashboardEdgeProcessor extends BaseDashboardProcessor { case DELETED: case UNASSIGNED_FROM_EDGE: DashboardUpdateMsg dashboardUpdateMsg = - dashboardMsgConstructor.constructDashboardDeleteMsg(dashboardId); + dashboardMsgConstructorFactory.getDashboardMsgConstructor(edgeVersion).constructDashboardDeleteMsg(dashboardId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addDashboardUpdateMsg(dashboardUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java index 97eb6bcfa1..2677004678 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java @@ -228,14 +228,14 @@ public class DeviceEdgeProcessor extends BaseDeviceProcessor { if (device != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); DeviceUpdateMsg deviceUpdateMsg = - deviceMsgConstructor.constructDeviceUpdatedMsg(msgType, device, edgeVersion); + deviceMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructDeviceUpdatedMsg(msgType, device); DownlinkMsg.Builder builder = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addDeviceUpdateMsg(deviceUpdateMsg); if (UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE.equals(msgType)) { DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileById(edgeEvent.getTenantId(), device.getDeviceProfileId()); deviceProfile = checkIfDeviceProfileDefaultFieldsAssignedToEdge(edgeEvent.getTenantId(), edgeId, deviceProfile, edgeVersion); - builder.addDeviceProfileUpdateMsg(deviceProfileMsgConstructor.constructDeviceProfileUpdatedMsg(msgType, deviceProfile, edgeVersion)); + builder.addDeviceProfileUpdateMsg(deviceMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructDeviceProfileUpdatedMsg(msgType, deviceProfile)); } downlinkMsg = builder.build(); } @@ -243,7 +243,7 @@ public class DeviceEdgeProcessor extends BaseDeviceProcessor { case DELETED: case UNASSIGNED_FROM_EDGE: DeviceUpdateMsg deviceUpdateMsg = - deviceMsgConstructor.constructDeviceDeleteMsg(deviceId); + deviceMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructDeviceDeleteMsg(deviceId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addDeviceUpdateMsg(deviceUpdateMsg) @@ -253,7 +253,7 @@ public class DeviceEdgeProcessor extends BaseDeviceProcessor { DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(edgeEvent.getTenantId(), deviceId); if (deviceCredentials != null) { DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg = - deviceMsgConstructor.constructDeviceCredentialsUpdatedMsg(deviceCredentials, edgeVersion); + deviceMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructDeviceCredentialsUpdatedMsg(deviceCredentials); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addDeviceCredentialsUpdateMsg(deviceCredentialsUpdateMsg) @@ -261,20 +261,16 @@ public class DeviceEdgeProcessor extends BaseDeviceProcessor { } break; case RPC_CALL: - return convertRpcCallEventToDownlink(edgeEvent); + return DownlinkMsg.newBuilder() + .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) + .addDeviceRpcCallMsg(deviceMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructDeviceRpcCallMsg(edgeEvent.getEntityId(), edgeEvent.getBody())) + .build(); case CREDENTIALS_REQUEST: return convertCredentialsRequestEventToDownlink(edgeEvent); } return downlinkMsg; } - private DownlinkMsg convertRpcCallEventToDownlink(EdgeEvent edgeEvent) { - return DownlinkMsg.newBuilder() - .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) - .addDeviceRpcCallMsg(deviceMsgConstructor.constructDeviceRpcCallMsg(edgeEvent.getEntityId(), edgeEvent.getBody())) - .build(); - } - private DownlinkMsg convertCredentialsRequestEventToDownlink(EdgeEvent edgeEvent) { DeviceId deviceId = new DeviceId(edgeEvent.getEntityId()); DeviceCredentialsRequestMsg deviceCredentialsRequestMsg = DeviceCredentialsRequestMsg.newBuilder() diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceProfileEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceProfileEdgeProcessor.java index 4f43fea35b..0b8fa1025a 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceProfileEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceProfileEdgeProcessor.java @@ -108,7 +108,7 @@ public class DeviceProfileEdgeProcessor extends BaseDeviceProfileProcessor { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); deviceProfile = checkIfDeviceProfileDefaultFieldsAssignedToEdge(edgeEvent.getTenantId(), edgeId, deviceProfile, edgeVersion); DeviceProfileUpdateMsg deviceProfileUpdateMsg = - deviceProfileMsgConstructor.constructDeviceProfileUpdatedMsg(msgType, deviceProfile, edgeVersion); + deviceMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructDeviceProfileUpdatedMsg(msgType, deviceProfile); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addDeviceProfileUpdateMsg(deviceProfileUpdateMsg) @@ -117,7 +117,7 @@ public class DeviceProfileEdgeProcessor extends BaseDeviceProfileProcessor { break; case DELETED: DeviceProfileUpdateMsg deviceProfileUpdateMsg = - deviceProfileMsgConstructor.constructDeviceProfileDeleteMsg(deviceProfileId); + deviceMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructDeviceProfileDeleteMsg(deviceProfileId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addDeviceProfileUpdateMsg(deviceProfileUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewEdgeProcessor.java index 73f66fc614..50eb258b7d 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewEdgeProcessor.java @@ -118,7 +118,7 @@ public class EntityViewEdgeProcessor extends BaseEntityViewProcessor { if (entityView != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); EntityViewUpdateMsg entityViewUpdateMsg = - entityViewMsgConstructor.constructEntityViewUpdatedMsg(msgType, entityView, edgeVersion); + entityViewMsgConstructorFactory.getEntityViewMsgConstructor(edgeVersion).constructEntityViewUpdatedMsg(msgType, entityView); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addEntityViewUpdateMsg(entityViewUpdateMsg) @@ -128,7 +128,7 @@ public class EntityViewEdgeProcessor extends BaseEntityViewProcessor { case DELETED: case UNASSIGNED_FROM_EDGE: EntityViewUpdateMsg entityViewUpdateMsg = - entityViewMsgConstructor.constructEntityViewDeleteMsg(entityViewId); + entityViewMsgConstructorFactory.getEntityViewMsgConstructor(edgeVersion).constructEntityViewDeleteMsg(entityViewId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addEntityViewUpdateMsg(entityViewUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/ota/OtaPackageEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/ota/OtaPackageEdgeProcessor.java index 3c710fd3ee..dd94cc8803 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/ota/OtaPackageEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/ota/OtaPackageEdgeProcessor.java @@ -43,7 +43,7 @@ public class OtaPackageEdgeProcessor extends BaseEdgeProcessor { if (otaPackage != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); OtaPackageUpdateMsg otaPackageUpdateMsg = - otaPackageMsgConstructor.constructOtaPackageUpdatedMsg(msgType, otaPackage, edgeVersion); + otaPackageMsgConstructorFactory.getOtaPackageMsgConstructor(edgeVersion).constructOtaPackageUpdatedMsg(msgType, otaPackage); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addOtaPackageUpdateMsg(otaPackageUpdateMsg) @@ -52,7 +52,7 @@ public class OtaPackageEdgeProcessor extends BaseEdgeProcessor { break; case DELETED: OtaPackageUpdateMsg otaPackageUpdateMsg = - otaPackageMsgConstructor.constructOtaPackageDeleteMsg(otaPackageId); + otaPackageMsgConstructorFactory.getOtaPackageMsgConstructor(edgeVersion).constructOtaPackageDeleteMsg(otaPackageId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addOtaPackageUpdateMsg(otaPackageUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/queue/QueueEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/queue/QueueEdgeProcessor.java index ce7bd33f9b..3836cf3c65 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/queue/QueueEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/queue/QueueEdgeProcessor.java @@ -28,8 +28,8 @@ import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; -@Component @Slf4j +@Component @TbCoreComponent public class QueueEdgeProcessor extends BaseEdgeProcessor { @@ -43,7 +43,7 @@ public class QueueEdgeProcessor extends BaseEdgeProcessor { if (queue != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); QueueUpdateMsg queueUpdateMsg = - queueMsgConstructor.constructQueueUpdatedMsg(msgType, queue, edgeVersion); + queueMsgConstructorFactory.getQueueMsgConstructor(edgeVersion).constructQueueUpdatedMsg(msgType, queue); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addQueueUpdateMsg(queueUpdateMsg) @@ -52,7 +52,7 @@ public class QueueEdgeProcessor extends BaseEdgeProcessor { break; case DELETED: QueueUpdateMsg queueDeleteMsg = - queueMsgConstructor.constructQueueDeleteMsg(queueId); + queueMsgConstructorFactory.getQueueMsgConstructor(edgeVersion).constructQueueDeleteMsg(queueId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addQueueUpdateMsg(queueDeleteMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessor.java index 566bed5146..3adf19548f 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessor.java @@ -59,7 +59,7 @@ public class RelationEdgeProcessor extends BaseRelationProcessor { public DownlinkMsg convertRelationEventToDownlink(EdgeEvent edgeEvent, EdgeVersion edgeVersion) { EntityRelation entityRelation = JacksonUtil.convertValue(edgeEvent.getBody(), EntityRelation.class); UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); - RelationUpdateMsg relationUpdateMsg = relationMsgConstructor.constructRelationUpdatedMsg(msgType, entityRelation, edgeVersion); + RelationUpdateMsg relationUpdateMsg = relationMsgConstructorFactory.getRelationMsgConstructor(edgeVersion).constructRelationUpdatedMsg(msgType, entityRelation); return DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addRelationUpdateMsg(relationUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessor.java index ae514d0a54..89ae103b2b 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessor.java @@ -81,7 +81,7 @@ public class ResourceEdgeProcessor extends BaseResourceProcessor { if (tbResource != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); ResourceUpdateMsg resourceUpdateMsg = - resourceMsgConstructor.constructResourceUpdatedMsg(msgType, tbResource, edgeVersion); + resourceMsgConstructorFactory.getResourceMsgConstructor(edgeVersion).constructResourceUpdatedMsg(msgType, tbResource); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addResourceUpdateMsg(resourceUpdateMsg) @@ -90,7 +90,7 @@ public class ResourceEdgeProcessor extends BaseResourceProcessor { break; case DELETED: ResourceUpdateMsg resourceUpdateMsg = - resourceMsgConstructor.constructResourceDeleteMsg(tbResourceId); + resourceMsgConstructorFactory.getResourceMsgConstructor(edgeVersion).constructResourceDeleteMsg(tbResourceId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addResourceUpdateMsg(resourceUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/rule/RuleChainEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/rule/RuleChainEdgeProcessor.java index d480f9e4cd..910e7e0541 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/rule/RuleChainEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/rule/RuleChainEdgeProcessor.java @@ -54,7 +54,7 @@ public class RuleChainEdgeProcessor extends BaseEdgeProcessor { } UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); RuleChainUpdateMsg ruleChainUpdateMsg = - ruleChainMsgConstructor.constructRuleChainUpdatedMsg(msgType, ruleChain, isRoot, edgeVersion); + ruleChainMsgConstructorFactory.getRuleChainMsgConstructor(edgeVersion).constructRuleChainUpdatedMsg(msgType, ruleChain, isRoot); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addRuleChainUpdateMsg(ruleChainUpdateMsg) @@ -65,7 +65,7 @@ public class RuleChainEdgeProcessor extends BaseEdgeProcessor { case UNASSIGNED_FROM_EDGE: downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) - .addRuleChainUpdateMsg(ruleChainMsgConstructor.constructRuleChainDeleteMsg(ruleChainId)) + .addRuleChainUpdateMsg(ruleChainMsgConstructorFactory.getRuleChainMsgConstructor(edgeVersion).constructRuleChainDeleteMsg(ruleChainId)) .build(); break; } @@ -80,7 +80,7 @@ public class RuleChainEdgeProcessor extends BaseEdgeProcessor { RuleChainMetaData ruleChainMetaData = ruleChainService.loadRuleChainMetaData(edgeEvent.getTenantId(), ruleChainId); UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = - ruleChainMsgConstructor.constructRuleChainMetadataUpdatedMsg(edgeEvent.getTenantId(), msgType, ruleChainMetaData, edgeVersion); + ruleChainMsgConstructorFactory.getRuleChainMsgConstructor(edgeVersion).constructRuleChainMetadataUpdatedMsg(edgeEvent.getTenantId(), msgType, ruleChainMetaData, edgeVersion); if (ruleChainMetadataUpdateMsg != null) { downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/settings/AdminSettingsEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/settings/AdminSettingsEdgeProcessor.java index 735c4de399..0b7f3021f3 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/settings/AdminSettingsEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/settings/AdminSettingsEdgeProcessor.java @@ -37,7 +37,8 @@ public class AdminSettingsEdgeProcessor extends BaseEdgeProcessor { if (adminSettings == null) { return null; } - AdminSettingsUpdateMsg adminSettingsUpdateMsg = adminSettingsMsgConstructor.constructAdminSettingsUpdateMsg(adminSettings, edgeVersion); + AdminSettingsUpdateMsg adminSettingsUpdateMsg = + adminSettingsMsgConstructorFactory.getAdminSettingsMsgConstructor(edgeVersion).constructAdminSettingsUpdateMsg(adminSettings); return DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addAdminSettingsUpdateMsg(adminSettingsUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantEdgeProcessor.java index f4b0c5a355..1ef44a72ad 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantEdgeProcessor.java @@ -43,9 +43,9 @@ public class TenantEdgeProcessor extends BaseEdgeProcessor { Tenant tenant = tenantService.findTenantById(tenantId); if (tenant != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); - TenantUpdateMsg tenantUpdateMsg = tenantMsgConstructor.constructTenantUpdateMsg(msgType, tenant, edgeVersion); + TenantUpdateMsg tenantUpdateMsg = tenantMsgConstructorFactory.getTenantMsgConstructor(edgeVersion).constructTenantUpdateMsg(msgType, tenant); TenantProfile tenantProfile = tenantProfileService.findTenantProfileById(tenantId, tenant.getTenantProfileId()); - TenantProfileUpdateMsg tenantProfileUpdateMsg = tenantProfileMsgConstructor.constructTenantProfileUpdateMsg(msgType, tenantProfile, edgeVersion); + TenantProfileUpdateMsg tenantProfileUpdateMsg = tenantMsgConstructorFactory.getTenantMsgConstructor(edgeVersion).constructTenantProfileUpdateMsg(msgType, tenantProfile, edgeVersion); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addTenantUpdateMsg(tenantUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantProfileEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantProfileEdgeProcessor.java index 970ea3a1f6..4f264f7bb4 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantProfileEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantProfileEdgeProcessor.java @@ -42,7 +42,7 @@ public class TenantProfileEdgeProcessor extends BaseEdgeProcessor { if (tenantProfile != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); TenantProfileUpdateMsg tenantProfileUpdateMsg = - tenantProfileMsgConstructor.constructTenantProfileUpdateMsg(msgType, tenantProfile, edgeVersion); + tenantMsgConstructorFactory.getTenantMsgConstructor(edgeVersion).constructTenantProfileUpdateMsg(msgType, tenantProfile, edgeVersion); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addTenantProfileUpdateMsg(tenantProfileUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/user/UserEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/user/UserEdgeProcessor.java index 8d27c5fc79..3ee3fc109f 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/user/UserEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/user/UserEdgeProcessor.java @@ -45,21 +45,21 @@ public class UserEdgeProcessor extends BaseEdgeProcessor { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) - .addUserUpdateMsg(userMsgConstructor.constructUserUpdatedMsg(msgType, user, edgeVersion)) + .addUserUpdateMsg(userMsgConstructorFactory.getUserMsgConstructor(edgeVersion).constructUserUpdatedMsg(msgType, user)) .build(); } break; case DELETED: downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) - .addUserUpdateMsg(userMsgConstructor.constructUserDeleteMsg(userId)) + .addUserUpdateMsg(userMsgConstructorFactory.getUserMsgConstructor(edgeVersion).constructUserDeleteMsg(userId)) .build(); break; case CREDENTIALS_UPDATED: UserCredentials userCredentialsByUserId = userService.findUserCredentialsByUserId(edgeEvent.getTenantId(), userId); if (userCredentialsByUserId != null && userCredentialsByUserId.isEnabled()) { UserCredentialsUpdateMsg userCredentialsUpdateMsg = - userMsgConstructor.constructUserCredentialsUpdatedMsg(userCredentialsByUserId, edgeVersion); + userMsgConstructorFactory.getUserMsgConstructor(edgeVersion).constructUserCredentialsUpdatedMsg(userCredentialsByUserId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addUserCredentialsUpdateMsg(userCredentialsUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetBundleEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetBundleEdgeProcessor.java index abef3a1ab5..e48387fc64 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetBundleEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetBundleEdgeProcessor.java @@ -46,7 +46,7 @@ public class WidgetBundleEdgeProcessor extends BaseEdgeProcessor { List widgets = widgetTypeService.findWidgetFqnsByWidgetsBundleId(edgeEvent.getTenantId(), widgetsBundleId); UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); WidgetsBundleUpdateMsg widgetsBundleUpdateMsg = - widgetsBundleMsgConstructor.constructWidgetsBundleUpdateMsg(msgType, widgetsBundle, widgets, edgeVersion); + widgetMsgConstructorFactory.getWidgetMsgConstructor(edgeVersion).constructWidgetsBundleUpdateMsg(msgType, widgetsBundle, widgets); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addWidgetsBundleUpdateMsg(widgetsBundleUpdateMsg) @@ -55,7 +55,7 @@ public class WidgetBundleEdgeProcessor extends BaseEdgeProcessor { break; case DELETED: WidgetsBundleUpdateMsg widgetsBundleUpdateMsg = - widgetsBundleMsgConstructor.constructWidgetsBundleDeleteMsg(widgetsBundleId); + widgetMsgConstructorFactory.getWidgetMsgConstructor(edgeVersion).constructWidgetsBundleDeleteMsg(widgetsBundleId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addWidgetsBundleUpdateMsg(widgetsBundleUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetTypeEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetTypeEdgeProcessor.java index de9617d5d7..8125235ae6 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetTypeEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetTypeEdgeProcessor.java @@ -28,8 +28,8 @@ import org.thingsboard.server.gen.edge.v1.WidgetTypeUpdateMsg; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; -@Component @Slf4j +@Component @TbCoreComponent public class WidgetTypeEdgeProcessor extends BaseEdgeProcessor { @@ -43,7 +43,7 @@ public class WidgetTypeEdgeProcessor extends BaseEdgeProcessor { if (widgetTypeDetails != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); WidgetTypeUpdateMsg widgetTypeUpdateMsg = - widgetTypeMsgConstructor.constructWidgetTypeUpdateMsg(msgType, widgetTypeDetails, edgeVersion); + widgetMsgConstructorFactory.getWidgetMsgConstructor(edgeVersion).constructWidgetTypeUpdateMsg(msgType, widgetTypeDetails, edgeVersion); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addWidgetTypeUpdateMsg(widgetTypeUpdateMsg) @@ -52,7 +52,7 @@ public class WidgetTypeEdgeProcessor extends BaseEdgeProcessor { break; case DELETED: WidgetTypeUpdateMsg widgetTypeUpdateMsg = - widgetTypeMsgConstructor.constructWidgetTypeDeleteMsg(widgetTypeId); + widgetMsgConstructorFactory.getWidgetMsgConstructor(edgeVersion).constructWidgetTypeDeleteMsg(widgetTypeId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addWidgetTypeUpdateMsg(widgetTypeUpdateMsg) @@ -61,5 +61,4 @@ public class WidgetTypeEdgeProcessor extends BaseEdgeProcessor { } return downlinkMsg; } - } diff --git a/application/src/test/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructorTest.java b/application/src/test/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructorTest.java index c1f34217ac..c7a3e7a24c 100644 --- a/application/src/test/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructorTest.java +++ b/application/src/test/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructorTest.java @@ -24,11 +24,11 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnitRunner; import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.msg.TbNodeConnectionType; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.data.msg.TbNodeConnectionType; import org.thingsboard.server.common.data.rule.NodeConnectionInfo; import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleNode; @@ -37,6 +37,7 @@ import org.thingsboard.server.gen.edge.v1.RuleChainConnectionInfoProto; import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; import org.thingsboard.server.gen.edge.v1.RuleNodeProto; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.service.edge.rpc.constructor.rule.RuleChainMsgConstructorV1; import java.util.ArrayList; import java.util.List; @@ -49,13 +50,13 @@ public class RuleChainMsgConstructorTest { private static final String RPC_CONNECTION_TYPE = "RPC"; - private RuleChainMsgConstructor constructor; + private RuleChainMsgConstructorV1 ruleChainMsgConstructorV1; private TenantId tenantId; @Before public void setup() { - constructor = new RuleChainMsgConstructor(); + ruleChainMsgConstructorV1 = new RuleChainMsgConstructorV1(); tenantId = new TenantId(UUID.randomUUID()); } @@ -65,7 +66,7 @@ public class RuleChainMsgConstructorTest { RuleChainMetaData ruleChainMetaData = createRuleChainMetaData( ruleChainId, 3, createRuleNodes(ruleChainId), createConnections()); RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = - constructor.constructRuleChainMetadataUpdatedMsg( + ruleChainMsgConstructorV1.constructRuleChainMetadataUpdatedMsg( tenantId, UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, ruleChainMetaData, @@ -84,7 +85,7 @@ public class RuleChainMsgConstructorTest { RuleChainMetaData ruleChainMetaData = createRuleChainMetaData( ruleChainId, 3, createRuleNodes(ruleChainId), createConnections()); RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = - constructor.constructRuleChainMetadataUpdatedMsg( + ruleChainMsgConstructorV1.constructRuleChainMetadataUpdatedMsg( tenantId, UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, ruleChainMetaData, @@ -123,7 +124,7 @@ public class RuleChainMsgConstructorTest { RuleChainId ruleChainId = new RuleChainId(UUID.randomUUID()); RuleChainMetaData ruleChainMetaData = createRuleChainMetaData(ruleChainId, 3, createRuleNodes(ruleChainId), createConnections()); RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = - constructor.constructRuleChainMetadataUpdatedMsg( + ruleChainMsgConstructorV1.constructRuleChainMetadataUpdatedMsg( tenantId, UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, ruleChainMetaData, @@ -165,7 +166,7 @@ public class RuleChainMsgConstructorTest { RuleChainId ruleChainId = new RuleChainId(UUID.randomUUID()); RuleChainMetaData ruleChainMetaData1 = createRuleChainMetaData(ruleChainId, 8, createRuleNodesInDifferentOrder(ruleChainId), createConnectionsInDifferentOrder()); RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = - constructor.constructRuleChainMetadataUpdatedMsg( + ruleChainMsgConstructorV1.constructRuleChainMetadataUpdatedMsg( tenantId, UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, ruleChainMetaData1, diff --git a/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessorTest.java b/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessorTest.java index dc59d4f7e9..c6ee264783 100644 --- a/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessorTest.java +++ b/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessorTest.java @@ -60,27 +60,53 @@ import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.provider.TbQueueProducerProvider; import org.thingsboard.server.queue.util.DataDecodingEncodingService; -import org.thingsboard.server.service.edge.rpc.constructor.AdminSettingsMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.AlarmMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.AssetMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.AssetProfileMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.CustomerMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.DashboardMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.DeviceMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.DeviceProfileMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.EdgeMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.EntityDataMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.EntityViewMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.OtaPackageMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.QueueMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.RelationMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.ResourceMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.RuleChainMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.TenantMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.TenantProfileMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.UserMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.WidgetTypeMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.WidgetsBundleMsgConstructor; +import org.thingsboard.server.service.edge.rpc.constructor.alarm.AlarmMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.alarm.AlarmMsgConstructorV1; +import org.thingsboard.server.service.edge.rpc.constructor.alarm.AlarmMsgConstructorV2; +import org.thingsboard.server.service.edge.rpc.constructor.asset.AssetMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.asset.AssetMsgConstructorV1; +import org.thingsboard.server.service.edge.rpc.constructor.asset.AssetMsgConstructorV2; +import org.thingsboard.server.service.edge.rpc.constructor.customer.CustomerMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.customer.CustomerMsgConstructorV1; +import org.thingsboard.server.service.edge.rpc.constructor.customer.CustomerMsgConstructorV2; +import org.thingsboard.server.service.edge.rpc.constructor.dashboard.DashboardMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.dashboard.DashboardMsgConstructorV1; +import org.thingsboard.server.service.edge.rpc.constructor.dashboard.DashboardMsgConstructorV2; +import org.thingsboard.server.service.edge.rpc.constructor.device.DeviceMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.device.DeviceMsgConstructorV1; +import org.thingsboard.server.service.edge.rpc.constructor.device.DeviceMsgConstructorV2; +import org.thingsboard.server.service.edge.rpc.constructor.edge.EdgeMsgConstructor; +import org.thingsboard.server.service.edge.rpc.constructor.entityview.EntityViewMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.entityview.EntityViewMsgConstructorV1; +import org.thingsboard.server.service.edge.rpc.constructor.entityview.EntityViewMsgConstructorV2; +import org.thingsboard.server.service.edge.rpc.constructor.ota.OtaPackageMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.ota.OtaPackageMsgConstructorV1; +import org.thingsboard.server.service.edge.rpc.constructor.ota.OtaPackageMsgConstructorV2; +import org.thingsboard.server.service.edge.rpc.constructor.queue.QueueMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.queue.QueueMsgConstructorV1; +import org.thingsboard.server.service.edge.rpc.constructor.queue.QueueMsgConstructorV2; +import org.thingsboard.server.service.edge.rpc.constructor.relation.RelationMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.relation.RelationMsgConstructorV1; +import org.thingsboard.server.service.edge.rpc.constructor.relation.RelationMsgConstructorV2; +import org.thingsboard.server.service.edge.rpc.constructor.resource.ResourceMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.resource.ResourceMsgConstructorV1; +import org.thingsboard.server.service.edge.rpc.constructor.resource.ResourceMsgConstructorV2; +import org.thingsboard.server.service.edge.rpc.constructor.rule.RuleChainMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.rule.RuleChainMsgConstructorV1; +import org.thingsboard.server.service.edge.rpc.constructor.rule.RuleChainMsgConstructorV2; +import org.thingsboard.server.service.edge.rpc.constructor.settings.AdminSettingsMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.settings.AdminSettingsMsgConstructorV1; +import org.thingsboard.server.service.edge.rpc.constructor.settings.AdminSettingsMsgConstructorV2; +import org.thingsboard.server.service.edge.rpc.constructor.telemetry.EntityDataMsgConstructor; +import org.thingsboard.server.service.edge.rpc.constructor.tenant.TenantMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.tenant.TenantMsgConstructorV1; +import org.thingsboard.server.service.edge.rpc.constructor.tenant.TenantMsgConstructorV2; +import org.thingsboard.server.service.edge.rpc.constructor.user.UserMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.user.UserMsgConstructorV1; +import org.thingsboard.server.service.edge.rpc.constructor.user.UserMsgConstructorV2; +import org.thingsboard.server.service.edge.rpc.constructor.widget.WidgetMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.widget.WidgetMsgConstructorV1; +import org.thingsboard.server.service.edge.rpc.constructor.widget.WidgetMsgConstructorV2; import org.thingsboard.server.service.entitiy.TbNotificationEntityService; import org.thingsboard.server.service.executors.DbCallbackExecutorService; import org.thingsboard.server.service.profile.TbAssetProfileCache; @@ -212,61 +238,139 @@ public abstract class BaseEdgeProcessorTest { protected EntityDataMsgConstructor entityDataMsgConstructor; @MockBean - protected RuleChainMsgConstructor ruleChainMsgConstructor; + protected AdminSettingsMsgConstructorV1 adminSettingsMsgConstructorV1; @MockBean - protected AlarmMsgConstructor alarmMsgConstructor; + protected AdminSettingsMsgConstructorV2 adminSettingsMsgConstructorV2; + + @MockBean + protected AlarmMsgConstructorV1 alarmMsgConstructorV1; + + @MockBean + protected AlarmMsgConstructorV2 alarmMsgConstructorV2; + + @SpyBean + protected AssetMsgConstructorV1 assetMsgConstructorV1; + + @SpyBean + protected AssetMsgConstructorV2 assetMsgConstructorV2; + + @MockBean + protected CustomerMsgConstructorV1 customerMsgConstructorV1; + + @MockBean + protected CustomerMsgConstructorV2 customerMsgConstructorV2; + + @MockBean + protected DashboardMsgConstructorV1 dashboardMsgConstructorV1; + + @MockBean + protected DashboardMsgConstructorV2 dashboardMsgConstructorV2; + + @SpyBean + protected DeviceMsgConstructorV1 deviceMsgConstructorV1; + + @SpyBean + protected DeviceMsgConstructorV2 deviceMsgConstructorV2; + + @MockBean + protected EntityViewMsgConstructorV1 entityViewMsgConstructorV1; + + @MockBean + protected EntityViewMsgConstructorV2 entityViewMsgConstructorV2; + + @MockBean + protected OtaPackageMsgConstructorV1 otaPackageMsgConstructorV1; + + @MockBean + protected OtaPackageMsgConstructorV2 otaPackageMsgConstructorV2; + + @MockBean + protected QueueMsgConstructorV1 queueMsgConstructorV1; + + @MockBean + protected QueueMsgConstructorV2 queueMsgConstructorV2; + + @MockBean + protected RelationMsgConstructorV1 relationMsgConstructorV1; + + @MockBean + protected RelationMsgConstructorV2 relationMsgConstructorV2; + + @MockBean + protected ResourceMsgConstructorV1 resourceMsgConstructorV1; + + @MockBean + protected ResourceMsgConstructorV2 resourceMsgConstructorV2; @SpyBean - protected DeviceMsgConstructor deviceMsgConstructor; + protected RuleChainMsgConstructorV1 ruleChainMsgConstructorV1; @SpyBean - protected AssetMsgConstructor assetMsgConstructor; + protected RuleChainMsgConstructorV2 ruleChainMsgConstructorV2; + + @MockBean + protected TenantMsgConstructorV1 tenantMsgConstructorV1; @MockBean - protected EntityViewMsgConstructor entityViewMsgConstructor; + protected TenantMsgConstructorV2 tenantMsgConstructorV2; @MockBean - protected DashboardMsgConstructor dashboardMsgConstructor; + protected UserMsgConstructorV1 userMsgConstructorV1; @MockBean - protected RelationMsgConstructor relationMsgConstructor; + protected UserMsgConstructorV2 userMsgConstructorV2; @MockBean - protected UserMsgConstructor userMsgConstructor; + protected WidgetMsgConstructorV1 widgetMsgConstructorV1; + + @MockBean + protected WidgetMsgConstructorV2 widgetMsgConstructorV2; + + @SpyBean + protected RuleChainMsgConstructorFactory ruleChainMsgConstructorFactory; @MockBean - protected CustomerMsgConstructor customerMsgConstructor; + protected AlarmMsgConstructorFactory alarmMsgConstructorFactory; @SpyBean - protected DeviceProfileMsgConstructor deviceProfileMsgConstructor; + protected DeviceMsgConstructorFactory deviceMsgConstructorFactory; @SpyBean - protected AssetProfileMsgConstructor assetProfileMsgConstructor; + protected AssetMsgConstructorFactory assetMsgConstructorFactory; + + @MockBean + protected DashboardMsgConstructorFactory dashboardMsgConstructorFactory; + + @MockBean + protected EntityViewMsgConstructorFactory entityViewMsgConstructorFactory; + + @MockBean + protected RelationMsgConstructorFactory relationMsgConstructorFactory; @MockBean - protected TenantMsgConstructor tenantMsgConstructor; + protected UserMsgConstructorFactory userMsgConstructorFactory; @MockBean - protected TenantProfileMsgConstructor tenantProfileMsgConstructor; + protected CustomerMsgConstructorFactory customerMsgConstructorFactory; @MockBean - protected WidgetsBundleMsgConstructor widgetsBundleMsgConstructor; + protected TenantMsgConstructorFactory tenantMsgConstructorFactory; @MockBean - protected WidgetTypeMsgConstructor widgetTypeMsgConstructor; + protected WidgetMsgConstructorFactory widgetBundleMsgConstructorFactory; @MockBean - protected AdminSettingsMsgConstructor adminSettingsMsgConstructor; + protected AdminSettingsMsgConstructorFactory adminSettingsMsgConstructorFactory; @MockBean - protected OtaPackageMsgConstructor otaPackageMsgConstructor; + protected OtaPackageMsgConstructorFactory otaPackageMsgConstructorFactory; @MockBean - protected QueueMsgConstructor queueMsgConstructor; + protected QueueMsgConstructorFactory queueMsgConstructorFactory; @MockBean - protected ResourceMsgConstructor resourceMsgConstructor; + protected ResourceMsgConstructorFactory resourceMsgConstructorFactory; @MockBean protected EdgeSynchronizationManager edgeSynchronizationManager; From acdd90eafdd1454dbf7107bf1706db38468ea2b8 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Fri, 24 Nov 2023 13:42:42 +0200 Subject: [PATCH 192/252] Renaming MsgConstructorFactory method to get MsgConstructor by Edge Version --- .../rpc/constructor/alarm/AlarmMsgConstructorFactory.java | 2 +- .../customer/CustomerMsgConstructorFactory.java | 2 +- .../edge/rpc/constructor/device/DeviceMsgConstructor.java | 7 +++++++ .../constructor/device/DeviceMsgConstructorFactory.java | 2 +- .../entityview/EntityViewMsgConstructorFactory.java | 2 +- .../constructor/ota/OtaPackageMsgConstructorFactory.java | 2 +- .../rpc/constructor/queue/QueueMsgConstructorFactory.java | 2 +- .../relation/RelationMsgConstructorFactory.java | 2 +- .../resource/ResourceMsgConstructorFactory.java | 2 +- .../constructor/rule/BaseRuleChainMetadataConstructor.java | 2 +- .../rpc/constructor/rule/BaseRuleChainMsgConstructor.java | 2 +- .../constructor/rule/RuleChainMetadataConstructorV330.java | 2 +- .../constructor/rule/RuleChainMetadataConstructorV340.java | 2 +- .../constructor/rule/RuleChainMetadataConstructorV362.java | 2 +- .../constructor/rule/RuleChainMsgConstructorFactory.java | 2 +- .../rpc/constructor/rule/RuleChainMsgConstructorV1.java | 2 +- .../rpc/constructor/rule/RuleChainMsgConstructorV2.java | 2 +- .../settings/AdminSettingsMsgConstructorFactory.java | 2 +- .../constructor/tenant/TenantMsgConstructorFactory.java | 2 +- .../rpc/constructor/user/UserMsgConstructorFactory.java | 2 +- .../constructor/widget/WidgetMsgConstructorFactory.java | 2 +- .../edge/rpc/processor/alarm/BaseAlarmProcessor.java | 4 ++-- .../edge/rpc/processor/customer/CustomerEdgeProcessor.java | 4 ++-- .../rpc/processor/dashboard/DashboardEdgeProcessor.java | 4 ++-- .../rpc/processor/entityview/EntityViewEdgeProcessor.java | 4 ++-- .../edge/rpc/processor/ota/OtaPackageEdgeProcessor.java | 4 ++-- .../edge/rpc/processor/queue/QueueEdgeProcessor.java | 4 ++-- .../edge/rpc/processor/relation/RelationEdgeProcessor.java | 2 +- .../edge/rpc/processor/resource/ResourceEdgeProcessor.java | 4 ++-- .../edge/rpc/processor/rule/RuleChainEdgeProcessor.java | 6 +++--- .../rpc/processor/settings/AdminSettingsEdgeProcessor.java | 2 +- .../edge/rpc/processor/tenant/TenantEdgeProcessor.java | 4 ++-- .../rpc/processor/tenant/TenantProfileEdgeProcessor.java | 2 +- .../service/edge/rpc/processor/user/UserEdgeProcessor.java | 6 +++--- .../rpc/processor/widget/WidgetBundleEdgeProcessor.java | 4 ++-- .../edge/rpc/processor/widget/WidgetTypeEdgeProcessor.java | 4 ++-- 36 files changed, 56 insertions(+), 49 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/alarm/AlarmMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/alarm/AlarmMsgConstructorFactory.java index 41a13e4447..4a49f6cf6b 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/alarm/AlarmMsgConstructorFactory.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/alarm/AlarmMsgConstructorFactory.java @@ -30,7 +30,7 @@ public class AlarmMsgConstructorFactory { @Autowired protected AlarmMsgConstructorV2 alarmMsgConstructorV2; - public AlarmMsgConstructor getAlarmMsgConstructor(EdgeVersion edgeVersion) { + public AlarmMsgConstructor getMsgConstructorByEdgeVersion(EdgeVersion edgeVersion) { switch (edgeVersion) { case V_3_3_0: case V_3_3_3: diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/CustomerMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/CustomerMsgConstructorFactory.java index 350a4b6f9d..ba8e643445 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/CustomerMsgConstructorFactory.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/CustomerMsgConstructorFactory.java @@ -30,7 +30,7 @@ public class CustomerMsgConstructorFactory { @Autowired protected CustomerMsgConstructorV2 customerMsgConstructorV2; - public CustomerMsgConstructor getCustomerMsgConstructor(EdgeVersion edgeVersion) { + public CustomerMsgConstructor getMsgConstructorByEdgeVersion(EdgeVersion edgeVersion) { switch (edgeVersion) { case V_3_3_0: case V_3_3_3: diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructor.java index 514aeceb68..3bd40a1fbb 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructor.java @@ -17,9 +17,12 @@ package org.thingsboard.server.service.edge.rpc.constructor.device; import com.fasterxml.jackson.databind.JsonNode; import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.gen.edge.v1.DeviceCredentialsUpdateMsg; +import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg; import org.thingsboard.server.gen.edge.v1.DeviceRpcCallMsg; import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; @@ -34,5 +37,9 @@ public interface DeviceMsgConstructor { DeviceCredentialsUpdateMsg constructDeviceCredentialsUpdatedMsg(DeviceCredentials deviceCredentials); + DeviceProfileUpdateMsg constructDeviceProfileUpdatedMsg(UpdateMsgType msgType, DeviceProfile deviceProfile); + + DeviceProfileUpdateMsg constructDeviceProfileDeleteMsg(DeviceProfileId deviceProfileId); + DeviceRpcCallMsg constructDeviceRpcCallMsg(UUID deviceId, JsonNode body); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructorFactory.java index 2f14c401ea..44e072cb11 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructorFactory.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructorFactory.java @@ -30,7 +30,7 @@ public class DeviceMsgConstructorFactory { @Autowired protected DeviceMsgConstructorV2 deviceMsgConstructorV2; - public DeviceMsgConstructor getDeviceMsgConstructor(EdgeVersion edgeVersion) { + public DeviceMsgConstructor getMsgConstructorByEdgeVersion(EdgeVersion edgeVersion) { switch (edgeVersion) { case V_3_3_0: case V_3_3_3: diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/EntityViewMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/EntityViewMsgConstructorFactory.java index b2c7f3b0c3..57b19d6a14 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/EntityViewMsgConstructorFactory.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/EntityViewMsgConstructorFactory.java @@ -30,7 +30,7 @@ public class EntityViewMsgConstructorFactory { @Autowired protected EntityViewMsgConstructorV2 entityViewMsgConstructorV2; - public EntityViewMsgConstructor getEntityViewMsgConstructor(EdgeVersion edgeVersion) { + public EntityViewMsgConstructor getMsgConstructorByEdgeVersion(EdgeVersion edgeVersion) { switch (edgeVersion) { case V_3_3_0: case V_3_3_3: diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/OtaPackageMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/OtaPackageMsgConstructorFactory.java index ae7a48028c..7e2b9cea7e 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/OtaPackageMsgConstructorFactory.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/OtaPackageMsgConstructorFactory.java @@ -30,7 +30,7 @@ public class OtaPackageMsgConstructorFactory { @Autowired protected OtaPackageMsgConstructorV2 otaPackageMsgConstructorV2; - public OtaPackageMsgConstructor getOtaPackageMsgConstructor(EdgeVersion edgeVersion) { + public OtaPackageMsgConstructor getMsgConstructorByEdgeVersion(EdgeVersion edgeVersion) { switch (edgeVersion) { case V_3_3_0: case V_3_3_3: diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/QueueMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/QueueMsgConstructorFactory.java index d250c5fd24..e6502aaff7 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/QueueMsgConstructorFactory.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/QueueMsgConstructorFactory.java @@ -30,7 +30,7 @@ public class QueueMsgConstructorFactory { @Autowired protected QueueMsgConstructorV2 queueMsgConstructorV2; - public QueueMsgConstructor getQueueMsgConstructor(EdgeVersion edgeVersion) { + public QueueMsgConstructor getMsgConstructorByEdgeVersion(EdgeVersion edgeVersion) { switch (edgeVersion) { case V_3_3_0: case V_3_3_3: diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/relation/RelationMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/relation/RelationMsgConstructorFactory.java index a17266300d..52015f38e7 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/relation/RelationMsgConstructorFactory.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/relation/RelationMsgConstructorFactory.java @@ -30,7 +30,7 @@ public class RelationMsgConstructorFactory { @Autowired protected RelationMsgConstructorV2 relationMsgConstructorV2; - public RelationMsgConstructor getRelationMsgConstructor(EdgeVersion edgeVersion) { + public RelationMsgConstructor getMsgConstructorByEdgeVersion(EdgeVersion edgeVersion) { switch (edgeVersion) { case V_3_3_0: case V_3_3_3: diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/ResourceMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/ResourceMsgConstructorFactory.java index ef32b623de..15b571a098 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/ResourceMsgConstructorFactory.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/ResourceMsgConstructorFactory.java @@ -30,7 +30,7 @@ public class ResourceMsgConstructorFactory { @Autowired protected ResourceMsgConstructorV2 resourceMsgConstructorV2; - public ResourceMsgConstructor getResourceMsgConstructor(EdgeVersion edgeVersion) { + public ResourceMsgConstructor getMsgConstructorByEdgeVersion(EdgeVersion edgeVersion) { switch (edgeVersion) { case V_3_3_0: case V_3_3_3: diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/BaseRuleChainMetadataConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/BaseRuleChainMetadataConstructor.java index 77815351bc..be10a714f6 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/BaseRuleChainMetadataConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/BaseRuleChainMetadataConstructor.java @@ -37,7 +37,7 @@ import java.util.NavigableSet; @Slf4j @AllArgsConstructor -public abstract class AbstractRuleChainMetadataConstructor implements RuleChainMetadataConstructor { +public abstract class BaseRuleChainMetadataConstructor implements RuleChainMetadataConstructor { @Override public RuleChainMetadataUpdateMsg constructRuleChainMetadataUpdatedMsg(TenantId tenantId, diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/BaseRuleChainMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/BaseRuleChainMsgConstructor.java index da237de924..cf5967fac1 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/BaseRuleChainMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/BaseRuleChainMsgConstructor.java @@ -23,7 +23,7 @@ import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; import org.thingsboard.server.gen.edge.v1.RuleChainUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; -public abstract class AbstractRuleChainMsgConstructor implements RuleChainMsgConstructor { +public abstract class BaseRuleChainMsgConstructor implements RuleChainMsgConstructor { @Override public RuleChainUpdateMsg constructRuleChainDeleteMsg(RuleChainId ruleChainId) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV330.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV330.java index cfa6f5edbe..64d362342b 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV330.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV330.java @@ -36,7 +36,7 @@ import java.util.UUID; import java.util.stream.Collectors; @Slf4j -public class RuleChainMetadataConstructorV330 extends AbstractRuleChainMetadataConstructor { +public class RuleChainMetadataConstructorV330 extends BaseRuleChainMetadataConstructor { private static final String RULE_CHAIN_INPUT_NODE = TbRuleChainInputNode.class.getName(); private static final String TB_RULE_CHAIN_OUTPUT_NODE = TbRuleChainOutputNode.class.getName(); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV340.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV340.java index 48c36c2b4d..2133024931 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV340.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV340.java @@ -23,7 +23,7 @@ import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; import java.util.TreeSet; @Slf4j -public class RuleChainMetadataConstructorV340 extends AbstractRuleChainMetadataConstructor { +public class RuleChainMetadataConstructorV340 extends BaseRuleChainMetadataConstructor { @Override protected void constructRuleChainMetadataUpdatedMsg(TenantId tenantId, diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV362.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV362.java index e1b1ea4d13..7a8a6d02cb 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV362.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV362.java @@ -20,7 +20,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; -public class RuleChainMetadataConstructorV362 extends AbstractRuleChainMetadataConstructor { +public class RuleChainMetadataConstructorV362 extends BaseRuleChainMetadataConstructor { @Override protected void constructRuleChainMetadataUpdatedMsg(TenantId tenantId, RuleChainMetadataUpdateMsg.Builder builder, RuleChainMetaData ruleChainMetaData) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructorFactory.java index b989612431..0c169db0f4 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructorFactory.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructorFactory.java @@ -30,7 +30,7 @@ public class RuleChainMsgConstructorFactory { @Autowired protected RuleChainMsgConstructorV2 ruleChainMsgConstructorV2; - public RuleChainMsgConstructor getRuleChainMsgConstructor(EdgeVersion edgeVersion) { + public RuleChainMsgConstructor getMsgConstructorByEdgeVersion(EdgeVersion edgeVersion) { switch (edgeVersion) { case V_3_3_0: case V_3_3_3: diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructorV1.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructorV1.java index 8404b5d67d..6c918cfc56 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructorV1.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructorV1.java @@ -24,7 +24,7 @@ import org.thingsboard.server.queue.util.TbCoreComponent; @Component @TbCoreComponent -public class RuleChainMsgConstructorV1 extends AbstractRuleChainMsgConstructor { +public class RuleChainMsgConstructorV1 extends BaseRuleChainMsgConstructor { @Override public RuleChainUpdateMsg constructRuleChainUpdatedMsg(UpdateMsgType msgType, RuleChain ruleChain, boolean isRoot) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructorV2.java index 492ea6c40d..f5c1c12b39 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructorV2.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructorV2.java @@ -24,7 +24,7 @@ import org.thingsboard.server.queue.util.TbCoreComponent; @Component @TbCoreComponent -public class RuleChainMsgConstructorV2 extends AbstractRuleChainMsgConstructor { +public class RuleChainMsgConstructorV2 extends BaseRuleChainMsgConstructor { @Override public RuleChainUpdateMsg constructRuleChainUpdatedMsg(UpdateMsgType msgType, RuleChain ruleChain, boolean isRoot) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/settings/AdminSettingsMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/settings/AdminSettingsMsgConstructorFactory.java index 060c7fa3a5..1dd9be719d 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/settings/AdminSettingsMsgConstructorFactory.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/settings/AdminSettingsMsgConstructorFactory.java @@ -30,7 +30,7 @@ public class AdminSettingsMsgConstructorFactory { @Autowired protected AdminSettingsMsgConstructorV2 adminSettingsMsgConstructorV2; - public AdminSettingsMsgConstructor getAdminSettingsMsgConstructor(EdgeVersion edgeVersion) { + public AdminSettingsMsgConstructor getMsgConstructorByEdgeVersion(EdgeVersion edgeVersion) { switch (edgeVersion) { case V_3_3_0: case V_3_3_3: diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorFactory.java index 753eb0dc05..a6ce714b1a 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorFactory.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorFactory.java @@ -30,7 +30,7 @@ public class TenantMsgConstructorFactory { @Autowired protected TenantMsgConstructorV2 tenantMsgConstructorV2; - public TenantMsgConstructor getTenantMsgConstructor(EdgeVersion edgeVersion) { + public TenantMsgConstructor getMsgConstructorByEdgeVersion(EdgeVersion edgeVersion) { switch (edgeVersion) { case V_3_3_0: case V_3_3_3: diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/UserMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/UserMsgConstructorFactory.java index 47554ce398..2e7f49c0fa 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/UserMsgConstructorFactory.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/UserMsgConstructorFactory.java @@ -30,7 +30,7 @@ public class UserMsgConstructorFactory { @Autowired protected UserMsgConstructorV2 userMsgConstructorV2; - public UserMsgConstructor getUserMsgConstructor(EdgeVersion edgeVersion) { + public UserMsgConstructor getMsgConstructorByEdgeVersion(EdgeVersion edgeVersion) { switch (edgeVersion) { case V_3_3_0: case V_3_3_3: diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/WidgetMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/WidgetMsgConstructorFactory.java index ce1ae735f3..b98494f36b 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/WidgetMsgConstructorFactory.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/WidgetMsgConstructorFactory.java @@ -30,7 +30,7 @@ public class WidgetMsgConstructorFactory { @Autowired protected WidgetMsgConstructorV2 widgetMsgConstructorV2; - public WidgetMsgConstructor getWidgetMsgConstructor(EdgeVersion edgeVersion) { + public WidgetMsgConstructor getMsgConstructorByEdgeVersion(EdgeVersion edgeVersion) { switch (edgeVersion) { case V_3_3_0: case V_3_3_3: diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java index edc03469e0..41c05a80c2 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java @@ -132,13 +132,13 @@ public abstract class BaseAlarmProcessor extends BaseEdgeProcessor { case ALARM_CLEAR: Alarm alarm = alarmService.findAlarmById(tenantId, alarmId); if (alarm != null) { - return alarmMsgConstructorFactory.getAlarmMsgConstructor(edgeVersion).constructAlarmUpdatedMsg(msgType, alarm, findOriginatorEntityName(tenantId, alarm)); + return alarmMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructAlarmUpdatedMsg(msgType, alarm, findOriginatorEntityName(tenantId, alarm)); } break; case DELETED: Alarm deletedAlarm = JacksonUtil.convertValue(body, Alarm.class); if (deletedAlarm != null) { - return alarmMsgConstructorFactory.getAlarmMsgConstructor(edgeVersion).constructAlarmUpdatedMsg(msgType, deletedAlarm, findOriginatorEntityName(tenantId, deletedAlarm)); + return alarmMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructAlarmUpdatedMsg(msgType, deletedAlarm, findOriginatorEntityName(tenantId, deletedAlarm)); } } return null; diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/customer/CustomerEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/customer/CustomerEdgeProcessor.java index 2ece4edbb5..2f6a979653 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/customer/CustomerEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/customer/CustomerEdgeProcessor.java @@ -58,7 +58,7 @@ public class CustomerEdgeProcessor extends BaseEdgeProcessor { if (customer != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); CustomerUpdateMsg customerUpdateMsg = - customerMsgConstructorFactory.getCustomerMsgConstructor(edgeVersion).constructCustomerUpdatedMsg(msgType, customer); + customerMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructCustomerUpdatedMsg(msgType, customer); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addCustomerUpdateMsg(customerUpdateMsg) @@ -67,7 +67,7 @@ public class CustomerEdgeProcessor extends BaseEdgeProcessor { break; case DELETED: CustomerUpdateMsg customerUpdateMsg = - customerMsgConstructorFactory.getCustomerMsgConstructor(edgeVersion).constructCustomerDeleteMsg(customerId); + customerMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructCustomerDeleteMsg(customerId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addCustomerUpdateMsg(customerUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardEdgeProcessor.java index 5c3c921cde..c4679b40b0 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardEdgeProcessor.java @@ -111,7 +111,7 @@ public class DashboardEdgeProcessor extends BaseDashboardProcessor { if (dashboard != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); DashboardUpdateMsg dashboardUpdateMsg = - dashboardMsgConstructorFactory.getDashboardMsgConstructor(edgeVersion).constructDashboardUpdatedMsg(msgType, dashboard); + dashboardMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructDashboardUpdatedMsg(msgType, dashboard); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addDashboardUpdateMsg(dashboardUpdateMsg) @@ -121,7 +121,7 @@ public class DashboardEdgeProcessor extends BaseDashboardProcessor { case DELETED: case UNASSIGNED_FROM_EDGE: DashboardUpdateMsg dashboardUpdateMsg = - dashboardMsgConstructorFactory.getDashboardMsgConstructor(edgeVersion).constructDashboardDeleteMsg(dashboardId); + dashboardMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructDashboardDeleteMsg(dashboardId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addDashboardUpdateMsg(dashboardUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewEdgeProcessor.java index 50eb258b7d..0f1727ab10 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewEdgeProcessor.java @@ -118,7 +118,7 @@ public class EntityViewEdgeProcessor extends BaseEntityViewProcessor { if (entityView != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); EntityViewUpdateMsg entityViewUpdateMsg = - entityViewMsgConstructorFactory.getEntityViewMsgConstructor(edgeVersion).constructEntityViewUpdatedMsg(msgType, entityView); + entityViewMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructEntityViewUpdatedMsg(msgType, entityView); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addEntityViewUpdateMsg(entityViewUpdateMsg) @@ -128,7 +128,7 @@ public class EntityViewEdgeProcessor extends BaseEntityViewProcessor { case DELETED: case UNASSIGNED_FROM_EDGE: EntityViewUpdateMsg entityViewUpdateMsg = - entityViewMsgConstructorFactory.getEntityViewMsgConstructor(edgeVersion).constructEntityViewDeleteMsg(entityViewId); + entityViewMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructEntityViewDeleteMsg(entityViewId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addEntityViewUpdateMsg(entityViewUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/ota/OtaPackageEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/ota/OtaPackageEdgeProcessor.java index dd94cc8803..1b06f323d0 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/ota/OtaPackageEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/ota/OtaPackageEdgeProcessor.java @@ -43,7 +43,7 @@ public class OtaPackageEdgeProcessor extends BaseEdgeProcessor { if (otaPackage != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); OtaPackageUpdateMsg otaPackageUpdateMsg = - otaPackageMsgConstructorFactory.getOtaPackageMsgConstructor(edgeVersion).constructOtaPackageUpdatedMsg(msgType, otaPackage); + otaPackageMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructOtaPackageUpdatedMsg(msgType, otaPackage); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addOtaPackageUpdateMsg(otaPackageUpdateMsg) @@ -52,7 +52,7 @@ public class OtaPackageEdgeProcessor extends BaseEdgeProcessor { break; case DELETED: OtaPackageUpdateMsg otaPackageUpdateMsg = - otaPackageMsgConstructorFactory.getOtaPackageMsgConstructor(edgeVersion).constructOtaPackageDeleteMsg(otaPackageId); + otaPackageMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructOtaPackageDeleteMsg(otaPackageId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addOtaPackageUpdateMsg(otaPackageUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/queue/QueueEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/queue/QueueEdgeProcessor.java index 3836cf3c65..fa35f35151 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/queue/QueueEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/queue/QueueEdgeProcessor.java @@ -43,7 +43,7 @@ public class QueueEdgeProcessor extends BaseEdgeProcessor { if (queue != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); QueueUpdateMsg queueUpdateMsg = - queueMsgConstructorFactory.getQueueMsgConstructor(edgeVersion).constructQueueUpdatedMsg(msgType, queue); + queueMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructQueueUpdatedMsg(msgType, queue); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addQueueUpdateMsg(queueUpdateMsg) @@ -52,7 +52,7 @@ public class QueueEdgeProcessor extends BaseEdgeProcessor { break; case DELETED: QueueUpdateMsg queueDeleteMsg = - queueMsgConstructorFactory.getQueueMsgConstructor(edgeVersion).constructQueueDeleteMsg(queueId); + queueMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructQueueDeleteMsg(queueId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addQueueUpdateMsg(queueDeleteMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessor.java index 3adf19548f..530ee47982 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessor.java @@ -59,7 +59,7 @@ public class RelationEdgeProcessor extends BaseRelationProcessor { public DownlinkMsg convertRelationEventToDownlink(EdgeEvent edgeEvent, EdgeVersion edgeVersion) { EntityRelation entityRelation = JacksonUtil.convertValue(edgeEvent.getBody(), EntityRelation.class); UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); - RelationUpdateMsg relationUpdateMsg = relationMsgConstructorFactory.getRelationMsgConstructor(edgeVersion).constructRelationUpdatedMsg(msgType, entityRelation); + RelationUpdateMsg relationUpdateMsg = relationMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructRelationUpdatedMsg(msgType, entityRelation); return DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addRelationUpdateMsg(relationUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessor.java index 89ae103b2b..fb811d2112 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessor.java @@ -81,7 +81,7 @@ public class ResourceEdgeProcessor extends BaseResourceProcessor { if (tbResource != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); ResourceUpdateMsg resourceUpdateMsg = - resourceMsgConstructorFactory.getResourceMsgConstructor(edgeVersion).constructResourceUpdatedMsg(msgType, tbResource); + resourceMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructResourceUpdatedMsg(msgType, tbResource); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addResourceUpdateMsg(resourceUpdateMsg) @@ -90,7 +90,7 @@ public class ResourceEdgeProcessor extends BaseResourceProcessor { break; case DELETED: ResourceUpdateMsg resourceUpdateMsg = - resourceMsgConstructorFactory.getResourceMsgConstructor(edgeVersion).constructResourceDeleteMsg(tbResourceId); + resourceMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructResourceDeleteMsg(tbResourceId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addResourceUpdateMsg(resourceUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/rule/RuleChainEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/rule/RuleChainEdgeProcessor.java index 910e7e0541..b71abd97e0 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/rule/RuleChainEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/rule/RuleChainEdgeProcessor.java @@ -54,7 +54,7 @@ public class RuleChainEdgeProcessor extends BaseEdgeProcessor { } UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); RuleChainUpdateMsg ruleChainUpdateMsg = - ruleChainMsgConstructorFactory.getRuleChainMsgConstructor(edgeVersion).constructRuleChainUpdatedMsg(msgType, ruleChain, isRoot); + ruleChainMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructRuleChainUpdatedMsg(msgType, ruleChain, isRoot); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addRuleChainUpdateMsg(ruleChainUpdateMsg) @@ -65,7 +65,7 @@ public class RuleChainEdgeProcessor extends BaseEdgeProcessor { case UNASSIGNED_FROM_EDGE: downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) - .addRuleChainUpdateMsg(ruleChainMsgConstructorFactory.getRuleChainMsgConstructor(edgeVersion).constructRuleChainDeleteMsg(ruleChainId)) + .addRuleChainUpdateMsg(ruleChainMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructRuleChainDeleteMsg(ruleChainId)) .build(); break; } @@ -80,7 +80,7 @@ public class RuleChainEdgeProcessor extends BaseEdgeProcessor { RuleChainMetaData ruleChainMetaData = ruleChainService.loadRuleChainMetaData(edgeEvent.getTenantId(), ruleChainId); UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = - ruleChainMsgConstructorFactory.getRuleChainMsgConstructor(edgeVersion).constructRuleChainMetadataUpdatedMsg(edgeEvent.getTenantId(), msgType, ruleChainMetaData, edgeVersion); + ruleChainMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructRuleChainMetadataUpdatedMsg(edgeEvent.getTenantId(), msgType, ruleChainMetaData, edgeVersion); if (ruleChainMetadataUpdateMsg != null) { downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/settings/AdminSettingsEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/settings/AdminSettingsEdgeProcessor.java index 0b7f3021f3..fa2517f7df 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/settings/AdminSettingsEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/settings/AdminSettingsEdgeProcessor.java @@ -38,7 +38,7 @@ public class AdminSettingsEdgeProcessor extends BaseEdgeProcessor { return null; } AdminSettingsUpdateMsg adminSettingsUpdateMsg = - adminSettingsMsgConstructorFactory.getAdminSettingsMsgConstructor(edgeVersion).constructAdminSettingsUpdateMsg(adminSettings); + adminSettingsMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructAdminSettingsUpdateMsg(adminSettings); return DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addAdminSettingsUpdateMsg(adminSettingsUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantEdgeProcessor.java index 1ef44a72ad..b421b1768c 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantEdgeProcessor.java @@ -43,9 +43,9 @@ public class TenantEdgeProcessor extends BaseEdgeProcessor { Tenant tenant = tenantService.findTenantById(tenantId); if (tenant != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); - TenantUpdateMsg tenantUpdateMsg = tenantMsgConstructorFactory.getTenantMsgConstructor(edgeVersion).constructTenantUpdateMsg(msgType, tenant); + TenantUpdateMsg tenantUpdateMsg = tenantMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructTenantUpdateMsg(msgType, tenant); TenantProfile tenantProfile = tenantProfileService.findTenantProfileById(tenantId, tenant.getTenantProfileId()); - TenantProfileUpdateMsg tenantProfileUpdateMsg = tenantMsgConstructorFactory.getTenantMsgConstructor(edgeVersion).constructTenantProfileUpdateMsg(msgType, tenantProfile, edgeVersion); + TenantProfileUpdateMsg tenantProfileUpdateMsg = tenantMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructTenantProfileUpdateMsg(msgType, tenantProfile, edgeVersion); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addTenantUpdateMsg(tenantUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantProfileEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantProfileEdgeProcessor.java index 4f264f7bb4..84b51b09b5 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantProfileEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantProfileEdgeProcessor.java @@ -42,7 +42,7 @@ public class TenantProfileEdgeProcessor extends BaseEdgeProcessor { if (tenantProfile != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); TenantProfileUpdateMsg tenantProfileUpdateMsg = - tenantMsgConstructorFactory.getTenantMsgConstructor(edgeVersion).constructTenantProfileUpdateMsg(msgType, tenantProfile, edgeVersion); + tenantMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructTenantProfileUpdateMsg(msgType, tenantProfile, edgeVersion); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addTenantProfileUpdateMsg(tenantProfileUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/user/UserEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/user/UserEdgeProcessor.java index 3ee3fc109f..68ac558e39 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/user/UserEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/user/UserEdgeProcessor.java @@ -45,21 +45,21 @@ public class UserEdgeProcessor extends BaseEdgeProcessor { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) - .addUserUpdateMsg(userMsgConstructorFactory.getUserMsgConstructor(edgeVersion).constructUserUpdatedMsg(msgType, user)) + .addUserUpdateMsg(userMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructUserUpdatedMsg(msgType, user)) .build(); } break; case DELETED: downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) - .addUserUpdateMsg(userMsgConstructorFactory.getUserMsgConstructor(edgeVersion).constructUserDeleteMsg(userId)) + .addUserUpdateMsg(userMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructUserDeleteMsg(userId)) .build(); break; case CREDENTIALS_UPDATED: UserCredentials userCredentialsByUserId = userService.findUserCredentialsByUserId(edgeEvent.getTenantId(), userId); if (userCredentialsByUserId != null && userCredentialsByUserId.isEnabled()) { UserCredentialsUpdateMsg userCredentialsUpdateMsg = - userMsgConstructorFactory.getUserMsgConstructor(edgeVersion).constructUserCredentialsUpdatedMsg(userCredentialsByUserId); + userMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructUserCredentialsUpdatedMsg(userCredentialsByUserId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addUserCredentialsUpdateMsg(userCredentialsUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetBundleEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetBundleEdgeProcessor.java index e48387fc64..89ebf9513a 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetBundleEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetBundleEdgeProcessor.java @@ -46,7 +46,7 @@ public class WidgetBundleEdgeProcessor extends BaseEdgeProcessor { List widgets = widgetTypeService.findWidgetFqnsByWidgetsBundleId(edgeEvent.getTenantId(), widgetsBundleId); UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); WidgetsBundleUpdateMsg widgetsBundleUpdateMsg = - widgetMsgConstructorFactory.getWidgetMsgConstructor(edgeVersion).constructWidgetsBundleUpdateMsg(msgType, widgetsBundle, widgets); + widgetMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructWidgetsBundleUpdateMsg(msgType, widgetsBundle, widgets); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addWidgetsBundleUpdateMsg(widgetsBundleUpdateMsg) @@ -55,7 +55,7 @@ public class WidgetBundleEdgeProcessor extends BaseEdgeProcessor { break; case DELETED: WidgetsBundleUpdateMsg widgetsBundleUpdateMsg = - widgetMsgConstructorFactory.getWidgetMsgConstructor(edgeVersion).constructWidgetsBundleDeleteMsg(widgetsBundleId); + widgetMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructWidgetsBundleDeleteMsg(widgetsBundleId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addWidgetsBundleUpdateMsg(widgetsBundleUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetTypeEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetTypeEdgeProcessor.java index 8125235ae6..4e611c83f0 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetTypeEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetTypeEdgeProcessor.java @@ -43,7 +43,7 @@ public class WidgetTypeEdgeProcessor extends BaseEdgeProcessor { if (widgetTypeDetails != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); WidgetTypeUpdateMsg widgetTypeUpdateMsg = - widgetMsgConstructorFactory.getWidgetMsgConstructor(edgeVersion).constructWidgetTypeUpdateMsg(msgType, widgetTypeDetails, edgeVersion); + widgetMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructWidgetTypeUpdateMsg(msgType, widgetTypeDetails, edgeVersion); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addWidgetTypeUpdateMsg(widgetTypeUpdateMsg) @@ -52,7 +52,7 @@ public class WidgetTypeEdgeProcessor extends BaseEdgeProcessor { break; case DELETED: WidgetTypeUpdateMsg widgetTypeUpdateMsg = - widgetMsgConstructorFactory.getWidgetMsgConstructor(edgeVersion).constructWidgetTypeDeleteMsg(widgetTypeId); + widgetMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructWidgetTypeDeleteMsg(widgetTypeId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addWidgetTypeUpdateMsg(widgetTypeUpdateMsg) From d83d1fe720cf12b0d8c00fc07cabadcc1987af12 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Fri, 24 Nov 2023 17:27:42 +0200 Subject: [PATCH 193/252] Improve factory implementation: add abstract factory and common interface to reduce code duplicate --- .../edge/rpc/constructor/MsgConstructor.java | 20 ++++++++ .../constructor/MsgConstructorFactory.java | 46 +++++++++++++++++++ .../alarm/AlarmMsgConstructor.java | 3 +- .../alarm/AlarmMsgConstructorFactory.java | 23 +--------- .../asset/AssetMsgConstructor.java | 3 +- .../asset/AssetMsgConstructorFactory.java | 23 +--------- .../customer/CustomerMsgConstructor.java | 3 +- .../CustomerMsgConstructorFactory.java | 23 +--------- .../dashboard/DashboardMsgConstructor.java | 3 +- .../DashboardMsgConstructorFactory.java | 23 +--------- .../device/DeviceMsgConstructor.java | 3 +- .../device/DeviceMsgConstructorFactory.java | 21 +-------- .../entityview/EntityViewMsgConstructor.java | 3 +- .../EntityViewMsgConstructorFactory.java | 23 +--------- .../ota/OtaPackageMsgConstructor.java | 3 +- .../ota/OtaPackageMsgConstructorFactory.java | 23 ++-------- .../queue/QueueMsgConstructor.java | 3 +- .../queue/QueueMsgConstructorFactory.java | 23 +--------- .../relation/RelationMsgConstructor.java | 3 +- .../RelationMsgConstructorFactory.java | 23 ++-------- .../resource/ResourceMsgConstructor.java | 3 +- .../ResourceMsgConstructorFactory.java | 23 +--------- .../rule/RuleChainMsgConstructor.java | 3 +- .../rule/RuleChainMsgConstructorFactory.java | 23 +--------- .../settings/AdminSettingsMsgConstructor.java | 3 +- .../AdminSettingsMsgConstructorFactory.java | 23 +--------- .../tenant/TenantMsgConstructor.java | 3 +- .../constructor/user/UserMsgConstructor.java | 3 +- .../user/UserMsgConstructorFactory.java | 23 +--------- .../widget/WidgetMsgConstructor.java | 3 +- .../widget/WidgetMsgConstructorFactory.java | 23 +--------- .../processor/alarm/BaseAlarmProcessor.java | 7 ++- .../processor/asset/AssetEdgeProcessor.java | 12 +++-- .../asset/AssetProfileEdgeProcessor.java | 9 ++-- .../customer/CustomerEdgeProcessor.java | 9 ++-- .../dashboard/DashboardEdgeProcessor.java | 9 ++-- .../processor/device/DeviceEdgeProcessor.java | 22 +++++---- .../device/DeviceProfileEdgeProcessor.java | 9 ++-- .../entityview/EntityViewEdgeProcessor.java | 9 ++-- .../ota/OtaPackageEdgeProcessor.java | 9 ++-- .../processor/queue/QueueEdgeProcessor.java | 9 ++-- .../relation/RelationEdgeProcessor.java | 4 +- .../resource/ResourceEdgeProcessor.java | 9 ++-- .../rule/RuleChainEdgeProcessor.java | 14 ++++-- .../settings/AdminSettingsEdgeProcessor.java | 5 +- .../processor/tenant/TenantEdgeProcessor.java | 9 +++- .../tenant/TenantProfileEdgeProcessor.java | 6 ++- .../rpc/processor/user/UserEdgeProcessor.java | 7 +-- .../widget/WidgetBundleEdgeProcessor.java | 5 +- .../widget/WidgetTypeEdgeProcessor.java | 5 +- 50 files changed, 230 insertions(+), 369 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/MsgConstructor.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/MsgConstructorFactory.java diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/MsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/MsgConstructor.java new file mode 100644 index 0000000000..071f2bc25d --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/MsgConstructor.java @@ -0,0 +1,20 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor; + +public interface MsgConstructor { + +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/MsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/MsgConstructorFactory.java new file mode 100644 index 0000000000..93f08c27ca --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/MsgConstructorFactory.java @@ -0,0 +1,46 @@ +/** + * Copyright © 2016-2023 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. + */ +package org.thingsboard.server.service.edge.rpc.constructor; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public abstract class MsgConstructorFactory { + + @Autowired + protected T v1Constructor; + + @Autowired + protected U v2Constructor; + + public MsgConstructor getMsgConstructorByEdgeVersion(EdgeVersion edgeVersion) { + switch (edgeVersion) { + case V_3_3_0: + case V_3_3_3: + case V_3_4_0: + case V_3_6_0: + case V_3_6_1: + return v1Constructor; + case V_3_6_2: + default: + return v2Constructor; + } + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/alarm/AlarmMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/alarm/AlarmMsgConstructor.java index 7251c067d7..fd3b4ef95d 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/alarm/AlarmMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/alarm/AlarmMsgConstructor.java @@ -18,8 +18,9 @@ package org.thingsboard.server.service.edge.rpc.constructor.alarm; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructor; -public interface AlarmMsgConstructor { +public interface AlarmMsgConstructor extends MsgConstructor { AlarmUpdateMsg constructAlarmUpdatedMsg(UpdateMsgType msgType, Alarm alarm, String entityName); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/alarm/AlarmMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/alarm/AlarmMsgConstructorFactory.java index 4a49f6cf6b..6d7e7ebd5c 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/alarm/AlarmMsgConstructorFactory.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/alarm/AlarmMsgConstructorFactory.java @@ -15,31 +15,12 @@ */ package org.thingsboard.server.service.edge.rpc.constructor.alarm; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructorFactory; @Component @TbCoreComponent -public class AlarmMsgConstructorFactory { +public class AlarmMsgConstructorFactory extends MsgConstructorFactory { - @Autowired - protected AlarmMsgConstructorV1 alarmMsgConstructorV1; - - @Autowired - protected AlarmMsgConstructorV2 alarmMsgConstructorV2; - - public AlarmMsgConstructor getMsgConstructorByEdgeVersion(EdgeVersion edgeVersion) { - switch (edgeVersion) { - case V_3_3_0: - case V_3_3_3: - case V_3_4_0: - case V_3_6_0: - case V_3_6_1: - return alarmMsgConstructorV1; - default: - return alarmMsgConstructorV2; - } - } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/AssetMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/AssetMsgConstructor.java index 0223351f54..338375ac37 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/AssetMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/AssetMsgConstructor.java @@ -22,8 +22,9 @@ import org.thingsboard.server.common.data.id.AssetProfileId; import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg; import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructor; -public interface AssetMsgConstructor { +public interface AssetMsgConstructor extends MsgConstructor { AssetUpdateMsg constructAssetUpdatedMsg(UpdateMsgType msgType, Asset asset); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/AssetMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/AssetMsgConstructorFactory.java index d4fa79c6c5..880beb446c 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/AssetMsgConstructorFactory.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/AssetMsgConstructorFactory.java @@ -15,31 +15,12 @@ */ package org.thingsboard.server.service.edge.rpc.constructor.asset; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructorFactory; @Component @TbCoreComponent -public class AssetMsgConstructorFactory { +public class AssetMsgConstructorFactory extends MsgConstructorFactory { - @Autowired - protected AssetMsgConstructorV1 assetMsgConstructorV1; - - @Autowired - protected AssetMsgConstructorV2 assetMsgConstructorV2; - - public AssetMsgConstructor getMsgConstructorByEdgeVersion(EdgeVersion edgeVersion) { - switch (edgeVersion) { - case V_3_3_0: - case V_3_3_3: - case V_3_4_0: - case V_3_6_0: - case V_3_6_1: - return assetMsgConstructorV1; - default: - return assetMsgConstructorV2; - } - } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/CustomerMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/CustomerMsgConstructor.java index fd9139de12..7351822a3f 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/CustomerMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/CustomerMsgConstructor.java @@ -19,8 +19,9 @@ import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.gen.edge.v1.CustomerUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructor; -public interface CustomerMsgConstructor { +public interface CustomerMsgConstructor extends MsgConstructor { CustomerUpdateMsg constructCustomerUpdatedMsg(UpdateMsgType msgType, Customer customer); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/CustomerMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/CustomerMsgConstructorFactory.java index ba8e643445..3e84fba57f 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/CustomerMsgConstructorFactory.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/CustomerMsgConstructorFactory.java @@ -15,31 +15,12 @@ */ package org.thingsboard.server.service.edge.rpc.constructor.customer; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructorFactory; @Component @TbCoreComponent -public class CustomerMsgConstructorFactory { +public class CustomerMsgConstructorFactory extends MsgConstructorFactory { - @Autowired - protected CustomerMsgConstructorV1 customerMsgConstructorV1; - - @Autowired - protected CustomerMsgConstructorV2 customerMsgConstructorV2; - - public CustomerMsgConstructor getMsgConstructorByEdgeVersion(EdgeVersion edgeVersion) { - switch (edgeVersion) { - case V_3_3_0: - case V_3_3_3: - case V_3_4_0: - case V_3_6_0: - case V_3_6_1: - return customerMsgConstructorV1; - default: - return customerMsgConstructorV2; - } - } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/DashboardMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/DashboardMsgConstructor.java index e2ca09b1d3..96504e5036 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/DashboardMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/DashboardMsgConstructor.java @@ -19,8 +19,9 @@ import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructor; -public interface DashboardMsgConstructor { +public interface DashboardMsgConstructor extends MsgConstructor { DashboardUpdateMsg constructDashboardUpdatedMsg(UpdateMsgType msgType, Dashboard dashboard); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/DashboardMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/DashboardMsgConstructorFactory.java index 5e566297fd..384327f5bc 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/DashboardMsgConstructorFactory.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/DashboardMsgConstructorFactory.java @@ -15,31 +15,12 @@ */ package org.thingsboard.server.service.edge.rpc.constructor.dashboard; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructorFactory; @Component @TbCoreComponent -public class DashboardMsgConstructorFactory { +public class DashboardMsgConstructorFactory extends MsgConstructorFactory { - @Autowired - protected DashboardMsgConstructorV1 dashboardMsgConstructorV1; - - @Autowired - protected DashboardMsgConstructorV2 dashboardMsgConstructorV2; - - public DashboardMsgConstructor getMsgConstructorByEdgeVersion(EdgeVersion edgeVersion) { - switch (edgeVersion) { - case V_3_3_0: - case V_3_3_3: - case V_3_4_0: - case V_3_6_0: - case V_3_6_1: - return dashboardMsgConstructorV1; - default: - return dashboardMsgConstructorV2; - } - } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructor.java index 3bd40a1fbb..4636dfd5ae 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructor.java @@ -26,10 +26,11 @@ import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg; import org.thingsboard.server.gen.edge.v1.DeviceRpcCallMsg; import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructor; import java.util.UUID; -public interface DeviceMsgConstructor { +public interface DeviceMsgConstructor extends MsgConstructor { DeviceUpdateMsg constructDeviceUpdatedMsg(UpdateMsgType msgType, Device device); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructorFactory.java index 44e072cb11..b384259e14 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructorFactory.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructorFactory.java @@ -19,27 +19,10 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructorFactory; @Component @TbCoreComponent -public class DeviceMsgConstructorFactory { +public class DeviceMsgConstructorFactory extends MsgConstructorFactory { - @Autowired - protected DeviceMsgConstructorV1 deviceMsgConstructorV1; - - @Autowired - protected DeviceMsgConstructorV2 deviceMsgConstructorV2; - - public DeviceMsgConstructor getMsgConstructorByEdgeVersion(EdgeVersion edgeVersion) { - switch (edgeVersion) { - case V_3_3_0: - case V_3_3_3: - case V_3_4_0: - case V_3_6_0: - case V_3_6_1: - return deviceMsgConstructorV1; - default: - return deviceMsgConstructorV2; - } - } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/EntityViewMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/EntityViewMsgConstructor.java index 60a404669c..7f14f62c68 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/EntityViewMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/EntityViewMsgConstructor.java @@ -19,8 +19,9 @@ import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.gen.edge.v1.EntityViewUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructor; -public interface EntityViewMsgConstructor { +public interface EntityViewMsgConstructor extends MsgConstructor { EntityViewUpdateMsg constructEntityViewUpdatedMsg(UpdateMsgType msgType, EntityView entityView); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/EntityViewMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/EntityViewMsgConstructorFactory.java index 57b19d6a14..849f7f093a 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/EntityViewMsgConstructorFactory.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/EntityViewMsgConstructorFactory.java @@ -15,31 +15,12 @@ */ package org.thingsboard.server.service.edge.rpc.constructor.entityview; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructorFactory; @Component @TbCoreComponent -public class EntityViewMsgConstructorFactory { +public class EntityViewMsgConstructorFactory extends MsgConstructorFactory { - @Autowired - protected EntityViewMsgConstructorV1 entityViewMsgConstructorV1; - - @Autowired - protected EntityViewMsgConstructorV2 entityViewMsgConstructorV2; - - public EntityViewMsgConstructor getMsgConstructorByEdgeVersion(EdgeVersion edgeVersion) { - switch (edgeVersion) { - case V_3_3_0: - case V_3_3_3: - case V_3_4_0: - case V_3_6_0: - case V_3_6_1: - return entityViewMsgConstructorV1; - default: - return entityViewMsgConstructorV2; - } - } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/OtaPackageMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/OtaPackageMsgConstructor.java index 17b6ea8338..500500a7fc 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/OtaPackageMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/OtaPackageMsgConstructor.java @@ -19,8 +19,9 @@ import org.thingsboard.server.common.data.OtaPackage; import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.gen.edge.v1.OtaPackageUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructor; -public interface OtaPackageMsgConstructor { +public interface OtaPackageMsgConstructor extends MsgConstructor { OtaPackageUpdateMsg constructOtaPackageUpdatedMsg(UpdateMsgType msgType, OtaPackage otaPackage); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/OtaPackageMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/OtaPackageMsgConstructorFactory.java index 7e2b9cea7e..fd775d77ff 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/OtaPackageMsgConstructorFactory.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/OtaPackageMsgConstructorFactory.java @@ -19,27 +19,12 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.customer.CustomerMsgConstructorV1; +import org.thingsboard.server.service.edge.rpc.constructor.customer.CustomerMsgConstructorV2; @Component @TbCoreComponent -public class OtaPackageMsgConstructorFactory { +public class OtaPackageMsgConstructorFactory extends MsgConstructorFactory { - @Autowired - protected OtaPackageMsgConstructorV1 otaPackageMsgConstructorV1; - - @Autowired - protected OtaPackageMsgConstructorV2 otaPackageMsgConstructorV2; - - public OtaPackageMsgConstructor getMsgConstructorByEdgeVersion(EdgeVersion edgeVersion) { - switch (edgeVersion) { - case V_3_3_0: - case V_3_3_3: - case V_3_4_0: - case V_3_6_0: - case V_3_6_1: - return otaPackageMsgConstructorV1; - default: - return otaPackageMsgConstructorV2; - } - } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/QueueMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/QueueMsgConstructor.java index a7d0776136..847f19419e 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/QueueMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/QueueMsgConstructor.java @@ -19,8 +19,9 @@ import org.thingsboard.server.common.data.id.QueueId; import org.thingsboard.server.common.data.queue.Queue; import org.thingsboard.server.gen.edge.v1.QueueUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructor; -public interface QueueMsgConstructor { +public interface QueueMsgConstructor extends MsgConstructor { QueueUpdateMsg constructQueueUpdatedMsg(UpdateMsgType msgType, Queue queue); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/QueueMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/QueueMsgConstructorFactory.java index e6502aaff7..0f9a4b9e2e 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/QueueMsgConstructorFactory.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/QueueMsgConstructorFactory.java @@ -15,31 +15,12 @@ */ package org.thingsboard.server.service.edge.rpc.constructor.queue; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructorFactory; @Component @TbCoreComponent -public class QueueMsgConstructorFactory { +public class QueueMsgConstructorFactory extends MsgConstructorFactory { - @Autowired - protected QueueMsgConstructorV1 queueMsgConstructorV1; - - @Autowired - protected QueueMsgConstructorV2 queueMsgConstructorV2; - - public QueueMsgConstructor getMsgConstructorByEdgeVersion(EdgeVersion edgeVersion) { - switch (edgeVersion) { - case V_3_3_0: - case V_3_3_3: - case V_3_4_0: - case V_3_6_0: - case V_3_6_1: - return queueMsgConstructorV1; - default: - return queueMsgConstructorV2; - } - } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/relation/RelationMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/relation/RelationMsgConstructor.java index 10d0a71830..f7fac6ce71 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/relation/RelationMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/relation/RelationMsgConstructor.java @@ -18,8 +18,9 @@ package org.thingsboard.server.service.edge.rpc.constructor.relation; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructor; -public interface RelationMsgConstructor { +public interface RelationMsgConstructor extends MsgConstructor { RelationUpdateMsg constructRelationUpdatedMsg(UpdateMsgType msgType, EntityRelation entityRelation); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/relation/RelationMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/relation/RelationMsgConstructorFactory.java index 52015f38e7..4ec02c5b49 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/relation/RelationMsgConstructorFactory.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/relation/RelationMsgConstructorFactory.java @@ -19,27 +19,12 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.customer.CustomerMsgConstructorV1; +import org.thingsboard.server.service.edge.rpc.constructor.customer.CustomerMsgConstructorV2; @Component @TbCoreComponent -public class RelationMsgConstructorFactory { +public class RelationMsgConstructorFactory extends MsgConstructorFactory { - @Autowired - protected RelationMsgConstructorV1 relationMsgConstructorV1; - - @Autowired - protected RelationMsgConstructorV2 relationMsgConstructorV2; - - public RelationMsgConstructor getMsgConstructorByEdgeVersion(EdgeVersion edgeVersion) { - switch (edgeVersion) { - case V_3_3_0: - case V_3_3_3: - case V_3_4_0: - case V_3_6_0: - case V_3_6_1: - return relationMsgConstructorV1; - default: - return relationMsgConstructorV2; - } - } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/ResourceMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/ResourceMsgConstructor.java index b631e737e2..cfeedced80 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/ResourceMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/ResourceMsgConstructor.java @@ -19,8 +19,9 @@ import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.gen.edge.v1.ResourceUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructor; -public interface ResourceMsgConstructor { +public interface ResourceMsgConstructor extends MsgConstructor { ResourceUpdateMsg constructResourceUpdatedMsg(UpdateMsgType msgType, TbResource tbResource); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/ResourceMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/ResourceMsgConstructorFactory.java index 15b571a098..2c67d8731e 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/ResourceMsgConstructorFactory.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/ResourceMsgConstructorFactory.java @@ -15,31 +15,12 @@ */ package org.thingsboard.server.service.edge.rpc.constructor.resource; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructorFactory; @Component @TbCoreComponent -public class ResourceMsgConstructorFactory { +public class ResourceMsgConstructorFactory extends MsgConstructorFactory { - @Autowired - protected ResourceMsgConstructorV1 resourceMsgConstructorV1; - - @Autowired - protected ResourceMsgConstructorV2 resourceMsgConstructorV2; - - public ResourceMsgConstructor getMsgConstructorByEdgeVersion(EdgeVersion edgeVersion) { - switch (edgeVersion) { - case V_3_3_0: - case V_3_3_3: - case V_3_4_0: - case V_3_6_0: - case V_3_6_1: - return resourceMsgConstructorV1; - default: - return resourceMsgConstructorV2; - } - } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructor.java index c1665ef48d..8ea2ff9a63 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructor.java @@ -23,8 +23,9 @@ import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; import org.thingsboard.server.gen.edge.v1.RuleChainUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructor; -public interface RuleChainMsgConstructor { +public interface RuleChainMsgConstructor extends MsgConstructor { RuleChainUpdateMsg constructRuleChainUpdatedMsg(UpdateMsgType msgType, RuleChain ruleChain, boolean isRoot); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructorFactory.java index 0c169db0f4..2c1a839ab9 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructorFactory.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructorFactory.java @@ -15,31 +15,12 @@ */ package org.thingsboard.server.service.edge.rpc.constructor.rule; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructorFactory; @Component @TbCoreComponent -public class RuleChainMsgConstructorFactory { +public class RuleChainMsgConstructorFactory extends MsgConstructorFactory { - @Autowired - protected RuleChainMsgConstructorV1 ruleChainMsgConstructorV1; - - @Autowired - protected RuleChainMsgConstructorV2 ruleChainMsgConstructorV2; - - public RuleChainMsgConstructor getMsgConstructorByEdgeVersion(EdgeVersion edgeVersion) { - switch (edgeVersion) { - case V_3_3_0: - case V_3_3_3: - case V_3_4_0: - case V_3_6_0: - case V_3_6_1: - return ruleChainMsgConstructorV1; - default: - return ruleChainMsgConstructorV2; - } - } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/settings/AdminSettingsMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/settings/AdminSettingsMsgConstructor.java index 4e5aa7bf54..da4f964218 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/settings/AdminSettingsMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/settings/AdminSettingsMsgConstructor.java @@ -17,8 +17,9 @@ package org.thingsboard.server.service.edge.rpc.constructor.settings; import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.gen.edge.v1.AdminSettingsUpdateMsg; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructor; -public interface AdminSettingsMsgConstructor { +public interface AdminSettingsMsgConstructor extends MsgConstructor { AdminSettingsUpdateMsg constructAdminSettingsUpdateMsg(AdminSettings adminSettings); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/settings/AdminSettingsMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/settings/AdminSettingsMsgConstructorFactory.java index 1dd9be719d..ecf6131e52 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/settings/AdminSettingsMsgConstructorFactory.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/settings/AdminSettingsMsgConstructorFactory.java @@ -15,31 +15,12 @@ */ package org.thingsboard.server.service.edge.rpc.constructor.settings; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructorFactory; @Component @TbCoreComponent -public class AdminSettingsMsgConstructorFactory { +public class AdminSettingsMsgConstructorFactory extends MsgConstructorFactory { - @Autowired - protected AdminSettingsMsgConstructorV1 adminSettingsMsgConstructorV1; - - @Autowired - protected AdminSettingsMsgConstructorV2 adminSettingsMsgConstructorV2; - - public AdminSettingsMsgConstructor getMsgConstructorByEdgeVersion(EdgeVersion edgeVersion) { - switch (edgeVersion) { - case V_3_3_0: - case V_3_3_3: - case V_3_4_0: - case V_3_6_0: - case V_3_6_1: - return adminSettingsMsgConstructorV1; - default: - return adminSettingsMsgConstructorV2; - } - } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructor.java index 612703b3c6..3de654fda6 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructor.java @@ -21,8 +21,9 @@ import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg; import org.thingsboard.server.gen.edge.v1.TenantUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructor; -public interface TenantMsgConstructor { +public interface TenantMsgConstructor extends MsgConstructor { TenantUpdateMsg constructTenantUpdateMsg(UpdateMsgType msgType, Tenant tenant); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/UserMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/UserMsgConstructor.java index e6e2c9572b..3184f8c751 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/UserMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/UserMsgConstructor.java @@ -21,8 +21,9 @@ import org.thingsboard.server.common.data.security.UserCredentials; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.UserCredentialsUpdateMsg; import org.thingsboard.server.gen.edge.v1.UserUpdateMsg; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructor; -public interface UserMsgConstructor { +public interface UserMsgConstructor extends MsgConstructor { UserUpdateMsg constructUserUpdatedMsg(UpdateMsgType msgType, User user); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/UserMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/UserMsgConstructorFactory.java index 2e7f49c0fa..bf1e4197c0 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/UserMsgConstructorFactory.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/UserMsgConstructorFactory.java @@ -15,31 +15,12 @@ */ package org.thingsboard.server.service.edge.rpc.constructor.user; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructorFactory; @Component @TbCoreComponent -public class UserMsgConstructorFactory { +public class UserMsgConstructorFactory extends MsgConstructorFactory { - @Autowired - protected UserMsgConstructorV1 userMsgConstructorV1; - - @Autowired - protected UserMsgConstructorV2 userMsgConstructorV2; - - public UserMsgConstructor getMsgConstructorByEdgeVersion(EdgeVersion edgeVersion) { - switch (edgeVersion) { - case V_3_3_0: - case V_3_3_3: - case V_3_4_0: - case V_3_6_0: - case V_3_6_1: - return userMsgConstructorV1; - default: - return userMsgConstructorV2; - } - } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/WidgetMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/WidgetMsgConstructor.java index 5d2366eef5..544ba39b13 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/WidgetMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/WidgetMsgConstructor.java @@ -23,10 +23,11 @@ import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.WidgetTypeUpdateMsg; import org.thingsboard.server.gen.edge.v1.WidgetsBundleUpdateMsg; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructor; import java.util.List; -public interface WidgetMsgConstructor { +public interface WidgetMsgConstructor extends MsgConstructor { WidgetsBundleUpdateMsg constructWidgetsBundleUpdateMsg(UpdateMsgType msgType, WidgetsBundle widgetsBundle, List widgets); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/WidgetMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/WidgetMsgConstructorFactory.java index b98494f36b..3a963f7041 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/WidgetMsgConstructorFactory.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/WidgetMsgConstructorFactory.java @@ -15,31 +15,12 @@ */ package org.thingsboard.server.service.edge.rpc.constructor.widget; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructorFactory; @Component @TbCoreComponent -public class WidgetMsgConstructorFactory { +public class WidgetMsgConstructorFactory extends MsgConstructorFactory { - @Autowired - protected WidgetMsgConstructorV1 widgetMsgConstructorV1; - - @Autowired - protected WidgetMsgConstructorV2 widgetMsgConstructorV2; - - public WidgetMsgConstructor getMsgConstructorByEdgeVersion(EdgeVersion edgeVersion) { - switch (edgeVersion) { - case V_3_3_0: - case V_3_3_3: - case V_3_4_0: - case V_3_6_0: - case V_3_6_1: - return widgetMsgConstructorV1; - default: - return widgetMsgConstructorV2; - } - } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java index 41c05a80c2..99b8d2c12a 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java @@ -39,6 +39,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg; import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.service.edge.rpc.constructor.alarm.AlarmMsgConstructor; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; @@ -132,13 +133,15 @@ public abstract class BaseAlarmProcessor extends BaseEdgeProcessor { case ALARM_CLEAR: Alarm alarm = alarmService.findAlarmById(tenantId, alarmId); if (alarm != null) { - return alarmMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructAlarmUpdatedMsg(msgType, alarm, findOriginatorEntityName(tenantId, alarm)); + return ((AlarmMsgConstructor) alarmMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)) + .constructAlarmUpdatedMsg(msgType, alarm, findOriginatorEntityName(tenantId, alarm)); } break; case DELETED: Alarm deletedAlarm = JacksonUtil.convertValue(body, Alarm.class); if (deletedAlarm != null) { - return alarmMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructAlarmUpdatedMsg(msgType, deletedAlarm, findOriginatorEntityName(tenantId, deletedAlarm)); + return ((AlarmMsgConstructor) alarmMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)) + .constructAlarmUpdatedMsg(msgType, deletedAlarm, findOriginatorEntityName(tenantId, deletedAlarm)); } } return null; diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessor.java index fb1362a9da..e760a4d73a 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessor.java @@ -41,6 +41,7 @@ import org.thingsboard.server.gen.edge.v1.DownlinkMsg; import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.asset.AssetMsgConstructor; import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; import java.util.UUID; @@ -120,23 +121,24 @@ public class AssetEdgeProcessor extends BaseAssetProcessor { Asset asset = assetService.findAssetById(edgeEvent.getTenantId(), assetId); if (asset != null && !BaseAssetService.TB_SERVICE_QUEUE.equals(asset.getType())) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); - AssetUpdateMsg assetUpdateMsg = - assetMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructAssetUpdatedMsg(msgType, asset); + AssetUpdateMsg assetUpdateMsg = ((AssetMsgConstructor) + assetMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructAssetUpdatedMsg(msgType, asset); DownlinkMsg.Builder builder = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addAssetUpdateMsg(assetUpdateMsg); if (UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE.equals(msgType)) { AssetProfile assetProfile = assetProfileService.findAssetProfileById(edgeEvent.getTenantId(), asset.getAssetProfileId()); assetProfile = checkIfAssetProfileDefaultFieldsAssignedToEdge(edgeEvent.getTenantId(), edgeId, assetProfile, edgeVersion); - builder.addAssetProfileUpdateMsg(assetMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructAssetProfileUpdatedMsg(msgType, assetProfile)); + builder.addAssetProfileUpdateMsg(((AssetMsgConstructor) assetMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)) + .constructAssetProfileUpdatedMsg(msgType, assetProfile)); } downlinkMsg = builder.build(); } break; case DELETED: case UNASSIGNED_FROM_EDGE: - AssetUpdateMsg assetUpdateMsg = - assetMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructAssetDeleteMsg(assetId); + AssetUpdateMsg assetUpdateMsg = ((AssetMsgConstructor) + assetMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructAssetDeleteMsg(assetId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addAssetUpdateMsg(assetUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetProfileEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetProfileEdgeProcessor.java index 76c43b0c01..a18030992d 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetProfileEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetProfileEdgeProcessor.java @@ -40,6 +40,7 @@ import org.thingsboard.server.gen.edge.v1.DownlinkMsg; import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.asset.AssetMsgConstructor; import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; import java.util.UUID; @@ -107,8 +108,8 @@ public class AssetProfileEdgeProcessor extends BaseAssetProfileProcessor { if (assetProfile != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); assetProfile = checkIfAssetProfileDefaultFieldsAssignedToEdge(edgeEvent.getTenantId(), edgeId, assetProfile, edgeVersion); - AssetProfileUpdateMsg assetProfileUpdateMsg = - assetMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructAssetProfileUpdatedMsg(msgType, assetProfile); + AssetProfileUpdateMsg assetProfileUpdateMsg = ((AssetMsgConstructor) + assetMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructAssetProfileUpdatedMsg(msgType, assetProfile); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addAssetProfileUpdateMsg(assetProfileUpdateMsg) @@ -116,8 +117,8 @@ public class AssetProfileEdgeProcessor extends BaseAssetProfileProcessor { } break; case DELETED: - AssetProfileUpdateMsg assetProfileUpdateMsg = - assetMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructAssetProfileDeleteMsg(assetProfileId); + AssetProfileUpdateMsg assetProfileUpdateMsg = ((AssetMsgConstructor) + assetMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructAssetProfileDeleteMsg(assetProfileId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addAssetProfileUpdateMsg(assetProfileUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/customer/CustomerEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/customer/CustomerEdgeProcessor.java index 2f6a979653..9b9d99fcc3 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/customer/CustomerEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/customer/CustomerEdgeProcessor.java @@ -37,6 +37,7 @@ import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.customer.CustomerMsgConstructor; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; import java.util.ArrayList; @@ -57,8 +58,8 @@ public class CustomerEdgeProcessor extends BaseEdgeProcessor { Customer customer = customerService.findCustomerById(edgeEvent.getTenantId(), customerId); if (customer != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); - CustomerUpdateMsg customerUpdateMsg = - customerMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructCustomerUpdatedMsg(msgType, customer); + CustomerUpdateMsg customerUpdateMsg = ((CustomerMsgConstructor) + customerMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructCustomerUpdatedMsg(msgType, customer); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addCustomerUpdateMsg(customerUpdateMsg) @@ -66,8 +67,8 @@ public class CustomerEdgeProcessor extends BaseEdgeProcessor { } break; case DELETED: - CustomerUpdateMsg customerUpdateMsg = - customerMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructCustomerDeleteMsg(customerId); + CustomerUpdateMsg customerUpdateMsg = ((CustomerMsgConstructor) + customerMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructCustomerDeleteMsg(customerId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addCustomerUpdateMsg(customerUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardEdgeProcessor.java index c4679b40b0..3104e1ac35 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardEdgeProcessor.java @@ -35,6 +35,7 @@ import org.thingsboard.server.gen.edge.v1.DownlinkMsg; import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.dashboard.DashboardMsgConstructor; import java.util.Set; import java.util.UUID; @@ -110,8 +111,8 @@ public class DashboardEdgeProcessor extends BaseDashboardProcessor { Dashboard dashboard = dashboardService.findDashboardById(edgeEvent.getTenantId(), dashboardId); if (dashboard != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); - DashboardUpdateMsg dashboardUpdateMsg = - dashboardMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructDashboardUpdatedMsg(msgType, dashboard); + DashboardUpdateMsg dashboardUpdateMsg = ((DashboardMsgConstructor) + dashboardMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructDashboardUpdatedMsg(msgType, dashboard); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addDashboardUpdateMsg(dashboardUpdateMsg) @@ -120,8 +121,8 @@ public class DashboardEdgeProcessor extends BaseDashboardProcessor { break; case DELETED: case UNASSIGNED_FROM_EDGE: - DashboardUpdateMsg dashboardUpdateMsg = - dashboardMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructDashboardDeleteMsg(dashboardId); + DashboardUpdateMsg dashboardUpdateMsg = ((DashboardMsgConstructor) + dashboardMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructDashboardDeleteMsg(dashboardId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addDashboardUpdateMsg(dashboardUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java index 2677004678..b65ef58e1d 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java @@ -55,6 +55,7 @@ import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.TbQueueCallback; import org.thingsboard.server.queue.TbQueueMsgMetadata; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.device.DeviceMsgConstructor; import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; import java.util.UUID; @@ -227,23 +228,26 @@ public class DeviceEdgeProcessor extends BaseDeviceProcessor { Device device = deviceService.findDeviceById(edgeEvent.getTenantId(), deviceId); if (device != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); - DeviceUpdateMsg deviceUpdateMsg = - deviceMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructDeviceUpdatedMsg(msgType, device); + DeviceUpdateMsg deviceUpdateMsg = ((DeviceMsgConstructor) + deviceMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)) + .constructDeviceUpdatedMsg(msgType, device); DownlinkMsg.Builder builder = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addDeviceUpdateMsg(deviceUpdateMsg); if (UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE.equals(msgType)) { DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileById(edgeEvent.getTenantId(), device.getDeviceProfileId()); deviceProfile = checkIfDeviceProfileDefaultFieldsAssignedToEdge(edgeEvent.getTenantId(), edgeId, deviceProfile, edgeVersion); - builder.addDeviceProfileUpdateMsg(deviceMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructDeviceProfileUpdatedMsg(msgType, deviceProfile)); + builder.addDeviceProfileUpdateMsg(((DeviceMsgConstructor) + deviceMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)) + .constructDeviceProfileUpdatedMsg(msgType, deviceProfile)); } downlinkMsg = builder.build(); } break; case DELETED: case UNASSIGNED_FROM_EDGE: - DeviceUpdateMsg deviceUpdateMsg = - deviceMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructDeviceDeleteMsg(deviceId); + DeviceUpdateMsg deviceUpdateMsg = ((DeviceMsgConstructor) + deviceMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructDeviceDeleteMsg(deviceId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addDeviceUpdateMsg(deviceUpdateMsg) @@ -252,8 +256,8 @@ public class DeviceEdgeProcessor extends BaseDeviceProcessor { case CREDENTIALS_UPDATED: DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(edgeEvent.getTenantId(), deviceId); if (deviceCredentials != null) { - DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg = - deviceMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructDeviceCredentialsUpdatedMsg(deviceCredentials); + DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg = ((DeviceMsgConstructor) + deviceMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructDeviceCredentialsUpdatedMsg(deviceCredentials); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addDeviceCredentialsUpdateMsg(deviceCredentialsUpdateMsg) @@ -263,7 +267,9 @@ public class DeviceEdgeProcessor extends BaseDeviceProcessor { case RPC_CALL: return DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) - .addDeviceRpcCallMsg(deviceMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructDeviceRpcCallMsg(edgeEvent.getEntityId(), edgeEvent.getBody())) + .addDeviceRpcCallMsg(((DeviceMsgConstructor) + deviceMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)) + .constructDeviceRpcCallMsg(edgeEvent.getEntityId(), edgeEvent.getBody())) .build(); case CREDENTIALS_REQUEST: return convertCredentialsRequestEventToDownlink(edgeEvent); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceProfileEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceProfileEdgeProcessor.java index 0b8fa1025a..c4105f9668 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceProfileEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceProfileEdgeProcessor.java @@ -40,6 +40,7 @@ import org.thingsboard.server.gen.edge.v1.DownlinkMsg; import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.device.DeviceMsgConstructor; import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; import java.util.UUID; @@ -107,8 +108,8 @@ public class DeviceProfileEdgeProcessor extends BaseDeviceProfileProcessor { if (deviceProfile != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); deviceProfile = checkIfDeviceProfileDefaultFieldsAssignedToEdge(edgeEvent.getTenantId(), edgeId, deviceProfile, edgeVersion); - DeviceProfileUpdateMsg deviceProfileUpdateMsg = - deviceMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructDeviceProfileUpdatedMsg(msgType, deviceProfile); + DeviceProfileUpdateMsg deviceProfileUpdateMsg = ((DeviceMsgConstructor) + deviceMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructDeviceProfileUpdatedMsg(msgType, deviceProfile); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addDeviceProfileUpdateMsg(deviceProfileUpdateMsg) @@ -116,8 +117,8 @@ public class DeviceProfileEdgeProcessor extends BaseDeviceProfileProcessor { } break; case DELETED: - DeviceProfileUpdateMsg deviceProfileUpdateMsg = - deviceMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructDeviceProfileDeleteMsg(deviceProfileId); + DeviceProfileUpdateMsg deviceProfileUpdateMsg = ((DeviceMsgConstructor) + deviceMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructDeviceProfileDeleteMsg(deviceProfileId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addDeviceProfileUpdateMsg(deviceProfileUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewEdgeProcessor.java index 0f1727ab10..863c1112e2 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewEdgeProcessor.java @@ -38,6 +38,7 @@ import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.EntityViewUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.entityview.EntityViewMsgConstructor; import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; import java.util.UUID; @@ -117,8 +118,8 @@ public class EntityViewEdgeProcessor extends BaseEntityViewProcessor { EntityView entityView = entityViewService.findEntityViewById(edgeEvent.getTenantId(), entityViewId); if (entityView != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); - EntityViewUpdateMsg entityViewUpdateMsg = - entityViewMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructEntityViewUpdatedMsg(msgType, entityView); + EntityViewUpdateMsg entityViewUpdateMsg = ((EntityViewMsgConstructor) + entityViewMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructEntityViewUpdatedMsg(msgType, entityView); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addEntityViewUpdateMsg(entityViewUpdateMsg) @@ -127,8 +128,8 @@ public class EntityViewEdgeProcessor extends BaseEntityViewProcessor { break; case DELETED: case UNASSIGNED_FROM_EDGE: - EntityViewUpdateMsg entityViewUpdateMsg = - entityViewMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructEntityViewDeleteMsg(entityViewId); + EntityViewUpdateMsg entityViewUpdateMsg = ((EntityViewMsgConstructor) + entityViewMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructEntityViewDeleteMsg(entityViewId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addEntityViewUpdateMsg(entityViewUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/ota/OtaPackageEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/ota/OtaPackageEdgeProcessor.java index 1b06f323d0..247cfe5c15 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/ota/OtaPackageEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/ota/OtaPackageEdgeProcessor.java @@ -26,6 +26,7 @@ import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.OtaPackageUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.ota.OtaPackageMsgConstructor; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; @Component @@ -42,8 +43,8 @@ public class OtaPackageEdgeProcessor extends BaseEdgeProcessor { OtaPackage otaPackage = otaPackageService.findOtaPackageById(edgeEvent.getTenantId(), otaPackageId); if (otaPackage != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); - OtaPackageUpdateMsg otaPackageUpdateMsg = - otaPackageMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructOtaPackageUpdatedMsg(msgType, otaPackage); + OtaPackageUpdateMsg otaPackageUpdateMsg = ((OtaPackageMsgConstructor) + otaPackageMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructOtaPackageUpdatedMsg(msgType, otaPackage); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addOtaPackageUpdateMsg(otaPackageUpdateMsg) @@ -51,8 +52,8 @@ public class OtaPackageEdgeProcessor extends BaseEdgeProcessor { } break; case DELETED: - OtaPackageUpdateMsg otaPackageUpdateMsg = - otaPackageMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructOtaPackageDeleteMsg(otaPackageId); + OtaPackageUpdateMsg otaPackageUpdateMsg = ((OtaPackageMsgConstructor) + otaPackageMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructOtaPackageDeleteMsg(otaPackageId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addOtaPackageUpdateMsg(otaPackageUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/queue/QueueEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/queue/QueueEdgeProcessor.java index fa35f35151..cd595c1699 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/queue/QueueEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/queue/QueueEdgeProcessor.java @@ -26,6 +26,7 @@ import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.QueueUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.queue.QueueMsgConstructor; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; @Slf4j @@ -42,8 +43,8 @@ public class QueueEdgeProcessor extends BaseEdgeProcessor { Queue queue = queueService.findQueueById(edgeEvent.getTenantId(), queueId); if (queue != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); - QueueUpdateMsg queueUpdateMsg = - queueMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructQueueUpdatedMsg(msgType, queue); + QueueUpdateMsg queueUpdateMsg = ((QueueMsgConstructor) + queueMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructQueueUpdatedMsg(msgType, queue); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addQueueUpdateMsg(queueUpdateMsg) @@ -51,8 +52,8 @@ public class QueueEdgeProcessor extends BaseEdgeProcessor { } break; case DELETED: - QueueUpdateMsg queueDeleteMsg = - queueMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructQueueDeleteMsg(queueId); + QueueUpdateMsg queueDeleteMsg = ((QueueMsgConstructor) + queueMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructQueueDeleteMsg(queueId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addQueueUpdateMsg(queueDeleteMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessor.java index 530ee47982..256d84a1e3 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessor.java @@ -35,6 +35,7 @@ import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.relation.RelationMsgConstructor; import java.util.ArrayList; import java.util.HashSet; @@ -59,7 +60,8 @@ public class RelationEdgeProcessor extends BaseRelationProcessor { public DownlinkMsg convertRelationEventToDownlink(EdgeEvent edgeEvent, EdgeVersion edgeVersion) { EntityRelation entityRelation = JacksonUtil.convertValue(edgeEvent.getBody(), EntityRelation.class); UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); - RelationUpdateMsg relationUpdateMsg = relationMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructRelationUpdatedMsg(msgType, entityRelation); + RelationUpdateMsg relationUpdateMsg = ((RelationMsgConstructor) relationMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)) + .constructRelationUpdatedMsg(msgType, entityRelation); return DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addRelationUpdateMsg(relationUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessor.java index fb811d2112..fd3a9c51f4 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessor.java @@ -33,6 +33,7 @@ import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.ResourceUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.resource.ResourceMsgConstructor; import java.util.UUID; @@ -80,8 +81,8 @@ public class ResourceEdgeProcessor extends BaseResourceProcessor { TbResource tbResource = resourceService.findResourceById(edgeEvent.getTenantId(), tbResourceId); if (tbResource != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); - ResourceUpdateMsg resourceUpdateMsg = - resourceMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructResourceUpdatedMsg(msgType, tbResource); + ResourceUpdateMsg resourceUpdateMsg = ((ResourceMsgConstructor) + resourceMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructResourceUpdatedMsg(msgType, tbResource); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addResourceUpdateMsg(resourceUpdateMsg) @@ -89,8 +90,8 @@ public class ResourceEdgeProcessor extends BaseResourceProcessor { } break; case DELETED: - ResourceUpdateMsg resourceUpdateMsg = - resourceMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructResourceDeleteMsg(tbResourceId); + ResourceUpdateMsg resourceUpdateMsg = ((ResourceMsgConstructor) + resourceMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructResourceDeleteMsg(tbResourceId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addResourceUpdateMsg(resourceUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/rule/RuleChainEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/rule/RuleChainEdgeProcessor.java index b71abd97e0..c17fb8030c 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/rule/RuleChainEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/rule/RuleChainEdgeProcessor.java @@ -28,6 +28,7 @@ import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; import org.thingsboard.server.gen.edge.v1.RuleChainUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.rule.RuleChainMsgConstructor; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; import static org.thingsboard.server.service.edge.DefaultEdgeNotificationService.EDGE_IS_ROOT_BODY_KEY; @@ -53,8 +54,9 @@ public class RuleChainEdgeProcessor extends BaseEdgeProcessor { } catch (Exception ignored) {} } UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); - RuleChainUpdateMsg ruleChainUpdateMsg = - ruleChainMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructRuleChainUpdatedMsg(msgType, ruleChain, isRoot); + RuleChainUpdateMsg ruleChainUpdateMsg = ((RuleChainMsgConstructor) + ruleChainMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)) + .constructRuleChainUpdatedMsg(msgType, ruleChain, isRoot); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addRuleChainUpdateMsg(ruleChainUpdateMsg) @@ -65,7 +67,8 @@ public class RuleChainEdgeProcessor extends BaseEdgeProcessor { case UNASSIGNED_FROM_EDGE: downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) - .addRuleChainUpdateMsg(ruleChainMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructRuleChainDeleteMsg(ruleChainId)) + .addRuleChainUpdateMsg(((RuleChainMsgConstructor) ruleChainMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)) + .constructRuleChainDeleteMsg(ruleChainId)) .build(); break; } @@ -79,8 +82,9 @@ public class RuleChainEdgeProcessor extends BaseEdgeProcessor { if (ruleChain != null) { RuleChainMetaData ruleChainMetaData = ruleChainService.loadRuleChainMetaData(edgeEvent.getTenantId(), ruleChainId); UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); - RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = - ruleChainMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructRuleChainMetadataUpdatedMsg(edgeEvent.getTenantId(), msgType, ruleChainMetaData, edgeVersion); + RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = ((RuleChainMsgConstructor) + ruleChainMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)) + .constructRuleChainMetadataUpdatedMsg(edgeEvent.getTenantId(), msgType, ruleChainMetaData, edgeVersion); if (ruleChainMetadataUpdateMsg != null) { downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/settings/AdminSettingsEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/settings/AdminSettingsEdgeProcessor.java index fa2517f7df..6c201a261c 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/settings/AdminSettingsEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/settings/AdminSettingsEdgeProcessor.java @@ -25,6 +25,7 @@ import org.thingsboard.server.gen.edge.v1.AdminSettingsUpdateMsg; import org.thingsboard.server.gen.edge.v1.DownlinkMsg; import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.settings.AdminSettingsMsgConstructor; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; @Component @@ -37,8 +38,8 @@ public class AdminSettingsEdgeProcessor extends BaseEdgeProcessor { if (adminSettings == null) { return null; } - AdminSettingsUpdateMsg adminSettingsUpdateMsg = - adminSettingsMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructAdminSettingsUpdateMsg(adminSettings); + AdminSettingsUpdateMsg adminSettingsUpdateMsg = ((AdminSettingsMsgConstructor) + adminSettingsMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructAdminSettingsUpdateMsg(adminSettings); return DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addAdminSettingsUpdateMsg(adminSettingsUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantEdgeProcessor.java index b421b1768c..92b887bf33 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantEdgeProcessor.java @@ -29,6 +29,7 @@ import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg; import org.thingsboard.server.gen.edge.v1.TenantUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.tenant.TenantMsgConstructor; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; @Component @@ -43,9 +44,13 @@ public class TenantEdgeProcessor extends BaseEdgeProcessor { Tenant tenant = tenantService.findTenantById(tenantId); if (tenant != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); - TenantUpdateMsg tenantUpdateMsg = tenantMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructTenantUpdateMsg(msgType, tenant); + TenantUpdateMsg tenantUpdateMsg = ((TenantMsgConstructor) + tenantMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)) + .constructTenantUpdateMsg(msgType, tenant); TenantProfile tenantProfile = tenantProfileService.findTenantProfileById(tenantId, tenant.getTenantProfileId()); - TenantProfileUpdateMsg tenantProfileUpdateMsg = tenantMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructTenantProfileUpdateMsg(msgType, tenantProfile, edgeVersion); + TenantProfileUpdateMsg tenantProfileUpdateMsg = ((TenantMsgConstructor) + tenantMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)) + .constructTenantProfileUpdateMsg(msgType, tenantProfile, edgeVersion); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addTenantUpdateMsg(tenantUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantProfileEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantProfileEdgeProcessor.java index 84b51b09b5..57c29e8632 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantProfileEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantProfileEdgeProcessor.java @@ -27,6 +27,7 @@ import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.tenant.TenantMsgConstructor; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; @Component @@ -41,8 +42,9 @@ public class TenantProfileEdgeProcessor extends BaseEdgeProcessor { TenantProfile tenantProfile = tenantProfileService.findTenantProfileById(edgeEvent.getTenantId(), tenantProfileId); if (tenantProfile != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); - TenantProfileUpdateMsg tenantProfileUpdateMsg = - tenantMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructTenantProfileUpdateMsg(msgType, tenantProfile, edgeVersion); + TenantProfileUpdateMsg tenantProfileUpdateMsg = ((TenantMsgConstructor) + tenantMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)) + .constructTenantProfileUpdateMsg(msgType, tenantProfile, edgeVersion); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addTenantProfileUpdateMsg(tenantProfileUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/user/UserEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/user/UserEdgeProcessor.java index 68ac558e39..c07cbc9414 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/user/UserEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/user/UserEdgeProcessor.java @@ -27,6 +27,7 @@ import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.UserCredentialsUpdateMsg; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.user.UserMsgConstructor; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; @Component @@ -45,21 +46,21 @@ public class UserEdgeProcessor extends BaseEdgeProcessor { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) - .addUserUpdateMsg(userMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructUserUpdatedMsg(msgType, user)) + .addUserUpdateMsg(((UserMsgConstructor) userMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructUserUpdatedMsg(msgType, user)) .build(); } break; case DELETED: downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) - .addUserUpdateMsg(userMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructUserDeleteMsg(userId)) + .addUserUpdateMsg(((UserMsgConstructor) userMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructUserDeleteMsg(userId)) .build(); break; case CREDENTIALS_UPDATED: UserCredentials userCredentialsByUserId = userService.findUserCredentialsByUserId(edgeEvent.getTenantId(), userId); if (userCredentialsByUserId != null && userCredentialsByUserId.isEnabled()) { UserCredentialsUpdateMsg userCredentialsUpdateMsg = - userMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructUserCredentialsUpdatedMsg(userCredentialsByUserId); + ((UserMsgConstructor) userMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructUserCredentialsUpdatedMsg(userCredentialsByUserId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addUserCredentialsUpdateMsg(userCredentialsUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetBundleEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetBundleEdgeProcessor.java index 89ebf9513a..28cd3167aa 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetBundleEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetBundleEdgeProcessor.java @@ -26,6 +26,7 @@ import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.WidgetsBundleUpdateMsg; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.widget.WidgetMsgConstructor; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; import java.util.List; @@ -46,7 +47,7 @@ public class WidgetBundleEdgeProcessor extends BaseEdgeProcessor { List widgets = widgetTypeService.findWidgetFqnsByWidgetsBundleId(edgeEvent.getTenantId(), widgetsBundleId); UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); WidgetsBundleUpdateMsg widgetsBundleUpdateMsg = - widgetMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructWidgetsBundleUpdateMsg(msgType, widgetsBundle, widgets); + ((WidgetMsgConstructor) widgetMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructWidgetsBundleUpdateMsg(msgType, widgetsBundle, widgets); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addWidgetsBundleUpdateMsg(widgetsBundleUpdateMsg) @@ -55,7 +56,7 @@ public class WidgetBundleEdgeProcessor extends BaseEdgeProcessor { break; case DELETED: WidgetsBundleUpdateMsg widgetsBundleUpdateMsg = - widgetMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructWidgetsBundleDeleteMsg(widgetsBundleId); + ((WidgetMsgConstructor) widgetMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructWidgetsBundleDeleteMsg(widgetsBundleId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addWidgetsBundleUpdateMsg(widgetsBundleUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetTypeEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetTypeEdgeProcessor.java index 4e611c83f0..40556b5215 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetTypeEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetTypeEdgeProcessor.java @@ -26,6 +26,7 @@ import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.WidgetTypeUpdateMsg; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.widget.WidgetMsgConstructor; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; @Slf4j @@ -43,7 +44,7 @@ public class WidgetTypeEdgeProcessor extends BaseEdgeProcessor { if (widgetTypeDetails != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); WidgetTypeUpdateMsg widgetTypeUpdateMsg = - widgetMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructWidgetTypeUpdateMsg(msgType, widgetTypeDetails, edgeVersion); + ((WidgetMsgConstructor) widgetMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructWidgetTypeUpdateMsg(msgType, widgetTypeDetails, edgeVersion); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addWidgetTypeUpdateMsg(widgetTypeUpdateMsg) @@ -52,7 +53,7 @@ public class WidgetTypeEdgeProcessor extends BaseEdgeProcessor { break; case DELETED: WidgetTypeUpdateMsg widgetTypeUpdateMsg = - widgetMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion).constructWidgetTypeDeleteMsg(widgetTypeId); + ((WidgetMsgConstructor) widgetMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructWidgetTypeDeleteMsg(widgetTypeId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addWidgetTypeUpdateMsg(widgetTypeUpdateMsg) From bda319c3e86e1f1f48aae906021a8d786dbf54ec Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Fri, 24 Nov 2023 18:47:46 +0200 Subject: [PATCH 194/252] UI: Implement gallery image input component. --- ui-ngx/src/app/core/http/image.service.ts | 8 +- .../home/components/home-components.module.ts | 7 +- .../image/gallery-image-input.component.html | 93 ++++++++ .../image/gallery-image-input.component.scss | 180 ++++++++++++++ .../image/gallery-image-input.component.ts | 224 ++++++++++++++++++ .../image/image-gallery.component.html | 27 ++- .../image/image-gallery.component.scss | 33 +++ .../image/image-gallery.component.ts | 60 +++-- .../image/images-in-use-dialog.component.html | 4 +- .../image/images-in-use-dialog.component.ts | 14 +- .../widget/widgets-bundle.component.html | 8 +- .../shared/components/popover.component.ts | 4 +- .../src/app/shared/models/resource.models.ts | 12 +- ui-ngx/src/app/shared/pipe/image.pipe.ts | 16 +- .../assets/locale/locale.constant-en_US.json | 10 +- 15 files changed, 654 insertions(+), 46 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/components/image/gallery-image-input.component.html create mode 100644 ui-ngx/src/app/modules/home/components/image/gallery-image-input.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/image/gallery-image-input.component.ts diff --git a/ui-ngx/src/app/core/http/image.service.ts b/ui-ngx/src/app/core/http/image.service.ts index abce29d9e3..4366d6edda 100644 --- a/ui-ngx/src/app/core/http/image.service.ts +++ b/ui-ngx/src/app/core/http/image.service.ts @@ -80,7 +80,7 @@ export class ImageService { defaultHttpOptionsFromConfig(config)); } - public getImageDataUrl(imageUrl: string, preview = false, asString = false): Observable { + public getImageDataUrl(imageUrl: string, preview = false, asString = false, emptyUrl = NO_IMAGE_DATA_URI): Observable { const parts = imageUrl.split('/'); const key = parts[parts.length - 1]; parts[parts.length - 1] = encodeURIComponent(key); @@ -92,13 +92,13 @@ export class ImageService { switchMap(val => blobToBase64(val).pipe( map((dataUrl) => asString ? dataUrl : this.sanitizer.bypassSecurityTrustUrl(dataUrl)) )), - catchError(() => of(asString ? NO_IMAGE_DATA_URI : this.sanitizer.bypassSecurityTrustUrl(NO_IMAGE_DATA_URI))) + catchError(() => of(asString ? emptyUrl : this.sanitizer.bypassSecurityTrustUrl(emptyUrl))) ); } - public resolveImageUrl(imageUrl: string, preview = false, asString = false): Observable { + public resolveImageUrl(imageUrl: string, preview = false, asString = false, emptyUrl = NO_IMAGE_DATA_URI): Observable { if (isImageResourceUrl(imageUrl)) { - return this.getImageDataUrl(imageUrl, preview, asString); + return this.getImageDataUrl(imageUrl, preview, asString, emptyUrl); } else { return of(asString ? imageUrl : this.sanitizer.bypassSecurityTrustUrl(imageUrl)); } diff --git a/ui-ngx/src/app/modules/home/components/home-components.module.ts b/ui-ngx/src/app/modules/home/components/home-components.module.ts index 75f124f2b9..14fcb6bddc 100644 --- a/ui-ngx/src/app/modules/home/components/home-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/home-components.module.ts @@ -187,6 +187,7 @@ import { UploadImageDialogComponent } from '@home/components/image/upload-image- import { ImageDialogComponent } from '@home/components/image/image-dialog.component'; import { ImagesInUseDialogComponent } from '@home/components/image/images-in-use-dialog.component'; import { ImageReferencesComponent } from '@home/components/image/image-references.component'; +import { GalleryImageInputComponent } from '@home/components/image/gallery-image-input.component'; @NgModule({ declarations: @@ -337,7 +338,8 @@ import { ImageReferencesComponent } from '@home/components/image/image-reference UploadImageDialogComponent, ImageDialogComponent, ImageReferencesComponent, - ImagesInUseDialogComponent + ImagesInUseDialogComponent, + GalleryImageInputComponent ], imports: [ CommonModule, @@ -481,7 +483,8 @@ import { ImageReferencesComponent } from '@home/components/image/image-reference UploadImageDialogComponent, ImageDialogComponent, ImageReferencesComponent, - ImagesInUseDialogComponent + ImagesInUseDialogComponent, + GalleryImageInputComponent ], providers: [ WidgetComponentService, diff --git a/ui-ngx/src/app/modules/home/components/image/gallery-image-input.component.html b/ui-ngx/src/app/modules/home/components/image/gallery-image-input.component.html new file mode 100644 index 0000000000..ce81ce4a1b --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/image/gallery-image-input.component.html @@ -0,0 +1,93 @@ + +
+ +
+
+ +
+
+
+
+
+ +
+ {{ imageResource.title }} +
+
+
{{ imageResource.descriptor.width }}x{{ imageResource.descriptor.height }}
+ +
{{ imageResource.descriptor.size | fileSize }}
+
+
+
+
+ + +
+ +
+
+ + +
+
+
+ + +
{{ (disabled ? 'image.no-image' : 'image.no-image-selected') | translate }}
+
+ + + diff --git a/ui-ngx/src/app/modules/home/components/image/gallery-image-input.component.scss b/ui-ngx/src/app/modules/home/components/image/gallery-image-input.component.scss new file mode 100644 index 0000000000..6b64516816 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/image/gallery-image-input.component.scss @@ -0,0 +1,180 @@ +/** + * Copyright © 2016-2023 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. + */ +$containerHeight: 96px !default; + +:host { + .tb-container { + margin-top: 0; + padding: 0 0 16px; + display: flex; + flex-direction: column; + gap: 8px; + label.tb-title { + display: block; + padding-bottom: 0; + } + } + + .tb-image-select-container { + width: 100%; + height: $containerHeight; + border-radius: 4px; + border: 1px solid rgba(0, 0, 0, 0.12); + display: flex; + align-items: center; + &.disabled { + width: $containerHeight; + .tb-image-container { + width: $containerHeight - 2px; + border-right: none; + } + } + } + + .tb-image-container { + width: $containerHeight - 1px; + height: $containerHeight - 2px; + display: flex; + justify-content: center; + align-items: center; + border-radius: 4px; + border-right: 1px solid rgba(0, 0, 0, 0.12); + background: #fff; + overflow: hidden; + } + + .tb-image-preview { + width: auto; + max-width: $containerHeight - 2px; + height: auto; + max-height: $containerHeight - 2px; + } + + .tb-no-image { + text-align: center; + color: rgba(0, 0, 0, 0.38); + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: 16px; + letter-spacing: 0.4px; + } + + .tb-image-info-container { + display: flex; + flex: 1; + align-self: stretch; + padding: 0 8px; + justify-content: flex-end; + align-items: center; + gap: 4px; + .tb-base64-image-container, .tb-resource-image-container, .tb-external-image-container { + display: flex; + flex: 1; + align-self: stretch; + } + .tb-resource-image-container { + padding: 8px; + justify-content: center; + align-items: flex-start; + flex-direction: column; + gap: 4px; + .tb-resource-image-name { + color: rgba(0, 0, 0, 0.54); + font-size: 12px; + font-style: normal; + font-weight: 500; + line-height: 16px; + letter-spacing: 0.25px; + } + .tb-resource-image-details { + display: flex; + align-items: center; + gap: 8px; + color: rgba(0, 0, 0, 0.38); + font-size: 11px; + font-style: normal; + font-weight: 400; + line-height: 16px; + letter-spacing: 0.017px; + .mat-divider.mat-divider-vertical { + height: 16px; + } + } + &.loading { + align-items: center; + } + } + + .tb-external-image-container { + padding: 16px 8px 0 16px; + flex-direction: column; + align-items: flex-start; + gap: 4px; + .tb-external-link-label { + color: rgba(0, 0, 0, 0.54); + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: 16px; + letter-spacing: 0.4px; + } + .tb-external-link-input-container { + display: flex; + justify-content: flex-end; + align-items: center; + gap: 4px; + align-self: stretch; + .tb-inline-field { + width: 100%; + } + } + } + + .tb-image-clear-btn { + color: rgba(0,0,0,0.38); + } + } + + .tb-image-select-buttons-container { + display: flex; + flex: 1; + align-self: stretch; + padding: 8px; + gap: 8px; + justify-content: center; + align-items: flex-start; + .tb-image-select-button { + width: 100%; + height: 100%; + align-self: stretch; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 4px; + padding: 16px; + line-height: normal; + font-size: 12px; + .mat-icon { + width: 24px; + height: 24px; + font-size: 24px; + margin: 0; + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/image/gallery-image-input.component.ts b/ui-ngx/src/app/modules/home/components/image/gallery-image-input.component.ts new file mode 100644 index 0000000000..527fe28d12 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/image/gallery-image-input.component.ts @@ -0,0 +1,224 @@ +/// +/// Copyright © 2016-2023 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, + forwardRef, + Input, + OnDestroy, + OnInit, + Renderer2, + ViewContainerRef +} from '@angular/core'; +import { PageComponent } from '@shared/components/page.component'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR, } from '@angular/forms'; +import { coerceBoolean } from '@shared/decorators/coercion'; +import { + extractParamsFromImageResourceUrl, + ImageResourceInfo, + isBase64DataImageUrl, + isImageResourceUrl +} from '@shared/models/resource.models'; +import { ImageService } from '@core/http/image.service'; +import { MatButton } from '@angular/material/button'; +import { TbPopoverService } from '@shared/components/popover.service'; +import { ImageGalleryComponent } from '@home/components/image/image-gallery.component'; + +enum ImageLinkType { + none = 'none', + base64 = 'base64', + external = 'external', + resource = 'resource' +} + +@Component({ + selector: 'tb-gallery-image-input', + templateUrl: './gallery-image-input.component.html', + styleUrls: ['./gallery-image-input.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => GalleryImageInputComponent), + multi: true + } + ] +}) +export class GalleryImageInputComponent extends PageComponent implements OnInit, OnDestroy, ControlValueAccessor { + + @Input() + label: string; + + @Input() + @coerceBoolean() + required = false; + + @Input() + disabled: boolean; + + imageUrl: string; + + imageResource: ImageResourceInfo; + + loadingImageResource = false; + + ImageLinkType = ImageLinkType; + + linkType: ImageLinkType = ImageLinkType.none; + + externalLinkControl = new FormControl(null); + + private propagateChange = null; + + constructor(protected store: Store, + private imageService: ImageService, + private cd: ChangeDetectorRef, + private renderer: Renderer2, + private viewContainerRef: ViewContainerRef, + private popoverService: TbPopoverService) { + super(store); + } + + ngOnInit() { + this.externalLinkControl.valueChanges.subscribe((value) => { + if (this.linkType === ImageLinkType.external) { + this.imageUrl = value; + this.updateModel(); + } + }); + } + + ngOnDestroy() { + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (this.disabled) { + this.detectLinkType(); + this.externalLinkControl.disable({emitEvent: false}); + } else { + this.externalLinkControl.enable({emitEvent: false}); + } + } + + writeValue(value: string): void { + if (this.imageUrl !== value) { + this.reset(); + this.imageUrl = value; + this.detectLinkType(); + if (this.linkType === ImageLinkType.resource) { + const params = extractParamsFromImageResourceUrl(value); + this.loadingImageResource = true; + this.imageService.getImageInfo(params.type, params.key, {ignoreLoading: true, ignoreErrors: true}).subscribe( + { + next: (res) => { + this.imageResource = res; + this.loadingImageResource = false; + this.cd.markForCheck(); + }, + error: () => { + this.reset(); + this.loadingImageResource = false; + this.cd.markForCheck(); + } + } + ); + } else if (this.linkType === ImageLinkType.base64) { + this.cd.markForCheck(); + } else if (this.linkType === ImageLinkType.external) { + this.externalLinkControl.setValue(this.imageUrl, {emitEvent: false}); + this.cd.markForCheck(); + } + } + } + + private detectLinkType() { + if (this.imageUrl) { + if (isImageResourceUrl(this.imageUrl)) { + this.linkType = ImageLinkType.resource; + } else if (isBase64DataImageUrl(this.imageUrl)) { + this.linkType = ImageLinkType.base64; + } else { + this.linkType = ImageLinkType.external; + } + } else { + this.linkType = ImageLinkType.none; + } + } + + private updateModel() { + this.cd.markForCheck(); + this.propagateChange(this.imageUrl); + } + + private reset() { + this.linkType = ImageLinkType.none; + this.imageUrl = null; + this.imageResource = null; + this.externalLinkControl.setValue(null, {emitEvent: false}); + } + + clearImage() { + this.reset(); + this.updateModel(); + } + + setLink($event: Event) { + if ($event) { + $event.stopPropagation(); + } + this.linkType = ImageLinkType.external; + } + + toggleGallery($event: Event, browseGalleryButton: MatButton) { + if ($event) { + $event.stopPropagation(); + } + const trigger = browseGalleryButton._elementRef.nativeElement; + if (this.popoverService.hasPopover(trigger)) { + this.popoverService.hidePopover(trigger); + } else { + const ctx: any = { + pageMode: false, + popoverMode: true, + mode: 'grid', + selectionMode: true + }; + const imageGalleryPopover = this.popoverService.displayPopover(trigger, this.renderer, + this.viewContainerRef, ImageGalleryComponent, 'top', true, null, + ctx, + {}, + {}, {}, true); + imageGalleryPopover.tbComponentRef.instance.imageSelected.subscribe((image) => { + imageGalleryPopover.hide(); + this.linkType = ImageLinkType.resource; + this.imageUrl = image.link; + this.imageResource = image; + this.updateModel(); + }); + } + } + +} diff --git a/ui-ngx/src/app/modules/home/components/image/image-gallery.component.html b/ui-ngx/src/app/modules/home/components/image/image-gallery.component.html index 96eb9c7be9..6b46bb1779 100644 --- a/ui-ngx/src/app/modules/home/components/image/image-gallery.component.html +++ b/ui-ngx/src/app/modules/home/components/image/image-gallery.component.html @@ -15,11 +15,11 @@ limitations under the License. --> -
+
+ [fxShow]="!textSearchMode && (mode === 'grid' || dataSource?.selection.isEmpty())" [ngClass.lt-md]="{'multi-row': !isSysAdmin}">
image.gallery @@ -93,7 +93,7 @@ {{ 'image.include-system-images' | translate }} - +
- +
- {{ translate.get('image.selected-images', {count: dataSource.selection.selected.length}) | async }} + {{ translate.get('image.selected-images', {count: dataSource?.selection.selected.length}) | async }} + +
-
+
+
{{ item.title }} diff --git a/ui-ngx/src/app/modules/home/components/image/image-gallery.component.scss b/ui-ngx/src/app/modules/home/components/image/image-gallery.component.scss index 0b7b5e3c65..00b40ff2e1 100644 --- a/ui-ngx/src/app/modules/home/components/image/image-gallery.component.scss +++ b/ui-ngx/src/app/modules/home/components/image/image-gallery.component.scss @@ -13,9 +13,42 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +@import "../../../../../scss/constants"; + $tb-button-selected-color: rgb(255, 110, 64) !default; .tb-images { + + &.tb-popover-mode { + padding-top: 8px; + position: relative; + width: 300px; + height: 500px; + max-width: calc(100vw - 50px); + max-height: calc(100vh - 100px); + @media #{$mat-gt-xs} { + width: 400px; + height: 600px; + } + @media #{$mat-gt-sm} { + width: 500px; + height: 600px; + } + @media #{$mat-gt-md} { + width: 700px; + height: 700px; + } + @media #{$mat-gt-xmd} { + width: 900px; + height: 700px; + } + @media #{$mat-gt-xl} { + width: 1200px; + height: 800px; + } + } + .tb-images-content { width: 100%; height: 100%; diff --git a/ui-ngx/src/app/modules/home/components/image/image-gallery.component.ts b/ui-ngx/src/app/modules/home/components/image/image-gallery.component.ts index 43938f2515..c8d65d2732 100644 --- a/ui-ngx/src/app/modules/home/components/image/image-gallery.component.ts +++ b/ui-ngx/src/app/modules/home/components/image/image-gallery.component.ts @@ -29,10 +29,10 @@ import { AfterViewInit, ChangeDetectorRef, Component, - ElementRef, + ElementRef, EventEmitter, Input, OnDestroy, - OnInit, + OnInit, Output, ViewChild, ViewEncapsulation } from '@angular/core'; @@ -74,6 +74,28 @@ interface GridImagesFilter { includeSystemImages: boolean; } +const pageGridColumns: ScrollGridColumns = { + columns: 2, + breakpoints: { + 'screen and (min-width: 2320px)': 10, + 'screen and (min-width: 2000px)': 8, + 'gt-lg': 7, + 'screen and (min-width: 1600px)': 6, + 'gt-md': 5, + 'screen and (min-width: 1120px)': 4, + 'gt-xs': 3 + } +}; + +const popoverGridColumns: ScrollGridColumns = { + columns: 2, + breakpoints: { + 'gt-lg': 5, + 'screen and (min-width: 1600px)': 4, + 'gt-md': 3 + } +}; + @Component({ selector: 'tb-image-gallery', templateUrl: './image-gallery.component.html', @@ -86,9 +108,19 @@ export class ImageGalleryComponent extends PageComponent implements OnInit, OnDe @coerceBoolean() pageMode = true; + @Input() + @coerceBoolean() + popoverMode = false; + @Input() mode: 'list' | 'grid' = 'list'; + @Input() + selectionMode = false; + + @Output() + imageSelected = new EventEmitter(); + @ViewChild('searchInput') searchInputField: ElementRef; @ViewChild(MatPaginator) paginator: MatPaginator; @@ -111,18 +143,7 @@ export class ImageGalleryComponent extends PageComponent implements OnInit, OnDe textSearch = this.fb.control('', {nonNullable: true}); includeSystemImages = this.fb.control(false); - gridColumns: ScrollGridColumns = { - columns: 2, - breakpoints: { - 'screen and (min-width: 2320px)': 10, - 'screen and (min-width: 2000px)': 8, - 'gt-lg': 7, - 'screen and (min-width: 1600px)': 6, - 'gt-md': 5, - 'screen and (min-width: 1120px)': 4, - 'gt-xs': 3 - } - }; + gridColumns: ScrollGridColumns; gridImagesFetchFunction: GridEntitiesFetchFunction; gridImagesFilter: GridImagesFilter = { @@ -166,6 +187,7 @@ export class ImageGalleryComponent extends PageComponent implements OnInit, OnDe } ngOnInit(): void { + this.gridColumns = this.popoverMode ? popoverGridColumns : pageGridColumns; this.displayedColumns = this.computeDisplayedColumns(); let sortOrder: SortOrder = this.defaultSortOrder; this.pageSizeOptions = [this.defaultPageSize, this.defaultPageSize * 2, this.defaultPageSize * 3]; @@ -285,6 +307,9 @@ export class ImageGalleryComponent extends PageComponent implements OnInit, OnDe } private computeDisplayedColumns(): string[] { + if (this.selectionMode) { + return ['preview', 'title', 'imageSelect']; + } const columns = ['select', 'preview', 'title', 'createdTime', 'resolution', 'size']; if (!this.isSysAdmin && this.includeSystemImages.value) { columns.push('system'); @@ -567,6 +592,13 @@ export class ImageGalleryComponent extends PageComponent implements OnInit, OnDe }); } + selectImage($event, image: ImageResourceInfo) { + if ($event) { + $event.stopPropagation(); + } + this.imageSelected.next(image); + } + uploadImage(): void { this.dialog.open(UploadImageDialogComponent, { diff --git a/ui-ngx/src/app/modules/home/components/image/images-in-use-dialog.component.html b/ui-ngx/src/app/modules/home/components/image/images-in-use-dialog.component.html index 12690094b2..8739f7c9b2 100644 --- a/ui-ngx/src/app/modules/home/components/image/images-in-use-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/image/images-in-use-dialog.component.html @@ -15,9 +15,9 @@ limitations under the License. --> -

{{title | translate}}

+

{{title}}

-
+
diff --git a/ui-ngx/src/app/modules/home/components/image/images-in-use-dialog.component.ts b/ui-ngx/src/app/modules/home/components/image/images-in-use-dialog.component.ts index 9e1e7708c9..2211f7e071 100644 --- a/ui-ngx/src/app/modules/home/components/image/images-in-use-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/image/images-in-use-dialog.component.ts @@ -60,12 +60,12 @@ export class ImagesInUseDialogComponent extends ngOnInit(): void { if (this.data.multiple) { - this.title = 'image.images-are-in-use'; - this.message = 'image.images-are-in-use-text'; + this.title = this.translate.instant('image.images-are-in-use'); + this.message = this.translate.instant('image.images-are-in-use-text'); this.dataSource = new ImagesDatasource(null, this.data.images, entity => true); } else { - this.title = 'image.image-is-in-use'; - this.message = 'image.image-is-in-use-text'; + this.title = this.translate.instant('image.image-is-in-use'); + this.message = this.translate.instant('image.image-is-in-use-text', {title: this.data.images[0].title}); this.references = this.data.images[0].references; } } @@ -97,8 +97,10 @@ export class ImagesInUseDialogComponent extends }, {}, {}, {}, false, visible => { - const addClasses = visible ? 'mdc-button--unelevated mat-mdc-unelevated-button' : 'mdc-button--outlined mat-mdc-outlined-button'; - const removeClasses = visible ? 'mdc-button--outlined mat-mdc-outlined-button' : 'mdc-button--unelevated mat-mdc-unelevated-button'; + const addClasses = + visible ? 'mdc-button--unelevated mat-mdc-unelevated-button' : 'mdc-button--outlined mat-mdc-outlined-button'; + const removeClasses = + visible ? 'mdc-button--outlined mat-mdc-outlined-button' : 'mdc-button--unelevated mat-mdc-unelevated-button'; addClasses.split(' ').forEach(clazz => { this.renderer.addClass(trigger, clazz); }); diff --git a/ui-ngx/src/app/modules/home/pages/widget/widgets-bundle.component.html b/ui-ngx/src/app/modules/home/pages/widget/widgets-bundle.component.html index 3e324a646f..89d122eccd 100644 --- a/ui-ngx/src/app/modules/home/pages/widget/widgets-bundle.component.html +++ b/ui-ngx/src/app/modules/home/pages/widget/widgets-bundle.component.html @@ -54,11 +54,15 @@ {{ 'widgets-bundle.title-max-length' | translate }} - + + widgets-bundle.description diff --git a/ui-ngx/src/app/shared/components/popover.component.ts b/ui-ngx/src/app/shared/components/popover.component.ts index 27705c4802..bd9df1b898 100644 --- a/ui-ngx/src/app/shared/components/popover.component.ts +++ b/ui-ngx/src/app/shared/components/popover.component.ts @@ -44,7 +44,7 @@ import { CdkConnectedOverlay, CdkOverlayOrigin, ConnectedOverlayPositionChange, - ConnectionPositionPair + ConnectionPositionPair, NoopScrollStrategy } from '@angular/cdk/overlay'; import { Subject, Subscription } from 'rxjs'; import { @@ -311,6 +311,7 @@ export class TbPopoverDirective implements OnChanges, OnDestroy, AfterViewInit { [cdkConnectedOverlayHasBackdrop]="hasBackdrop" [cdkConnectedOverlayOrigin]="origin" [cdkConnectedOverlayPositions]="positions" + [cdkConnectedOverlayScrollStrategy]="scrollStrategy" [cdkConnectedOverlayOpen]="visible" [cdkConnectedOverlayPush]="true" (overlayOutsideClick)="onClickOutside($event)" @@ -451,6 +452,7 @@ export class TbPopoverComponent implements OnDestroy, OnInit { public dir: Direction = 'ltr'; classMap: { [klass: string]: any } = {}; positions: ConnectionPositionPair[] = [...DEFAULT_POPOVER_POSITIONS]; + scrollStrategy = new NoopScrollStrategy(); private parentScrollSubscription: Subscription = null; private intersectionObserver = new IntersectionObserver((entries) => { if (this.lastIsIntersecting !== entries[0].isIntersecting) { diff --git a/ui-ngx/src/app/shared/models/resource.models.ts b/ui-ngx/src/app/shared/models/resource.models.ts index da6cc197a5..b019c064ea 100644 --- a/ui-ngx/src/app/shared/models/resource.models.ts +++ b/ui-ngx/src/app/shared/models/resource.models.ts @@ -127,8 +127,18 @@ export const toImageDeleteResult = (image: ImageResourceInfo, e?: any): ImageDel export const imageResourceType = (imageInfo: ImageResourceInfo): ImageResourceType => (!imageInfo.tenantId || imageInfo.tenantId?.id === NULL_UUID) ? 'system' : 'tenant'; +export const IMAGES_URL_REGEXP = /\/api\/images\/(tenant|system)\/(.*)/; export const IMAGES_URL_PREFIX = '/api/images'; -export const isImageResourceUrl = (url: string): boolean => url && url.startsWith(IMAGES_URL_PREFIX); +export const IMAGE_BASE64_URL_PREFIX = 'data:image/'; + +export const isImageResourceUrl = (url: string): boolean => url && IMAGES_URL_REGEXP.test(url); + +export const extractParamsFromImageResourceUrl = (url: string): {type: ImageResourceType; key: string} => { + const res = url.match(IMAGES_URL_REGEXP); + return {type: res[1] as ImageResourceType, key: res[2]}; +}; + +export const isBase64DataImageUrl = (url: string): boolean => url && url.startsWith(IMAGE_BASE64_URL_PREFIX); export const NO_IMAGE_DATA_URI = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=='; diff --git a/ui-ngx/src/app/shared/pipe/image.pipe.ts b/ui-ngx/src/app/shared/pipe/image.pipe.ts index 6cad747676..e1467fe40e 100644 --- a/ui-ngx/src/app/shared/pipe/image.pipe.ts +++ b/ui-ngx/src/app/shared/pipe/image.pipe.ts @@ -40,18 +40,22 @@ export class ImagePipe implements PipeTransform { transform(urlData: string | UrlHolder, args?: any): Observable { const ignoreLoadingImage = !!args?.ignoreLoadingImage; const asString = !!args?.asString; + const emptyUrl = args?.emptyUrl || NO_IMAGE_DATA_URI; const image$ = ignoreLoadingImage ? new Subject() : new BehaviorSubject(LOADING_IMAGE_DATA_URI); const url = (typeof urlData === 'string') ? urlData : urlData?.url; if (isDefinedAndNotNull(url)) { const preview = !!args?.preview; - this.imageService.resolveImageUrl(url, preview, asString).subscribe((imageUrl) => { - image$.next(imageUrl); - image$.complete(); + this.imageService.resolveImageUrl(url, preview, asString, emptyUrl).subscribe((imageUrl) => { + Promise.resolve().then(() => { + image$.next(imageUrl); + image$.complete(); + }); }); } else { - const emptyUrl = args?.emptyUrl || NO_IMAGE_DATA_URI; - image$.next(asString ? emptyUrl : this.sanitizer.bypassSecurityTrustUrl(emptyUrl)); - image$.complete(); + Promise.resolve().then(() => { + image$.next(asString ? emptyUrl : this.sanitizer.bypassSecurityTrustUrl(emptyUrl)); + image$.complete(); + }); } return image$.asObservable(); } 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 9bd9ef140c..f29faeaffd 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2976,13 +2976,19 @@ "invalid-image-json-file-error": "Unable to import image from JSON: Invalid image JSON data structure.", "image-is-in-use": "Image is used by other entities", "images-are-in-use": "Images are used by other entities", - "image-is-in-use-text": "The image was not deleted because it is used by the following entities:", + "image-is-in-use-text": "The image '{{title}}' was not deleted because it is used by the following entities:", "images-are-in-use-text": "Not all images have been deleted because they are used by other entities.
You can view referenced entities by clicking the References button in the corresponding image row.
If you still want to delete these images, select them in the table below and click the Delete selected button.", "delete-image-in-use-text": "If you still want to delete the image, click the Delete anyway button.", "system-entities": "System entities:", "entities": "entities:", "references": "References", - "include-system-images": "Include system images" + "include-system-images": "Include system images", + "clear-image": "Clear image", + "no-image": "No image", + "no-image-selected": "No image selected", + "browse-from-gallery": "Browse from gallery", + "set-link": "Set link", + "image-link": "Image link" }, "image-input": { "drop-images-or": "Drag and drop an images or", From 248bcefa809f86ccf9aa6003419585f28c204df0 Mon Sep 17 00:00:00 2001 From: nick Date: Sat, 25 Nov 2023 19:39:58 +0200 Subject: [PATCH 195/252] stringToBytes: refactoring, Input parameter Object as HexString, DigString, Integer, Byte --- .../thingsboard/script/api/tbel/TbUtils.java | 18 +++++- .../script/api/tbel/TbUtilsTest.java | 57 +++++++++++++++++++ 2 files changed, 72 insertions(+), 3 deletions(-) diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java index 31bd634221..07c2baee2d 100644 --- a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java +++ b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java @@ -182,7 +182,7 @@ public class TbUtils { return TbJson.parse(ctx, jsonStr); } - public static String bytesToString(List bytesList) { + public static String bytesToString(List bytesList) { byte[] bytes = bytesFromList(bytesList); return new String(bytes); } @@ -210,10 +210,22 @@ public class TbUtils { } } - private static byte[] bytesFromList(List bytesList) { + private static byte[] bytesFromList(List bytesList) { byte[] bytes = new byte[bytesList.size()]; for (int i = 0; i < bytesList.size(); i++) { - bytes[i] = bytesList.get(i); + if (bytesList.get(i) instanceof Integer) { + byte val = ((Integer) bytesList.get(i)).byteValue(); + if (((Integer) bytesList.get(i)).intValue() > 255 || ((Integer) bytesList.get(i)).intValue() < -128){ + throw new NumberFormatException("The value " + bytesList.get(i) + " could not be converted to a byte. Integer to byte needs only 8-bits (min/max = -128/127 or 0/255) !"); + } else { + bytes[i] = val; } + } else if (bytesList.get(i) instanceof String) { + bytes[i] = parseInt((String) bytesList.get(i)).byteValue(); + } else if (bytesList.get(i) instanceof Byte) { + bytes[i] = (byte) bytesList.get(i); + } else { + throw new NumberFormatException("Value \"" + bytesList.get(i) + " could not be converted to a byte. Must be format HexDecimal/String/Integer !"); + } } return bytes; } diff --git a/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbUtilsTest.java b/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbUtilsTest.java index d2900d6ec4..b80ac85ca7 100644 --- a/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbUtilsTest.java +++ b/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbUtilsTest.java @@ -31,6 +31,7 @@ import java.io.IOException; import java.math.BigInteger; import java.nio.ByteBuffer; import java.util.ArrayList; +import java.util.Arrays; import java.util.Calendar; import java.util.List; import java.util.Random; @@ -385,6 +386,62 @@ public class TbUtilsTest { Assert.assertThrows(IllegalAccessException.class, () -> TbUtils.stringToBytes(ctx, ((ExecutionHashMap) finalInputJson).get("hello"), "UTF-8")); } + @Test + public void bytesFromList() { + byte[] arrayBytes = {(byte)0x00, (byte)0x08, (byte)0x10, (byte)0x1C, (byte)0xFF, (byte)0xFC, (byte)0xAD, (byte)0x88, (byte)0x75, (byte)0x74, (byte)0x8A, (byte)0x82}; + Object[] arrayMix = { "0x00", 8, "16", "0x1C", 255, 252, 173, 136, 117, 116, -118, "-126"}; + + String expected = new String(arrayBytes); + ArrayList listBytes = new ArrayList<>(arrayBytes.length); + for (Byte element : arrayBytes) { + listBytes.add(element); + } + Assert.assertEquals(expected, TbUtils.bytesToString(listBytes)); + + ArrayList listMix = new ArrayList<>(arrayMix.length); + for (Object element : arrayMix) { + listMix.add(element); + } + Assert.assertEquals(expected, TbUtils.bytesToString(listMix)); + } + + @Test + public void bytesFromList_Error() { + List listHex = new ArrayList<>(); + listHex.add("0xFG"); + try { + TbUtils.bytesToString(listHex); + Assert.fail("Should throw NumberFormatException"); + } catch (NumberFormatException e) { + Assert.assertTrue(e.getMessage().contains("Failed radix: [16] for value: \"FG\"!")); + } + listHex.add(0, "1F"); + try { + TbUtils.bytesToString(listHex); + Assert.fail("Should throw NumberFormatException"); + } catch (NumberFormatException e) { + Assert.assertTrue(e.getMessage().contains("Failed radix: [10] for value: \"1F\"!")); + } + + ArrayList listIntBytes = new ArrayList<>(); + listIntBytes.add(-129); + try { + TbUtils.bytesToString(listIntBytes); + Assert.fail("Should throw NumberFormatException"); + } catch (NumberFormatException e) { + Assert.assertTrue(e.getMessage().contains("The value -129 could not be converted to a byte. Integer to byte needs only 8-bits (min/max = -128/127 or 0/255")); + } + + listIntBytes.add(0, 256); + try { + TbUtils.bytesToString(listIntBytes); + Assert.fail("Should throw NumberFormatException"); + } catch (NumberFormatException e) { + Assert.assertTrue(e.getMessage().contains("The value 256 could not be converted to a byte. Integer to byte needs only 8-bits (min/max = -128/127 or 0/255")); + } + } + + private static List toList(byte[] data) { List result = new ArrayList<>(data.length); for (Byte b : data) { From baa684f72b59b9a584a4ed8e003c14d30480ba64 Mon Sep 17 00:00:00 2001 From: nick Date: Sun, 26 Nov 2023 21:58:29 +0200 Subject: [PATCH 196/252] stringToBytes: refactoring test --- .../test/java/org/thingsboard/script/api/tbel/TbUtilsTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbUtilsTest.java b/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbUtilsTest.java index b80ac85ca7..3a3fe22000 100644 --- a/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbUtilsTest.java +++ b/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbUtilsTest.java @@ -389,7 +389,7 @@ public class TbUtilsTest { @Test public void bytesFromList() { byte[] arrayBytes = {(byte)0x00, (byte)0x08, (byte)0x10, (byte)0x1C, (byte)0xFF, (byte)0xFC, (byte)0xAD, (byte)0x88, (byte)0x75, (byte)0x74, (byte)0x8A, (byte)0x82}; - Object[] arrayMix = { "0x00", 8, "16", "0x1C", 255, 252, 173, 136, 117, 116, -118, "-126"}; + Object[] arrayMix = { "0x00", 8, "16", "0x1C", 255, (byte)0xFC, 173, 136, 117, 116, -118, "-126"}; String expected = new String(arrayBytes); ArrayList listBytes = new ArrayList<>(arrayBytes.length); From 3ab4c8d468b2986d7884cd12694921991832fb95 Mon Sep 17 00:00:00 2001 From: nick Date: Sun, 26 Nov 2023 22:28:50 +0200 Subject: [PATCH 197/252] stringToBytes: refactoring --- .../src/main/java/org/thingsboard/script/api/tbel/TbUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java index 07c2baee2d..8fde4cd332 100644 --- a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java +++ b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java @@ -187,7 +187,7 @@ public class TbUtils { return new String(bytes); } - public static String bytesToString(List bytesList, String charsetName) throws UnsupportedEncodingException { + public static String bytesToString(List bytesList, String charsetName) throws UnsupportedEncodingException { byte[] bytes = bytesFromList(bytesList); return new String(bytes, charsetName); } From e8ba1e17ebfc3dfb6b74ca2bc14bb2018ac4ccae Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Mon, 27 Nov 2023 11:42:54 +0200 Subject: [PATCH 198/252] Monitoring for IPs associated with the domain --- .../monitoring/config/MonitoringTarget.java | 4 ++ .../transport/TransportMonitoringConfig.java | 3 +- .../transport/TransportMonitoringTarget.java | 1 + .../service/BaseMonitoringService.java | 39 ++++++++++++++++--- .../TransportsMonitoringService.java | 7 ++++ .../src/main/resources/tb-monitoring.yml | 16 ++++++-- 6 files changed, 59 insertions(+), 11 deletions(-) diff --git a/monitoring/src/main/java/org/thingsboard/monitoring/config/MonitoringTarget.java b/monitoring/src/main/java/org/thingsboard/monitoring/config/MonitoringTarget.java index 0e62670f81..0176b14d54 100644 --- a/monitoring/src/main/java/org/thingsboard/monitoring/config/MonitoringTarget.java +++ b/monitoring/src/main/java/org/thingsboard/monitoring/config/MonitoringTarget.java @@ -21,4 +21,8 @@ public interface MonitoringTarget { UUID getDeviceId(); + String getBaseUrl(); + + boolean isCheckDomainIps(); + } diff --git a/monitoring/src/main/java/org/thingsboard/monitoring/config/transport/TransportMonitoringConfig.java b/monitoring/src/main/java/org/thingsboard/monitoring/config/transport/TransportMonitoringConfig.java index 77d702f779..c5f843192c 100644 --- a/monitoring/src/main/java/org/thingsboard/monitoring/config/transport/TransportMonitoringConfig.java +++ b/monitoring/src/main/java/org/thingsboard/monitoring/config/transport/TransportMonitoringConfig.java @@ -23,9 +23,8 @@ import java.util.List; @Data public abstract class TransportMonitoringConfig implements MonitoringConfig { - private int requestTimeoutMs; - private List targets; + private int requestTimeoutMs; public abstract TransportType getTransportType(); diff --git a/monitoring/src/main/java/org/thingsboard/monitoring/config/transport/TransportMonitoringTarget.java b/monitoring/src/main/java/org/thingsboard/monitoring/config/transport/TransportMonitoringTarget.java index 816f64fbce..e3277af0ff 100644 --- a/monitoring/src/main/java/org/thingsboard/monitoring/config/transport/TransportMonitoringTarget.java +++ b/monitoring/src/main/java/org/thingsboard/monitoring/config/transport/TransportMonitoringTarget.java @@ -25,6 +25,7 @@ public class TransportMonitoringTarget implements MonitoringTarget { private String baseUrl; private DeviceConfig device; // set manually during initialization + private boolean checkDomainIps; @Override public UUID getDeviceId() { diff --git a/monitoring/src/main/java/org/thingsboard/monitoring/service/BaseMonitoringService.java b/monitoring/src/main/java/org/thingsboard/monitoring/service/BaseMonitoringService.java index def53557db..24ca9de4d4 100644 --- a/monitoring/src/main/java/org/thingsboard/monitoring/service/BaseMonitoringService.java +++ b/monitoring/src/main/java/org/thingsboard/monitoring/service/BaseMonitoringService.java @@ -15,6 +15,7 @@ */ package org.thingsboard.monitoring.service; +import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; @@ -29,9 +30,14 @@ import org.thingsboard.monitoring.service.transport.TransportHealthChecker; import org.thingsboard.monitoring.util.TbStopWatch; import javax.annotation.PostConstruct; +import java.net.InetAddress; +import java.net.URI; +import java.util.Arrays; import java.util.LinkedList; import java.util.List; +import java.util.Set; import java.util.UUID; +import java.util.stream.Collectors; @Slf4j public abstract class BaseMonitoringService, T extends MonitoringTarget> { @@ -60,15 +66,36 @@ public abstract class BaseMonitoringService, T ext tbClient.logIn(); configs.forEach(config -> { config.getTargets().forEach(target -> { - BaseHealthChecker healthChecker = (BaseHealthChecker) createHealthChecker(config, target); - log.info("Initializing {}", healthChecker.getClass().getSimpleName()); - healthChecker.initialize(tbClient); - devices.add(target.getDeviceId()); - healthCheckers.add(healthChecker); + initHealthChecker(target, config); + if (target.isCheckDomainIps()) { + initIpsHealthCheckers(target, config); + } }); }); } + private void initHealthChecker(T target, C config) { + BaseHealthChecker healthChecker = (BaseHealthChecker) createHealthChecker(config, target); + log.info("Initializing {} for {}", healthChecker.getClass().getSimpleName(), target.getBaseUrl()); + healthChecker.initialize(tbClient); + devices.add(target.getDeviceId()); + healthCheckers.add(healthChecker); + } + + @SneakyThrows + private void initIpsHealthCheckers(T target, C config) { + URI baseUrl = new URI(target.getBaseUrl()); + String domain = baseUrl.getHost(); + + Set ips = Arrays.stream(InetAddress.getAllByName(domain)) + .map(InetAddress::getHostAddress) + .collect(Collectors.toSet()); + for (String ip : ips) { + String url = new URI(baseUrl.getScheme(), null, ip, baseUrl.getPort(), "", null, null).toString(); + initHealthChecker(createTarget(url), config); + } + } + public final void runChecks() { if (healthCheckers.isEmpty()) { return; @@ -99,6 +126,8 @@ public abstract class BaseMonitoringService, T ext protected abstract BaseHealthChecker createHealthChecker(C config, T target); + protected abstract T createTarget(String baseUrl); + protected abstract String getName(); } diff --git a/monitoring/src/main/java/org/thingsboard/monitoring/service/transport/TransportsMonitoringService.java b/monitoring/src/main/java/org/thingsboard/monitoring/service/transport/TransportsMonitoringService.java index b3ce86e799..ca7c5a94da 100644 --- a/monitoring/src/main/java/org/thingsboard/monitoring/service/transport/TransportsMonitoringService.java +++ b/monitoring/src/main/java/org/thingsboard/monitoring/service/transport/TransportsMonitoringService.java @@ -33,6 +33,13 @@ public final class TransportsMonitoringService extends BaseMonitoringService Date: Mon, 27 Nov 2023 11:43:18 +0200 Subject: [PATCH 199/252] updated test to cover sysadmin authority --- .../controller/WidgetTypeControllerTest.java | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/WidgetTypeControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/WidgetTypeControllerTest.java index 553b0d6490..e515250321 100644 --- a/application/src/test/java/org/thingsboard/server/controller/WidgetTypeControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/WidgetTypeControllerTest.java @@ -193,17 +193,29 @@ public class WidgetTypeControllerTest extends AbstractControllerTest { loginCustomerUser(); - List loadedWidgetTypes2 = doGetTyped("/api/widgetTypes?widgetsBundleId={widgetsBundleId}", + List loadedWidgetTypesCustomer = doGetTyped("/api/widgetTypes?widgetsBundleId={widgetsBundleId}", new TypeReference<>(){}, widgetsBundle.getId().getId().toString()); - Collections.sort(loadedWidgetTypes2, idComparator); - Assert.assertEquals(widgetTypes, loadedWidgetTypes2); + Collections.sort(loadedWidgetTypesCustomer, idComparator); + Assert.assertEquals(widgetTypes, loadedWidgetTypesCustomer); - List loadedWidgetTypes3 = doGetTyped("/api/widgetTypesDetails?widgetsBundleId={widgetsBundleId}", + List customerLoadedWidgetTypesDetails = doGetTyped("/api/widgetTypesDetails?widgetsBundleId={widgetsBundleId}", new TypeReference<>(){}, widgetsBundle.getId().getId().toString()); - List widgetTypes3 = loadedWidgetTypes3.stream().map(WidgetType::new).collect(Collectors.toList()); - Collections.sort(widgetTypes3, idComparator); - Assert.assertEquals(widgetTypes3, loadedWidgetTypes); + List widgetTypesFromDetailsListCustomer = customerLoadedWidgetTypesDetails.stream().map(WidgetType::new).collect(Collectors.toList()); + Collections.sort(widgetTypesFromDetailsListCustomer, idComparator); + Assert.assertEquals(widgetTypesFromDetailsListCustomer, loadedWidgetTypes); + loginSysAdmin(); + + List sysAdminLoadedWidgetTypes = doGetTyped("/api/widgetTypes?widgetsBundleId={widgetsBundleId}", + new TypeReference<>(){}, widgetsBundle.getId().getId().toString()); + Collections.sort(sysAdminLoadedWidgetTypes, idComparator); + Assert.assertEquals(widgetTypes, sysAdminLoadedWidgetTypes); + + List sysAdminLoadedWidgetTypesDetails = doGetTyped("/api/widgetTypesDetails?widgetsBundleId={widgetsBundleId}", + new TypeReference<>(){}, widgetsBundle.getId().getId().toString()); + List widgetTypesFromDetailsListSysAdmin = sysAdminLoadedWidgetTypesDetails.stream().map(WidgetType::new).collect(Collectors.toList()); + Collections.sort(widgetTypesFromDetailsListSysAdmin, idComparator); + Assert.assertEquals(widgetTypesFromDetailsListSysAdmin, loadedWidgetTypes); } @Test From 24241010b72f6f4965d96b44be7239f99d7257db Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Mon, 27 Nov 2023 11:47:28 +0200 Subject: [PATCH 200/252] Root Rule Chain for monitoring tenant --- .../src/main/resources/root_rule_chain.json | 436 ++++++++++++++++++ 1 file changed, 436 insertions(+) create mode 100644 monitoring/src/main/resources/root_rule_chain.json diff --git a/monitoring/src/main/resources/root_rule_chain.json b/monitoring/src/main/resources/root_rule_chain.json new file mode 100644 index 0000000000..ed8a93fb63 --- /dev/null +++ b/monitoring/src/main/resources/root_rule_chain.json @@ -0,0 +1,436 @@ +{ + "ruleChain": { + "additionalInfo": null, + "name": "Root Rule Chain", + "type": "CORE", + "firstRuleNodeId": null, + "root": false, + "debugMode": false, + "configuration": null, + "externalId": null + }, + "metadata": { + "firstNodeIndex": 12, + "nodes": [ + { + "additionalInfo": { + "description": null, + "layoutX": 1202, + "layoutY": 221 + }, + "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode", + "name": "Save Timeseries", + "debugMode": true, + "singletonMode": false, + "configurationVersion": 0, + "configuration": { + "defaultTTL": 0 + }, + "externalId": null + }, + { + "additionalInfo": { + "layoutX": 1000, + "layoutY": 167 + }, + "type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode", + "name": "Save Attributes", + "debugMode": false, + "singletonMode": false, + "configurationVersion": 1, + "configuration": { + "scope": "CLIENT_SCOPE", + "notifyDevice": false, + "sendAttributesUpdatedNotification": false, + "updateAttributesOnlyOnValueChange": false + }, + "externalId": null + }, + { + "additionalInfo": { + "layoutX": 566, + "layoutY": 302 + }, + "type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode", + "name": "Message Type Switch", + "debugMode": false, + "singletonMode": false, + "configurationVersion": 0, + "configuration": { + "version": 0 + }, + "externalId": null + }, + { + "additionalInfo": { + "layoutX": 1000, + "layoutY": 381 + }, + "type": "org.thingsboard.rule.engine.action.TbLogNode", + "name": "Log RPC from Device", + "debugMode": false, + "singletonMode": false, + "configurationVersion": 0, + "configuration": { + "scriptLang": "TBEL", + "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);", + "tbelScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);" + }, + "externalId": null + }, + { + "additionalInfo": { + "layoutX": 1000, + "layoutY": 494 + }, + "type": "org.thingsboard.rule.engine.action.TbLogNode", + "name": "Log Other", + "debugMode": false, + "singletonMode": false, + "configurationVersion": 0, + "configuration": { + "scriptLang": "TBEL", + "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);", + "tbelScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);" + }, + "externalId": null + }, + { + "additionalInfo": { + "layoutX": 1000, + "layoutY": 583 + }, + "type": "org.thingsboard.rule.engine.rpc.TbSendRPCRequestNode", + "name": "RPC Call Request", + "debugMode": false, + "singletonMode": false, + "configurationVersion": 0, + "configuration": { + "timeoutInSeconds": 60 + }, + "externalId": null + }, + { + "additionalInfo": { + "layoutX": 255, + "layoutY": 301 + }, + "type": "org.thingsboard.rule.engine.filter.TbOriginatorTypeFilterNode", + "name": "Is Entity Group", + "debugMode": false, + "singletonMode": false, + "configurationVersion": 0, + "configuration": { + "originatorTypes": [ + "ENTITY_GROUP" + ] + }, + "externalId": null + }, + { + "additionalInfo": { + "layoutX": 319, + "layoutY": 109 + }, + "type": "org.thingsboard.rule.engine.filter.TbMsgTypeFilterNode", + "name": "Post attributes or RPC request", + "debugMode": false, + "singletonMode": false, + "configurationVersion": 0, + "configuration": { + "messageTypes": [ + "POST_ATTRIBUTES_REQUEST", + "RPC_CALL_FROM_SERVER_TO_DEVICE" + ] + }, + "externalId": null + }, + { + "additionalInfo": { + "layoutX": 627, + "layoutY": 108 + }, + "type": "org.thingsboard.rule.engine.transform.TbDuplicateMsgToGroupNode", + "name": "Duplicate To Group Entities", + "debugMode": false, + "singletonMode": false, + "configurationVersion": 0, + "configuration": { + "entityGroupId": null, + "entityGroupIsMessageOriginator": true + }, + "externalId": null + }, + { + "additionalInfo": { + "description": "Process incoming messages from devices with the alarm rules defined in the device profile. Dispatch all incoming messages with \"Success\" relation type.", + "layoutX": 45, + "layoutY": 359 + }, + "type": "org.thingsboard.rule.engine.profile.TbDeviceProfileNode", + "name": "Device Profile Node", + "debugMode": true, + "singletonMode": false, + "configurationVersion": 0, + "configuration": { + "persistAlarmRulesState": false, + "fetchAlarmRulesStateOnStart": false + }, + "externalId": null + }, + { + "additionalInfo": { + "description": "", + "layoutX": 160, + "layoutY": 631 + }, + "type": "org.thingsboard.rule.engine.filter.TbJsFilterNode", + "name": "Test JS script", + "debugMode": false, + "singletonMode": false, + "configurationVersion": 0, + "configuration": { + "scriptLang": "JS", + "jsScript": "var test = {\n a: 'a',\n b: 'b'\n};\nreturn test.a === 'a' && test.b === 'b';", + "tbelScript": "return msg.temperature > 20;" + }, + "externalId": null + }, + { + "additionalInfo": { + "description": "", + "layoutX": 427, + "layoutY": 541 + }, + "type": "org.thingsboard.rule.engine.filter.TbJsFilterNode", + "name": "Test TBEL script", + "debugMode": false, + "singletonMode": false, + "configurationVersion": 0, + "configuration": { + "scriptLang": "TBEL", + "jsScript": "return msg.temperature > 20;", + "tbelScript": "var a = \"a\";\nvar b = \"b\";\nreturn a.equals(\"a\") && b.equals(\"b\");" + }, + "externalId": null + }, + { + "additionalInfo": { + "description": "", + "layoutX": 40, + "layoutY": 252 + }, + "type": "org.thingsboard.rule.engine.transform.TbTransformMsgNode", + "name": "Add arrival timestamp", + "debugMode": false, + "singletonMode": false, + "configurationVersion": 0, + "configuration": { + "scriptLang": "TBEL", + "jsScript": "return {msg: msg, metadata: metadata, msgType: msgType};", + "tbelScript": "metadata.arrivalTs = Date.now();\nreturn {msg: msg, metadata: metadata, msgType: msgType};" + }, + "externalId": null + }, + { + "additionalInfo": { + "description": "", + "layoutX": 1467, + "layoutY": 267 + }, + "type": "org.thingsboard.rule.engine.transform.TbTransformMsgNode", + "name": "Calculate additional latencies", + "debugMode": true, + "singletonMode": false, + "configurationVersion": 0, + "configuration": { + "scriptLang": "TBEL", + "jsScript": "return {msg: msg, metadata: metadata, msgType: msgType};", + "tbelScript": "var arrivalLatency = metadata.arrivalTs - metadata.ts;\nvar processingTime = Date.now() - metadata.arrivalTs;\nmsg = {\n arrivalLatency: arrivalLatency,\n processingTime: processingTime\n};\nreturn {msg: msg, metadata: metadata, msgType: msgType};" + }, + "externalId": null + }, + { + "additionalInfo": { + "description": "", + "layoutX": 1438, + "layoutY": 403 + }, + "type": "org.thingsboard.rule.engine.transform.TbChangeOriginatorNode", + "name": "To latencies asset", + "debugMode": false, + "singletonMode": false, + "configurationVersion": 0, + "configuration": { + "originatorSource": "ENTITY", + "entityType": "ASSET", + "entityNamePattern": "[Monitoring] Latencies", + "relationsQuery": { + "direction": "FROM", + "maxLevel": 1, + "filters": [ + { + "relationType": "Contains", + "entityTypes": [] + } + ], + "fetchLastLevelOnly": false + } + }, + "externalId": null + }, + { + "additionalInfo": { + "description": null, + "layoutX": 1458, + "layoutY": 505 + }, + "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode", + "name": "Save Timeseries", + "debugMode": true, + "singletonMode": false, + "configurationVersion": 0, + "configuration": { + "defaultTTL": 0 + }, + "externalId": null + }, + { + "additionalInfo": { + "description": "", + "layoutX": 928, + "layoutY": 266 + }, + "type": "org.thingsboard.rule.engine.filter.TbCheckMessageNode", + "name": "Has testData", + "debugMode": false, + "singletonMode": false, + "configurationVersion": 0, + "configuration": { + "messageNames": [ + "testData" + ], + "metadataNames": [], + "checkAllKeys": true + }, + "externalId": null + }, + { + "additionalInfo": { + "description": null, + "layoutX": 1203, + "layoutY": 327 + }, + "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode", + "name": "Save Timeseries with TTL", + "debugMode": true, + "singletonMode": false, + "configurationVersion": 0, + "configuration": { + "defaultTTL": 180, + "skipLatestPersistence": null, + "useServerTs": null + }, + "externalId": null + } + ], + "connections": [ + { + "fromIndex": 2, + "toIndex": 1, + "type": "Post attributes" + }, + { + "fromIndex": 2, + "toIndex": 3, + "type": "RPC Request from Device" + }, + { + "fromIndex": 2, + "toIndex": 4, + "type": "Other" + }, + { + "fromIndex": 2, + "toIndex": 5, + "type": "RPC Request to Device" + }, + { + "fromIndex": 2, + "toIndex": 16, + "type": "Post telemetry" + }, + { + "fromIndex": 6, + "toIndex": 2, + "type": "False" + }, + { + "fromIndex": 6, + "toIndex": 7, + "type": "True" + }, + { + "fromIndex": 7, + "toIndex": 2, + "type": "False" + }, + { + "fromIndex": 7, + "toIndex": 8, + "type": "True" + }, + { + "fromIndex": 8, + "toIndex": 2, + "type": "Success" + }, + { + "fromIndex": 9, + "toIndex": 10, + "type": "Success" + }, + { + "fromIndex": 10, + "toIndex": 11, + "type": "True" + }, + { + "fromIndex": 11, + "toIndex": 6, + "type": "True" + }, + { + "fromIndex": 12, + "toIndex": 9, + "type": "Success" + }, + { + "fromIndex": 13, + "toIndex": 14, + "type": "Success" + }, + { + "fromIndex": 14, + "toIndex": 15, + "type": "Success" + }, + { + "fromIndex": 16, + "toIndex": 0, + "type": "False" + }, + { + "fromIndex": 16, + "toIndex": 17, + "type": "True" + }, + { + "fromIndex": 17, + "toIndex": 13, + "type": "Success" + } + ], + "ruleChainConnections": null + } +} \ No newline at end of file From 98a87bfd33aa3261119759be757f8c070cfbf11b Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Mon, 27 Nov 2023 14:01:23 +0200 Subject: [PATCH 201/252] Monitoring: support for dynamic change of load-balancers list --- .../monitoring/service/BaseHealthChecker.java | 13 ++- .../service/BaseMonitoringService.java | 83 ++++++++++++++----- 2 files changed, 75 insertions(+), 21 deletions(-) diff --git a/monitoring/src/main/java/org/thingsboard/monitoring/service/BaseHealthChecker.java b/monitoring/src/main/java/org/thingsboard/monitoring/service/BaseHealthChecker.java index 697573289d..290fd307c6 100644 --- a/monitoring/src/main/java/org/thingsboard/monitoring/service/BaseHealthChecker.java +++ b/monitoring/src/main/java/org/thingsboard/monitoring/service/BaseHealthChecker.java @@ -15,6 +15,7 @@ */ package org.thingsboard.monitoring.service; +import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -30,13 +31,17 @@ import org.thingsboard.monitoring.util.TbStopWatch; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; +import java.util.HashMap; +import java.util.Map; import java.util.UUID; @RequiredArgsConstructor @Slf4j public abstract class BaseHealthChecker { + @Getter protected final C config; + @Getter protected final T target; private Object info; @@ -48,6 +53,9 @@ public abstract class BaseHealthChecker> associates = new HashMap<>(); + public static final String TEST_TELEMETRY_KEY = "testData"; @PostConstruct @@ -84,6 +92,10 @@ public abstract class BaseHealthChecker { + healthChecker.check(wsClient); + }); } private void checkWsUpdate(WsClient wsClient, String testValue) { @@ -99,7 +111,6 @@ public abstract class BaseHealthChecker, T ext tbClient.logIn(); configs.forEach(config -> { config.getTargets().forEach(target -> { - initHealthChecker(target, config); + BaseHealthChecker healthChecker = initHealthChecker(target, config); + healthCheckers.add(healthChecker); + if (target.isCheckDomainIps()) { - initIpsHealthCheckers(target, config); + getAssociatedUrls(target.getBaseUrl()).forEach(url -> { + healthChecker.getAssociates().put(url, initHealthChecker(createTarget(url), config)); + }); } }); }); } - private void initHealthChecker(T target, C config) { + private BaseHealthChecker initHealthChecker(T target, C config) { BaseHealthChecker healthChecker = (BaseHealthChecker) createHealthChecker(config, target); log.info("Initializing {} for {}", healthChecker.getClass().getSimpleName(), target.getBaseUrl()); healthChecker.initialize(tbClient); devices.add(target.getDeviceId()); - healthCheckers.add(healthChecker); - } - - @SneakyThrows - private void initIpsHealthCheckers(T target, C config) { - URI baseUrl = new URI(target.getBaseUrl()); - String domain = baseUrl.getHost(); - - Set ips = Arrays.stream(InetAddress.getAllByName(domain)) - .map(InetAddress::getHostAddress) - .collect(Collectors.toSet()); - for (String ip : ips) { - String url = new URI(baseUrl.getScheme(), null, ip, baseUrl.getPort(), "", null, null).toString(); - initHealthChecker(createTarget(url), config); - } + return healthChecker; } public final void runChecks() { @@ -108,9 +101,8 @@ public abstract class BaseMonitoringService, T ext try (WsClient wsClient = wsClientFactory.createClient(accessToken)) { wsClient.subscribeForTelemetry(devices, TransportHealthChecker.TEST_TELEMETRY_KEY).waitForReply(); - for (BaseHealthChecker healthChecker : healthCheckers) { - healthChecker.check(wsClient); + check(healthChecker, wsClient); } } reporter.reportLatencies(tbClient); @@ -124,6 +116,57 @@ public abstract class BaseMonitoringService, T ext } } + private void check(BaseHealthChecker healthChecker, WsClient wsClient) throws Exception { + healthChecker.check(wsClient); + + T target = healthChecker.getTarget(); + if (target.isCheckDomainIps()) { + Set associatedUrls = getAssociatedUrls(target.getBaseUrl()); + Map> associates = healthChecker.getAssociates(); + Set prevAssociatedUrls = new HashSet<>(associates.keySet()); + + boolean changed = false; + for (String url : associatedUrls) { + if (!prevAssociatedUrls.contains(url)) { + BaseHealthChecker associate = initHealthChecker(createTarget(url), healthChecker.getConfig()); + associates.put(url, associate); + changed = true; + } + } + for (String url : prevAssociatedUrls) { + if (!associatedUrls.contains(url)) { + stopHealthChecker(healthChecker); + associates.remove(url); + changed = true; + } + } + if (changed) { + log.info("Updated IPs for {}: {} (old list: {})", target.getBaseUrl(), associatedUrls, prevAssociatedUrls); + } + } + } + + @SneakyThrows + private Set getAssociatedUrls(String baseUrl) { + URI url = new URI(baseUrl); + return Arrays.stream(InetAddress.getAllByName(url.getHost())) + .map(InetAddress::getHostAddress) + .map(ip -> { + try { + return new URI(url.getScheme(), null, ip, url.getPort(), "", null, null).toString(); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + }) + .collect(Collectors.toSet()); + } + + private void stopHealthChecker(BaseHealthChecker healthChecker) throws Exception { + healthChecker.destroyClient(); + devices.remove(healthChecker.getTarget().getDeviceId()); + log.info("Stopped {} for {}", healthChecker.getClass().getSimpleName(), healthChecker.getTarget().getBaseUrl()); + } + protected abstract BaseHealthChecker createHealthChecker(C config, T target); protected abstract T createTarget(String baseUrl); From cf942478b3783bb4bb24a53d23f21c85d7842816 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Mon, 27 Nov 2023 14:56:22 +0200 Subject: [PATCH 202/252] UI: Improve image references component style. Improve browse image gallery popup. --- .../image/gallery-image-input.component.scss | 13 ++- .../image/gallery-image-input.component.ts | 16 ++-- .../image/image-gallery.component.html | 8 +- .../image/image-gallery.component.scss | 14 ++- .../image/image-gallery.component.ts | 73 +++++++++------ .../image/image-references.component.html | 27 +++--- .../image/image-references.component.scss | 92 +++++++++++++++++++ .../image/image-references.component.ts | 2 +- .../image/images-in-use-dialog.component.scss | 1 + ui-ngx/src/form.scss | 1 + 10 files changed, 190 insertions(+), 57 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/components/image/image-references.component.scss diff --git a/ui-ngx/src/app/modules/home/components/image/gallery-image-input.component.scss b/ui-ngx/src/app/modules/home/components/image/gallery-image-input.component.scss index 6b64516816..77b3aaec7a 100644 --- a/ui-ngx/src/app/modules/home/components/image/gallery-image-input.component.scss +++ b/ui-ngx/src/app/modules/home/components/image/gallery-image-input.component.scss @@ -13,6 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +@import "../../../../../scss/constants"; + $containerHeight: 96px !default; :host { @@ -94,6 +97,11 @@ $containerHeight: 96px !default; gap: 4px; .tb-resource-image-name { color: rgba(0, 0, 0, 0.54); + overflow: hidden; + text-overflow: ellipsis; + -webkit-line-clamp: 2; + display: -webkit-box; + -webkit-box-orient: vertical; font-size: 12px; font-style: normal; font-weight: 500; @@ -166,9 +174,12 @@ $containerHeight: 96px !default; justify-content: center; align-items: center; gap: 4px; - padding: 16px; + padding: 8px; line-height: normal; font-size: 12px; + @media #{$mat-gt-xs} { + padding: 16px; + } .mat-icon { width: 24px; height: 24px; diff --git a/ui-ngx/src/app/modules/home/components/image/gallery-image-input.component.ts b/ui-ngx/src/app/modules/home/components/image/gallery-image-input.component.ts index 527fe28d12..e0e4166918 100644 --- a/ui-ngx/src/app/modules/home/components/image/gallery-image-input.component.ts +++ b/ui-ngx/src/app/modules/home/components/image/gallery-image-input.component.ts @@ -97,8 +97,7 @@ export class GalleryImageInputComponent extends PageComponent implements OnInit, ngOnInit() { this.externalLinkControl.valueChanges.subscribe((value) => { if (this.linkType === ImageLinkType.external) { - this.imageUrl = value; - this.updateModel(); + this.updateModel(value); } }); } @@ -168,21 +167,23 @@ export class GalleryImageInputComponent extends PageComponent implements OnInit, } } - private updateModel() { + private updateModel(value: string) { this.cd.markForCheck(); - this.propagateChange(this.imageUrl); + if (this.imageUrl !== value) { + this.imageUrl = value; + this.propagateChange(this.imageUrl); + } } private reset() { this.linkType = ImageLinkType.none; - this.imageUrl = null; this.imageResource = null; this.externalLinkControl.setValue(null, {emitEvent: false}); } clearImage() { this.reset(); - this.updateModel(); + this.updateModel(null); } setLink($event: Event) { @@ -214,9 +215,8 @@ export class GalleryImageInputComponent extends PageComponent implements OnInit, imageGalleryPopover.tbComponentRef.instance.imageSelected.subscribe((image) => { imageGalleryPopover.hide(); this.linkType = ImageLinkType.resource; - this.imageUrl = image.link; this.imageResource = image; - this.updateModel(); + this.updateModel(image.link); }); } } diff --git a/ui-ngx/src/app/modules/home/components/image/image-gallery.component.html b/ui-ngx/src/app/modules/home/components/image/image-gallery.component.html index 6b46bb1779..882f411dd9 100644 --- a/ui-ngx/src/app/modules/home/components/image/image-gallery.component.html +++ b/ui-ngx/src/app/modules/home/components/image/image-gallery.component.html @@ -45,7 +45,8 @@ {{ 'image.include-system-images' | translate }} + [ngModel]="includeSystemImages" + (ngModelChange)="includeSystemImagesChanged($event)">{{ 'image.include-system-images' | translate }}
@@ -91,7 +92,8 @@
{{ 'image.include-system-images' | translate }} + [ngModel]="includeSystemImages" + (ngModelChange)="includeSystemImagesChanged($event)">{{ 'image.include-system-images' | translate }}
@@ -259,7 +261,7 @@ + *matRowDef="let image; columns: displayedColumns;" (click)="rowClick($event, image)">
diff --git a/ui-ngx/src/app/modules/home/components/image/image-gallery.component.scss b/ui-ngx/src/app/modules/home/components/image/image-gallery.component.scss index 00b40ff2e1..e82fcc84b1 100644 --- a/ui-ngx/src/app/modules/home/components/image/image-gallery.component.scss +++ b/ui-ngx/src/app/modules/home/components/image/image-gallery.component.scss @@ -28,23 +28,27 @@ $tb-button-selected-color: rgb(255, 110, 64) !default; max-width: calc(100vw - 50px); max-height: calc(100vh - 100px); @media #{$mat-gt-xs} { - width: 400px; + width: 500px; height: 600px; } @media #{$mat-gt-sm} { - width: 500px; + width: 700px; height: 600px; } @media #{$mat-gt-md} { - width: 700px; + width: 1000px; height: 700px; } @media #{$mat-gt-xmd} { - width: 900px; + width: 1300px; height: 700px; } @media #{$mat-gt-xl} { - width: 1200px; + width: 1600px; + height: 800px; + } + @media screen and (min-width: 2320px) { + width: 2000px; height: 800px; } } diff --git a/ui-ngx/src/app/modules/home/components/image/image-gallery.component.ts b/ui-ngx/src/app/modules/home/components/image/image-gallery.component.ts index c8d65d2732..0bad2f1f42 100644 --- a/ui-ngx/src/app/modules/home/components/image/image-gallery.component.ts +++ b/ui-ngx/src/app/modules/home/components/image/image-gallery.component.ts @@ -88,11 +88,14 @@ const pageGridColumns: ScrollGridColumns = { }; const popoverGridColumns: ScrollGridColumns = { - columns: 2, + columns: 1, breakpoints: { - 'gt-lg': 5, - 'screen and (min-width: 1600px)': 4, - 'gt-md': 3 + 'screen and (min-width: 2320px)': 8, + 'gt-lg': 6, + 'screen and (min-width: 1600px)': 5, + 'gt-md': 4, + 'gt-sm': 3, + 'gt-xs': 2 } }; @@ -141,7 +144,7 @@ export class ImageGalleryComponent extends PageComponent implements OnInit, OnDe dataSource: ImagesDatasource; textSearch = this.fb.control('', {nonNullable: true}); - includeSystemImages = this.fb.control(false); + includeSystemImages = false; gridColumns: ScrollGridColumns; @@ -257,29 +260,29 @@ export class ImageGalleryComponent extends PageComponent implements OnInit, OnDe } else { this.gridImagesFilter = { search: isNotEmptyStr(value) ? encodeURI(value) : null, - includeSystemImages: this.includeSystemImages.value + includeSystemImages: this.includeSystemImages }; this.cd.markForCheck(); } }); - this.includeSystemImages.valueChanges.pipe( - takeUntil(this.destroy$) - ).subscribe(value => { - this.displayedColumns = this.computeDisplayedColumns(); - this.gridImagesFilter = { - search: this.gridImagesFilter.search, - includeSystemImages: value - }; - if (this.mode === 'list') { - this.paginator.pageIndex = 0; - this.updateData(); - } else { - this.cd.markForCheck(); - } - }); this.updateMode(); } + public includeSystemImagesChanged(value: boolean) { + this.includeSystemImages = value; + this.displayedColumns = this.computeDisplayedColumns(); + this.gridImagesFilter = { + search: this.gridImagesFilter.search, + includeSystemImages: this.includeSystemImages + }; + if (this.mode === 'list') { + this.paginator.pageIndex = 0; + this.updateData(); + } else { + this.cd.markForCheck(); + } + } + public setMode(targetMode: 'list' | 'grid') { if (this.mode !== targetMode) { if (this.widgetResize$) { @@ -307,14 +310,20 @@ export class ImageGalleryComponent extends PageComponent implements OnInit, OnDe } private computeDisplayedColumns(): string[] { + let columns: string[]; if (this.selectionMode) { - return ['preview', 'title', 'imageSelect']; - } - const columns = ['select', 'preview', 'title', 'createdTime', 'resolution', 'size']; - if (!this.isSysAdmin && this.includeSystemImages.value) { - columns.push('system'); + columns = ['preview', 'title']; + if (!this.isSysAdmin && this.includeSystemImages) { + columns.push('system'); + } + columns.push('imageSelect'); + } else { + columns = ['select', 'preview', 'title', 'createdTime', 'resolution', 'size']; + if (!this.isSysAdmin && this.includeSystemImages) { + columns.push('system'); + } + columns.push('actions'); } - columns.push('actions'); return columns; } @@ -411,7 +420,7 @@ export class ImageGalleryComponent extends PageComponent implements OnInit, OnDe } else { this.pageLink.sortOrder = null; } - this.dataSource.loadEntities(this.pageLink, this.includeSystemImages.value); + this.dataSource.loadEntities(this.pageLink, this.includeSystemImages); } else { this.gridComponent.update(); } @@ -599,6 +608,14 @@ export class ImageGalleryComponent extends PageComponent implements OnInit, OnDe this.imageSelected.next(image); } + rowClick($event, image: ImageResourceInfo) { + if (this.selectionMode) { + this.selectImage($event, image); + } else { + this.dataSource.selection.toggle(image); + } + } + uploadImage(): void { this.dialog.open(UploadImageDialogComponent, { diff --git a/ui-ngx/src/app/modules/home/components/image/image-references.component.html b/ui-ngx/src/app/modules/home/components/image/image-references.component.html index fd8fd29fbe..611d744cfa 100644 --- a/ui-ngx/src/app/modules/home/components/image/image-references.component.html +++ b/ui-ngx/src/app/modules/home/components/image/image-references.component.html @@ -20,15 +20,17 @@ -