From 2f85f7037aeea8562979ee591bdb032642ef8402 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Mon, 27 Apr 2020 12:56:58 +0300 Subject: [PATCH 01/52] Visit only for SYSTEM rule chains --- .../server/actors/shared/rulechain/RuleChainManager.java | 3 ++- .../org/thingsboard/server/actors/tenant/TenantActor.java | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/rulechain/RuleChainManager.java b/application/src/main/java/org/thingsboard/server/actors/shared/rulechain/RuleChainManager.java index ef2b4282d4..aa47cc1769 100644 --- a/application/src/main/java/org/thingsboard/server/actors/shared/rulechain/RuleChainManager.java +++ b/application/src/main/java/org/thingsboard/server/actors/shared/rulechain/RuleChainManager.java @@ -24,6 +24,7 @@ import org.thingsboard.server.actors.ruleChain.RuleChainActor; import org.thingsboard.server.actors.shared.EntityActorsManager; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.dao.rule.RuleChainService; /** @@ -50,7 +51,7 @@ public abstract class RuleChainManager extends EntityActorsManager Date: Mon, 27 Apr 2020 13:53:51 +0300 Subject: [PATCH 02/52] Push credentials updates to edge --- .../server/controller/BaseController.java | 5 +++++ .../constructor/DeviceUpdateMsgConstructor.java | 15 ++++++++++++++- .../thingsboard/server/dao/edge/EdgeService.java | 2 -- common/edge-api/src/main/proto/edge.proto | 3 +++ 4 files changed, 22 insertions(+), 3 deletions(-) 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 64b1f8c12b..86f6b2b023 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -565,6 +565,7 @@ public abstract class BaseController { } if (e == null) { pushEntityActionToRuleEngine(entityId, entity, user, customerId, actionType, additionalInfo); + // TODO: voba - refactor to push events to edge queue directly, instead of the rule engine flow } auditLogService.logEntityAction(user.getTenantId(), customerId, user.getId(), user.getName(), entityId, entity, actionType, e, additionalInfo); } @@ -611,6 +612,10 @@ public abstract class BaseController { case UNASSIGNED_FROM_EDGE: msgType = DataConstants.ENTITY_UNASSIGNED_FROM_EDGE; break; + case CREDENTIALS_UPDATED: + //TODO: voba - this is not efficient way to do this. Refactor on later stages + msgType = DataConstants.ENTITY_UPDATED; + break; } if (!StringUtils.isEmpty(msgType)) { try { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceUpdateMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceUpdateMsgConstructor.java index ff4835d609..dce2be491d 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceUpdateMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceUpdateMsgConstructor.java @@ -16,8 +16,11 @@ package org.thingsboard.server.service.edge.rpc.constructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.security.DeviceCredentials; +import org.thingsboard.server.dao.device.DeviceCredentialsService; import org.thingsboard.server.gen.edge.DeviceUpdateMsg; import org.thingsboard.server.gen.edge.UpdateMsgType; @@ -25,11 +28,21 @@ import org.thingsboard.server.gen.edge.UpdateMsgType; @Slf4j public class DeviceUpdateMsgConstructor { + @Autowired + private DeviceCredentialsService deviceCredentialsService; + public DeviceUpdateMsg constructDeviceUpdatedMsg(UpdateMsgType msgType, Device device) { + DeviceCredentials deviceCredentials + = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getTenantId(), device.getId()); DeviceUpdateMsg.Builder builder = DeviceUpdateMsg.newBuilder() .setMsgType(msgType) .setName(device.getName()) - .setType(device.getType()); + .setType(device.getType()) + .setCredentialsType(deviceCredentials.getCredentialsType().name()) + .setCredentialsId(deviceCredentials.getCredentialsId()); + if (deviceCredentials.getCredentialsValue() != null) { + builder.setCredentialsValue(deviceCredentials.getCredentialsValue()); + } return builder.build(); } } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java index 7db5a08c1f..29e317fe59 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java @@ -15,12 +15,10 @@ */ package org.thingsboard.server.dao.edge; -import com.fasterxml.jackson.core.JsonProcessingException; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.Event; -import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeSearchQuery; import org.thingsboard.server.common.data.id.CustomerId; diff --git a/common/edge-api/src/main/proto/edge.proto b/common/edge-api/src/main/proto/edge.proto index 732005efa4..f7f5c3c064 100644 --- a/common/edge-api/src/main/proto/edge.proto +++ b/common/edge-api/src/main/proto/edge.proto @@ -160,6 +160,9 @@ message DeviceUpdateMsg { UpdateMsgType msgType = 1; string name = 2; string type = 3; + string credentialsType = 4; + string credentialsId = 5; + string credentialsValue = 6; } message AssetUpdateMsg { From 58c78c943022ab8e84c85d07f11339136c9893db Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Wed, 29 Apr 2020 16:27:41 +0300 Subject: [PATCH 03/52] Removed imports --- .../org/thingsboard/server/controller/EdgeController.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java b/application/src/main/java/org/thingsboard/server/controller/EdgeController.java index 932d5b6c6c..546196208f 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EdgeController.java @@ -27,12 +27,9 @@ import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import org.thingsboard.server.common.data.Customer; -import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.audit.ActionType; -import org.thingsboard.server.common.data.device.DeviceSearchQuery; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeSearchQuery; import org.thingsboard.server.common.data.exception.ThingsboardException; @@ -42,7 +39,6 @@ import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageLink; -import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.model.ModelConstants; From c114dd980c9ecf369426d01f17b2b1aa9b4dd769 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Sat, 9 May 2020 02:15:12 +0300 Subject: [PATCH 04/52] Refactoring --- .../demo/rule_chains/thermostat_alarms.json | 1 + .../main/data/upgrade/2.4.x/schema_update.cql | 66 +++++++++---------- .../src/main/resources/thingsboard.yml | 1 + .../server/common/data/rule/RuleChain.java | 4 -- .../resources/cassandra/schema-entities.cql | 66 +++++++++---------- .../app/dashboard/dashboard-fieldset.tpl.html | 6 -- 6 files changed, 68 insertions(+), 76 deletions(-) diff --git a/application/src/main/data/json/demo/rule_chains/thermostat_alarms.json b/application/src/main/data/json/demo/rule_chains/thermostat_alarms.json index d67052cbc5..155d05f119 100644 --- a/application/src/main/data/json/demo/rule_chains/thermostat_alarms.json +++ b/application/src/main/data/json/demo/rule_chains/thermostat_alarms.json @@ -2,6 +2,7 @@ "ruleChain": { "additionalInfo": null, "name": "Thermostat Alarms", + "type": "SYSTEM", "firstRuleNodeId": null, "root": false, "debugMode": false, diff --git a/application/src/main/data/upgrade/2.4.x/schema_update.cql b/application/src/main/data/upgrade/2.4.x/schema_update.cql index 28bbeec7c0..7e1a99f8db 100644 --- a/application/src/main/data/upgrade/2.4.x/schema_update.cql +++ b/application/src/main/data/upgrade/2.4.x/schema_update.cql @@ -15,49 +15,49 @@ -- CREATE TABLE IF NOT EXISTS thingsboard.edge ( - id timeuuid, - tenant_id timeuuid, - customer_id timeuuid, - name text, - search_text text, - configuration text, - additional_info text, - PRIMARY KEY (id, tenant_id) + id timeuuid, + tenant_id timeuuid, + customer_id timeuuid, + name text, + search_text text, + configuration text, + additional_info text, + PRIMARY KEY (id, tenant_id) ); CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.edge_by_tenant_and_name AS - SELECT * - from thingsboard.edge - WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND name IS NOT NULL AND id IS NOT NULL - PRIMARY KEY ( tenant_id, name, id, customer_id, type) - WITH CLUSTERING ORDER BY ( name ASC, id DESC, customer_id DESC); + SELECT * + from thingsboard.edge + WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND name IS NOT NULL AND id IS NOT NULL + PRIMARY KEY ( tenant_id, name, id, customer_id, type) + WITH CLUSTERING ORDER BY ( name ASC, id DESC, customer_id DESC); CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.edge_by_tenant_and_search_text AS - SELECT * - from thingsboard.edge - WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL - PRIMARY KEY ( tenant_id, search_text, id, customer_id, type) - WITH CLUSTERING ORDER BY ( search_text ASC, id DESC, customer_id DESC); + SELECT * + from thingsboard.edge + WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL + PRIMARY KEY ( tenant_id, search_text, id, customer_id, type) + WITH CLUSTERING ORDER BY ( search_text ASC, id DESC, customer_id DESC); CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.edge_by_tenant_by_type_and_search_text AS - SELECT * - from thingsboard.edge - WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL - PRIMARY KEY ( tenant_id, type, search_text, id, customer_id) - WITH CLUSTERING ORDER BY ( type ASC, search_text ASC, id DESC, customer_id DESC); + SELECT * + from thingsboard.edge + WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL + PRIMARY KEY ( tenant_id, type, search_text, id, customer_id) + WITH CLUSTERING ORDER BY ( type ASC, search_text ASC, id DESC, customer_id DESC); CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.edge_by_customer_and_search_text AS - SELECT * - from thingsboard.edge - WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL - PRIMARY KEY ( customer_id, tenant_id, search_text, id, type ) - WITH CLUSTERING ORDER BY ( tenant_id DESC, search_text ASC, id DESC ); + SELECT * + from thingsboard.edge + WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL + PRIMARY KEY ( customer_id, tenant_id, search_text, id, type ) + WITH CLUSTERING ORDER BY ( tenant_id DESC, search_text ASC, id DESC ); CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.edge_by_customer_by_type_and_search_text AS - SELECT * - from thingsboard.edge - WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL - PRIMARY KEY ( customer_id, tenant_id, type, search_text, id ) - WITH CLUSTERING ORDER BY ( tenant_id DESC, type ASC, search_text ASC, id DESC ); + SELECT * + from thingsboard.edge + WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL + PRIMARY KEY ( customer_id, tenant_id, type, search_text, id ) + WITH CLUSTERING ORDER BY ( tenant_id DESC, type ASC, search_text ASC, id DESC ); -- VOBA ADD changes for the MATERIALIZED view for DEVICE ASSET ENTITY_VIEW RULE_CHAIN \ No newline at end of file diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 80c22a5652..31a119376e 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -456,6 +456,7 @@ audit-log: "rule_chain": "${AUDIT_LOG_MASK_RULE_CHAIN:W}" "alarm": "${AUDIT_LOG_MASK_ALARM:W}" "entity_view": "${AUDIT_LOG_MASK_ENTITY_VIEW:W}" + "edge": "${AUDIT_LOG_MASK_EDGE:W}" sink: # Type of external sink. possible options: none, elasticsearch type: "${AUDIT_LOG_SINK_TYPE:none}" diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java index be9ce649bc..a7b649951d 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java @@ -67,10 +67,6 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo im this.type = ruleChain.getType(); this.firstRuleNodeId = ruleChain.getFirstRuleNodeId(); this.root = ruleChain.isRoot(); - - // TODO: VOBA - check that this is needed - // this.debugMode = ruleChain.isDebugMode(); - this.assignedEdges = ruleChain.getAssignedEdges(); this.setConfiguration(ruleChain.getConfiguration()); } diff --git a/dao/src/main/resources/cassandra/schema-entities.cql b/dao/src/main/resources/cassandra/schema-entities.cql index be42ffad63..ced78c3826 100644 --- a/dao/src/main/resources/cassandra/schema-entities.cql +++ b/dao/src/main/resources/cassandra/schema-entities.cql @@ -727,47 +727,47 @@ CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_ent WITH CLUSTERING ORDER BY (entity_id DESC, customer_id DESC, search_text ASC, id DESC); CREATE TABLE IF NOT EXISTS thingsboard.edge ( - id timeuuid, - tenant_id timeuuid, - customer_id timeuuid, - name text, - search_text text, - configuration text, - additional_info text, - PRIMARY KEY (id, tenant_id) + id timeuuid, + tenant_id timeuuid, + customer_id timeuuid, + name text, + search_text text, + configuration text, + additional_info text, + PRIMARY KEY (id, tenant_id) ); CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.edge_by_tenant_and_name AS - SELECT * - from thingsboard.edge - WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND name IS NOT NULL AND id IS NOT NULL - PRIMARY KEY ( tenant_id, name, id, customer_id, type) - WITH CLUSTERING ORDER BY ( name ASC, id DESC, customer_id DESC); + SELECT * + from thingsboard.edge + WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND name IS NOT NULL AND id IS NOT NULL + PRIMARY KEY ( tenant_id, name, id, customer_id, type) + WITH CLUSTERING ORDER BY ( name ASC, id DESC, customer_id DESC); CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.edge_by_tenant_and_search_text AS - SELECT * - from thingsboard.edge - WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL - PRIMARY KEY ( tenant_id, search_text, id, customer_id, type) - WITH CLUSTERING ORDER BY ( search_text ASC, id DESC, customer_id DESC); + SELECT * + from thingsboard.edge + WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL + PRIMARY KEY ( tenant_id, search_text, id, customer_id, type) + WITH CLUSTERING ORDER BY ( search_text ASC, id DESC, customer_id DESC); CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.edge_by_tenant_by_type_and_search_text AS - SELECT * - from thingsboard.edge - WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL - PRIMARY KEY ( tenant_id, type, search_text, id, customer_id) - WITH CLUSTERING ORDER BY ( type ASC, search_text ASC, id DESC, customer_id DESC); + SELECT * + from thingsboard.edge + WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL + PRIMARY KEY ( tenant_id, type, search_text, id, customer_id) + WITH CLUSTERING ORDER BY ( type ASC, search_text ASC, id DESC, customer_id DESC); CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.edge_by_customer_and_search_text AS - SELECT * - from thingsboard.edge - WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL - PRIMARY KEY ( customer_id, tenant_id, search_text, id, type ) - WITH CLUSTERING ORDER BY ( tenant_id DESC, search_text ASC, id DESC ); + SELECT * + from thingsboard.edge + WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL + PRIMARY KEY ( customer_id, tenant_id, search_text, id, type ) + WITH CLUSTERING ORDER BY ( tenant_id DESC, search_text ASC, id DESC ); CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.edge_by_customer_by_type_and_search_text AS - SELECT * - from thingsboard.edge - WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL - PRIMARY KEY ( customer_id, tenant_id, type, search_text, id ) - WITH CLUSTERING ORDER BY ( tenant_id DESC, type ASC, search_text ASC, id DESC ); + SELECT * + from thingsboard.edge + WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL + PRIMARY KEY ( customer_id, tenant_id, type, search_text, id ) + WITH CLUSTERING ORDER BY ( tenant_id DESC, type ASC, search_text ASC, id DESC ); diff --git a/ui/src/app/dashboard/dashboard-fieldset.tpl.html b/ui/src/app/dashboard/dashboard-fieldset.tpl.html index 6d5aadd78a..bf3283b857 100644 --- a/ui/src/app/dashboard/dashboard-fieldset.tpl.html +++ b/ui/src/app/dashboard/dashboard-fieldset.tpl.html @@ -31,12 +31,6 @@ {{ 'dashboard.unassign-from-customer' | translate }} - - - - - - {{ 'dashboard.delete' | translate }} From 95ce5595fc4d281be00d4be9615021e3ec9fd21f Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Sat, 9 May 2020 03:27:03 +0300 Subject: [PATCH 05/52] Fixes after merge --- .../service/security/permission/Resource.java | 4 ++-- .../engine/action/TbAbstractRelationActionNode.java | 9 +++++++++ .../rule/engine/action/TbAssignToCustomerNode.java | 7 +++++++ .../rule/engine/action/TbCreateRelationNode.java | 13 +++++++++++++ .../engine/action/TbUnassignFromCustomerNode.java | 7 +++++++ .../engine/filter/TbOriginatorTypeSwitchNode.java | 3 +++ .../engine/metadata/TbGetCustomerDetailsNode.java | 13 +++++++++++++ 7 files changed, 54 insertions(+), 2 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java b/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java index 604a4fe919..4738ee44c5 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java @@ -27,12 +27,12 @@ public enum Resource { CUSTOMER(EntityType.CUSTOMER), DASHBOARD(EntityType.DASHBOARD), ENTITY_VIEW(EntityType.ENTITY_VIEW), + EDGE(EntityType.EDGE), TENANT(EntityType.TENANT), RULE_CHAIN(EntityType.RULE_CHAIN), USER(EntityType.USER), WIDGETS_BUNDLE(EntityType.WIDGETS_BUNDLE), - WIDGET_TYPE(EntityType.WIDGET_TYPE), - EDGE(EntityType.EDGE); + WIDGET_TYPE(EntityType.WIDGET_TYPE); private final EntityType entityType; diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractRelationActionNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractRelationActionNode.java index 46e4e3e396..e9e3676427 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractRelationActionNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractRelationActionNode.java @@ -36,6 +36,7 @@ import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.page.TextPageData; @@ -48,6 +49,7 @@ import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.device.DeviceService; +import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.entityview.EntityViewService; import java.util.List; @@ -236,6 +238,13 @@ public abstract class TbAbstractRelationActionNode dashboardInfoTextPageData = dashboardService.findDashboardsByTenantId(ctx.getTenantId(), new TextPageLink(200, entitykey.getEntityName())); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAssignToCustomerNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAssignToCustomerNode.java index f6ccd33119..07a8744873 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAssignToCustomerNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAssignToCustomerNode.java @@ -69,6 +69,9 @@ public class TbAssignToCustomerNode extends TbAbstractCustomerActionNode processEdge(TbContext ctx, EntityContainer entityContainer, SearchDirectionIds sdId, String relationType) { + return Futures.transformAsync(ctx.getEdgeService().findEdgeByIdAsync(ctx.getTenantId(), new EdgeId(entityContainer.getEntityId().getId())), edge -> { + if (edge != null) { + return processSave(ctx, sdId, relationType); + } else { + return Futures.immediateFuture(true); + } + }, ctx.getDbCallbackExecutor()); + } + private ListenableFuture processDevice(TbContext ctx, EntityContainer entityContainer, SearchDirectionIds sdId, String relationType) { return Futures.transformAsync(ctx.getDeviceService().findDeviceByIdAsync(ctx.getTenantId(), new DeviceId(entityContainer.getEntityId().getId())), device -> { if (device != null) { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbUnassignFromCustomerNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbUnassignFromCustomerNode.java index a8ab882a46..73f9e2da08 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbUnassignFromCustomerNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbUnassignFromCustomerNode.java @@ -62,6 +62,9 @@ public class TbUnassignFromCustomerNode extends TbAbstractCustomerActionNode { + if (edge != null) { + if (!edge.getCustomerId().isNullUid()) { + return ctx.getCustomerService().findCustomerByIdAsync(ctx.getTenantId(), edge.getCustomerId()); + } else { + throw new RuntimeException("Edge with name '" + edge.getName() + "' is not assigned to Customer."); + } + } else { + return Futures.immediateFuture(null); + } + }, MoreExecutors.directExecutor()); default: throw new RuntimeException("Entity with entityType '" + msg.getOriginator().getEntityType() + "' is not supported."); } From 12b5a932c362aba9bded2efce06e6fb18383fdf4 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Sun, 10 May 2020 23:20:30 +0300 Subject: [PATCH 06/52] Fixes after code review --- ui/src/app/api/entity.service.js | 21 +++++ .../entity/entity-filter-view.directive.js | 9 ++ ui/src/app/entity/entity-filter.directive.js | 8 ++ ui/src/app/entity/entity-filter.tpl.html | 91 +++++++++++++++++++ .../widget/lib/entities-hierarchy-widget.js | 3 + 5 files changed, 132 insertions(+) diff --git a/ui/src/app/api/entity.service.js b/ui/src/app/api/entity.service.js index f6e664a5cf..e2c8dd627b 100644 --- a/ui/src/app/api/entity.service.js +++ b/ui/src/app/api/entity.service.js @@ -594,6 +594,21 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device } ); break; + case types.aliasFilterType.edgeType.value: + getEntitiesByNameFilter(types.entityType.edge, filter.edgeNameFilter, maxItems, {ignoreLoading: true}, filter.edgeType).then( + function success(entities) { + if (entities && entities.length || !failOnEmpty) { + result.entities = entitiesToEntitiesInfo(entities); + deferred.resolve(result); + } else { + deferred.reject(); + } + }, + function fail() { + deferred.reject(); + } + ); + break; case types.aliasFilterType.relationsQuery.value: result.stateEntity = filter.rootStateEntity; var rootEntityType; @@ -648,6 +663,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device case types.aliasFilterType.assetSearchQuery.value: case types.aliasFilterType.deviceSearchQuery.value: case types.aliasFilterType.entityViewSearchQuery.value: + case types.aliasFilterType.edgeSearchQuery.value: result.stateEntity = filter.rootStateEntity; if (result.stateEntity && stateEntityId) { rootEntityType = stateEntityId.entityType; @@ -777,6 +793,8 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device return entityType === types.entityType.device; case types.aliasFilterType.entityViewType.value: return entityType === types.entityType.entityView; + case types.aliasFilterType.edgeType.value: + return entityType === types.entityType.edge; case types.aliasFilterType.relationsQuery.value: return true; case types.aliasFilterType.assetSearchQuery.value: @@ -906,6 +924,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device entityFieldKeys.push(types.entityField.phone.keyName); break; case types.entityType.entityView: + case types.entityType.edge: entityFieldKeys.push(types.entityField.name.keyName); entityFieldKeys.push(types.entityField.type.keyName); break; @@ -1121,6 +1140,8 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device findByQueryPromise = deviceService.findByQuery(entitySearchQuery, true, {ignoreLoading: true}); } else if (entityType == types.entityType.entityView) { findByQueryPromise = entityViewService.findByQuery(entitySearchQuery, true, {ignoreLoading: true}); + } else if (entityType == types.entityType.edge) { + findByQueryPromise = edgeService.findByQuery(entitySearchQuery, true, {ignoreLoading: true}); } findByQueryPromise.then( function success(entities) { diff --git a/ui/src/app/entity/entity-filter-view.directive.js b/ui/src/app/entity/entity-filter-view.directive.js index 837313df28..c910f6f197 100644 --- a/ui/src/app/entity/entity-filter-view.directive.js +++ b/ui/src/app/entity/entity-filter-view.directive.js @@ -86,6 +86,15 @@ export default function EntityFilterViewDirective($compile, $templateCache, $q, scope.filterDisplayValue = $translate.instant('alias.filter-type-entity-view-type-description', {entityViewType: entityViewType}); } break; + case types.aliasFilterType.edgeType.value: + var edgeType = scope.filter.edgeType; + prefix = scope.filter.edgeNameFilter; + if (prefix && prefix.length) { + scope.filterDisplayValue = $translate.instant('alias.filter-type-edge-type-and-name-description', {edgeType: edgeType, prefix: prefix}); + } else { + scope.filterDisplayValue = $translate.instant('alias.filter-type-edge-type-description', {edgeType: edgeType}); + } + break; case types.aliasFilterType.relationsQuery.value: var rootEntityText; var directionText; diff --git a/ui/src/app/entity/entity-filter.directive.js b/ui/src/app/entity/entity-filter.directive.js index 39e3892ff9..629433545d 100644 --- a/ui/src/app/entity/entity-filter.directive.js +++ b/ui/src/app/entity/entity-filter.directive.js @@ -73,10 +73,15 @@ export default function EntityFilterDirective($compile, $templateCache, $q, $doc filter.entityViewType = null; filter.entityViewNameFilter = ''; break; + case types.aliasFilterType.edgeType.value: + filter.edgeType = null; + filter.edgeNameFilter = ''; + break; case types.aliasFilterType.relationsQuery.value: case types.aliasFilterType.assetSearchQuery.value: case types.aliasFilterType.deviceSearchQuery.value: case types.aliasFilterType.entityViewSearchQuery.value: + case types.aliasFilterType.edgeSearchQuery.value: filter.rootStateEntity = false; filter.stateEntityParamName = null; filter.defaultStateEntity = null; @@ -95,6 +100,9 @@ export default function EntityFilterDirective($compile, $templateCache, $q, $doc } else if (filter.type === types.aliasFilterType.entityViewSearchQuery.value) { filter.relationType = null; filter.entityViewTypes = []; + } else if (filter.type === types.aliasFilterType.edgeSearchQuery.value) { + filter.relationType = null; + filter.edgeTypes = []; } break; } diff --git a/ui/src/app/entity/entity-filter.tpl.html b/ui/src/app/entity/entity-filter.tpl.html index bfc63a7d24..65370f7ba9 100644 --- a/ui/src/app/entity/entity-filter.tpl.html +++ b/ui/src/app/entity/entity-filter.tpl.html @@ -126,6 +126,20 @@ aria-label="{{ 'entity-view.name-starts-with' | translate }}"> +
+ + + + + + +
@@ -426,4 +440,81 @@ ng-model="filter.entityViewTypes">
+
+ +
+ + + +
+
+ + +
+
+ + + + +
+ + + +
+
+
+
+ + + +
+
+
+ + + + + {{ ('relation.search-direction.' + direction) | translate}} + + + + + + + +
+
relation.relation-type
+ + +
edge.edge-types
+ + +
diff --git a/ui/src/app/widget/lib/entities-hierarchy-widget.js b/ui/src/app/widget/lib/entities-hierarchy-widget.js index 977bd7cea6..3bd5c30d59 100644 --- a/ui/src/app/widget/lib/entities-hierarchy-widget.js +++ b/ui/src/app/widget/lib/entities-hierarchy-widget.js @@ -508,6 +508,9 @@ function EntitiesHierarchyWidgetController($element, $scope, $q, $timeout, toast case types.entityType.entityView: materialIcon = 'view_quilt'; break; + case types.entityType.edge: + materialIcon = 'router'; + break; } } return { From 2af6e5d3d69964d4cc61d15bcc3d7b05e0f78433 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Mon, 11 May 2020 12:31:56 +0300 Subject: [PATCH 07/52] Fixed push to updates --- .../server/actors/ruleChain/RuleChainActorMessageProcessor.java | 2 +- application/src/main/resources/thingsboard.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java index f432290e3c..8bca159d85 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java @@ -313,7 +313,6 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor Date: Wed, 13 May 2020 12:31:33 +0300 Subject: [PATCH 08/52] Fixes for downlink msg. Refactoring --- .../{2.4.x => 2.6.0}/schema_update.cql | 0 .../{2.4.x => 2.6.0}/schema_update.sql | 0 .../service/edge/rpc/EdgeGrpcSession.java | 22 +++++-- .../CassandraDatabaseUpgradeService.java | 2 +- .../install/SqlDatabaseUpgradeService.java | 46 ++++++++------ .../server/common/data/DashboardInfo.java | 40 +++--------- .../server/common/data/EdgeUtils.java | 61 +++++++++++++++++++ .../server/common/data/rule/RuleChain.java | 43 +++---------- .../server/dao/edge/EdgeServiceImpl.java | 7 ++- 9 files changed, 126 insertions(+), 95 deletions(-) rename application/src/main/data/upgrade/{2.4.x => 2.6.0}/schema_update.cql (100%) rename application/src/main/data/upgrade/{2.4.x => 2.6.0}/schema_update.sql (100%) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/EdgeUtils.java diff --git a/application/src/main/data/upgrade/2.4.x/schema_update.cql b/application/src/main/data/upgrade/2.6.0/schema_update.cql similarity index 100% rename from application/src/main/data/upgrade/2.4.x/schema_update.cql rename to application/src/main/data/upgrade/2.6.0/schema_update.cql diff --git a/application/src/main/data/upgrade/2.4.x/schema_update.sql b/application/src/main/data/upgrade/2.6.0/schema_update.sql similarity index 100% rename from application/src/main/data/upgrade/2.4.x/schema_update.sql rename to application/src/main/data/upgrade/2.6.0/schema_update.sql 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 f99c51e8a8..249071058a 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 @@ -26,6 +26,7 @@ import com.google.protobuf.ByteString; import io.grpc.stub.StreamObserver; import lombok.Data; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.codec.binary.Base64; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.DataConstants; @@ -81,6 +82,7 @@ import org.thingsboard.server.gen.edge.UplinkResponseMsg; import org.thingsboard.server.gen.edge.UserUpdateMsg; import org.thingsboard.server.service.edge.EdgeContextComponent; +import java.io.Closeable; import java.io.IOException; import java.util.Collections; import java.util.List; @@ -95,7 +97,7 @@ import static org.thingsboard.server.gen.edge.UpdateMsgType.ENTITY_CREATED_RPC_M @Slf4j @Data -public final class EdgeGrpcSession implements Cloneable { +public final class EdgeGrpcSession implements Closeable { private static final ReentrantLock entityCreationLock = new ReentrantLock(); @@ -168,7 +170,7 @@ public final class EdgeGrpcSession implements Cloneable { UUID ifOffset = null; do { pageData = ctx.getEdgeService().findQueueEvents(edge.getTenantId(), edge.getId(), pageLink); - if (!pageData.getData().isEmpty()) { + if (isConnected() && !pageData.getData().isEmpty()) { log.trace("[{}] [{}] event(s) are going to be processed.", this.sessionId, pageData.getData().size()); for (Event event : pageData.getData()) { log.trace("[{}] Processing event [{}]", this.sessionId, event); @@ -196,7 +198,7 @@ public final class EdgeGrpcSession implements Cloneable { ifOffset = event.getUuidId(); } } - if (pageData.hasNext()) { + if (isConnected() && pageData.hasNext()) { pageLink = pageData.getNextPageLink(); try { Thread.sleep(ctx.getEdgeEventStorageSettings().getSleepIntervalBetweenBatches()); @@ -204,7 +206,7 @@ public final class EdgeGrpcSession implements Cloneable { log.error("Error during sleep between batches", e); } } - } while (pageData.hasNext()); + } while (isConnected() && pageData.hasNext()); if (ifOffset != null) { Long newStartTs = UUIDs.unixTimestamp(ifOffset); @@ -287,7 +289,7 @@ public final class EdgeGrpcSession implements Cloneable { private void processCustomDownlinkMessage(EdgeQueueEntry entry) throws IOException { log.trace("Executing processCustomDownlinkMessage, entry [{}]", entry); - TbMsg tbMsg = objectMapper.readValue(entry.getData(), TbMsg.class); + TbMsg tbMsg = TbMsg.fromBytes(Base64.decodeBase64(entry.getData()), TbMsgCallback.EMPTY); String entityName = null; switch (entry.getEntityType()) { case DEVICE: @@ -700,4 +702,14 @@ public final class EdgeGrpcSession implements Cloneable { .setType(edge.getType().toString()) .build(); } + + @Override + public void close() { + connected = false; + try { + outputStream.onCompleted(); + } catch (Exception e) { + log.debug("[{}] Failed to close output stream: {}", sessionId, e.getMessage()); + } + } } diff --git a/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java index e87b9138b2..56b4e011cb 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java @@ -308,7 +308,7 @@ public class CassandraDatabaseUpgradeService extends AbstractCassandraDatabaseUp break; case "2.5.0": log.info("Updating schema ..."); - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.4.x", SCHEMA_UPDATE_CQL); + schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.6.0", SCHEMA_UPDATE_CQL); loadCql(schemaUpdateFile); try { 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 5a73a7a076..bff44636d6 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 @@ -211,26 +211,6 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService case "2.4.3": try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { log.info("Updating schema ..."); - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.4.x", SCHEMA_UPDATE_SQL); - loadSql(schemaUpdateFile, conn); - try { - conn.createStatement().execute("ALTER TABLE asset ADD edge_id varchar(31)"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script - } catch (Exception e) {} - try { - conn.createStatement().execute("ALTER TABLE device ADD edge_id varchar(31)"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script - } catch (Exception e) {} - try { - conn.createStatement().execute("ALTER TABLE entity_view ADD edge_id varchar(31)"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script - } catch (Exception e) {} - try { - conn.createStatement().execute("ALTER TABLE dashboard ADD assigned_edges varchar(10000000)"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script - } catch (Exception e) {} - try { - conn.createStatement().execute("ALTER TABLE rule_chain ADD assigned_edges varchar(10000000)"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script - } catch (Exception e) {} - try { - conn.createStatement().execute("ALTER TABLE rule_chain ADD type varchar(255) DEFAULT 'SYSTEM'"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script - } catch (Exception e) {} try { conn.createStatement().execute("ALTER TABLE attribute_kv ADD COLUMN json_v json;"); } catch (Exception e) { @@ -253,6 +233,32 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService log.info("Schema updated."); } break; + case "2.5.0": + try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { + log.info("Updating schema ..."); + schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.6.0", SCHEMA_UPDATE_SQL); + loadSql(schemaUpdateFile, conn); + try { + conn.createStatement().execute("ALTER TABLE asset ADD edge_id varchar(31)"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script + } catch (Exception e) {} + try { + conn.createStatement().execute("ALTER TABLE device ADD edge_id varchar(31)"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script + } catch (Exception e) {} + try { + conn.createStatement().execute("ALTER TABLE entity_view ADD edge_id varchar(31)"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script + } catch (Exception e) {} + try { + conn.createStatement().execute("ALTER TABLE dashboard ADD assigned_edges varchar(10000000)"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script + } catch (Exception e) {} + try { + conn.createStatement().execute("ALTER TABLE rule_chain ADD assigned_edges varchar(10000000)"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script + } catch (Exception e) {} + try { + conn.createStatement().execute("ALTER TABLE rule_chain ADD type varchar(255) DEFAULT 'SYSTEM'"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script + } catch (Exception e) {} + log.info("Schema updated."); + } + break; default: throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion); } 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 817c9d1857..0a3ebe0abd 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 @@ -124,52 +124,26 @@ public class DashboardInfo extends SearchTextBased implements HasNa } public boolean isAssignedToEdge(EdgeId edgeId) { - return this.assignedEdges != null && this.assignedEdges.contains(new ShortEdgeInfo(edgeId, null, null)); + return EdgeUtils.isAssignedToEdge(this.assignedEdges, edgeId); } public ShortEdgeInfo getAssignedEdgeInfo(EdgeId edgeId) { - if (this.assignedEdges != null) { - for (ShortEdgeInfo edgeInfo : this.assignedEdges) { - if (edgeInfo.getEdgeId().equals(edgeId)) { - return edgeInfo; - } - } - } - return null; + return EdgeUtils.getAssignedEdgeInfo(this.assignedEdges, edgeId); } public boolean addAssignedEdge(Edge edge) { - ShortEdgeInfo edgeInfo = edge.toShortEdgeInfo(); - if (this.assignedEdges != null && this.assignedEdges.contains(edgeInfo)) { - return false; - } else { - if (this.assignedEdges == null) { - this.assignedEdges = new HashSet<>(); - } - this.assignedEdges.add(edgeInfo); - return true; + if (this.assignedEdges == null) { + this.assignedEdges = new HashSet<>(); } + return EdgeUtils.addAssignedEdge(this.assignedEdges, edge); } public boolean updateAssignedEdge(Edge edge) { - ShortEdgeInfo edgeInfo = edge.toShortEdgeInfo(); - if (this.assignedEdges != null && this.assignedEdges.contains(edgeInfo)) { - this.assignedEdges.remove(edgeInfo); - this.assignedEdges.add(edgeInfo); - return true; - } else { - return false; - } + return EdgeUtils.updateAssignedEdge(this.assignedEdges, edge); } public boolean removeAssignedEdge(Edge edge) { - ShortEdgeInfo edgeInfo = edge.toShortEdgeInfo(); - if (this.assignedEdges != null && this.assignedEdges.contains(edgeInfo)) { - this.assignedEdges.remove(edgeInfo); - return true; - } else { - return false; - } + return EdgeUtils.removeAssignedEdge(this.assignedEdges, edge); } @Override diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EdgeUtils.java b/common/data/src/main/java/org/thingsboard/server/common/data/EdgeUtils.java new file mode 100644 index 0000000000..fd0c21b2cb --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/EdgeUtils.java @@ -0,0 +1,61 @@ +package org.thingsboard.server.common.data; + +import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.id.EdgeId; + +import java.util.Set; + +public final class EdgeUtils { + + private EdgeUtils() {} + + public static boolean isAssignedToEdge(Set assignedEdges, EdgeId edgeId) { + return assignedEdges != null && assignedEdges.contains(new ShortEdgeInfo(edgeId, null, null)); + } + + public static ShortEdgeInfo getAssignedEdgeInfo(Set assignedEdges, EdgeId edgeId) { + if (assignedEdges != null) { + for (ShortEdgeInfo edgeInfo : assignedEdges) { + if (edgeInfo.getEdgeId().equals(edgeId)) { + return edgeInfo; + } + } + } + return null; + } + + public static boolean addAssignedEdge(Set assignedEdges, Edge edge) { + ShortEdgeInfo edgeInfo = edge.toShortEdgeInfo(); + if (assignedEdges != null && assignedEdges.contains(edgeInfo)) { + return false; + } else { + if (assignedEdges != null) { + assignedEdges.add(edgeInfo); + return true; + } else { + return false; + } + } + } + + public static boolean updateAssignedEdge(Set assignedEdges, Edge edge) { + ShortEdgeInfo edgeInfo = edge.toShortEdgeInfo(); + if (assignedEdges != null && assignedEdges.contains(edgeInfo)) { + assignedEdges.remove(edgeInfo); + assignedEdges.add(edgeInfo); + return true; + } else { + return false; + } + } + + public static boolean removeAssignedEdge(Set assignedEdges, Edge edge) { + ShortEdgeInfo edgeInfo = edge.toShortEdgeInfo(); + if (assignedEdges != null && assignedEdges.contains(edgeInfo)) { + assignedEdges.remove(edgeInfo); + return true; + } else { + return false; + } + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java index a7b649951d..b81f3f7a9a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java @@ -20,6 +20,7 @@ import com.fasterxml.jackson.databind.JsonNode; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.extern.slf4j.Slf4j; +import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; @@ -89,52 +90,28 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo im setJson(data, json -> this.configuration = json, bytes -> this.configurationBytes = bytes); } + + public boolean isAssignedToEdge(EdgeId edgeId) { - return this.assignedEdges != null && this.assignedEdges.contains(new ShortEdgeInfo(edgeId, null, null)); + return EdgeUtils.isAssignedToEdge(this.assignedEdges, edgeId); } public ShortEdgeInfo getAssignedEdgeInfo(EdgeId edgeId) { - if (this.assignedEdges != null) { - for (ShortEdgeInfo edgeInfo : this.assignedEdges) { - if (edgeInfo.getEdgeId().equals(edgeId)) { - return edgeInfo; - } - } - } - return null; + return EdgeUtils.getAssignedEdgeInfo(this.assignedEdges, edgeId); } public boolean addAssignedEdge(Edge edge) { - ShortEdgeInfo edgeInfo = edge.toShortEdgeInfo(); - if (this.assignedEdges != null && this.assignedEdges.contains(edgeInfo)) { - return false; - } else { - if (this.assignedEdges == null) { - this.assignedEdges = new HashSet<>(); - } - this.assignedEdges.add(edgeInfo); - return true; + if (this.assignedEdges == null) { + this.assignedEdges = new HashSet<>(); } + return EdgeUtils.addAssignedEdge(this.assignedEdges, edge); } public boolean updateAssignedEdge(Edge edge) { - ShortEdgeInfo edgeInfo = edge.toShortEdgeInfo(); - if (this.assignedEdges != null && this.assignedEdges.contains(edgeInfo)) { - this.assignedEdges.remove(edgeInfo); - this.assignedEdges.add(edgeInfo); - return true; - } else { - return false; - } + return EdgeUtils.updateAssignedEdge(this.assignedEdges, edge); } public boolean removeAssignedEdge(Edge edge) { - ShortEdgeInfo edgeInfo = edge.toShortEdgeInfo(); - if (this.assignedEdges != null && this.assignedEdges.contains(edgeInfo)) { - this.assignedEdges.remove(edgeInfo); - return true; - } else { - return false; - } + return EdgeUtils.removeAssignedEdge(this.assignedEdges, edge); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java index dc64d7df40..ef384a9fba 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java @@ -22,6 +22,7 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.codec.binary.Base64; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; @@ -301,7 +302,7 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic log.trace("Executing unassignCustomerEdges, tenantId [{}], customerId [{}]", tenantId, customerId); validateId(tenantId, INCORRECT_TENANT_ID + tenantId); validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); - customerEdgeUnasigner.removeEntities(tenantId, customerId); + customerEdgeUnassigner.removeEntities(tenantId, customerId); } @Override @@ -387,7 +388,7 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic EdgeQueueEntityType edgeQueueEntityType = getEdgeQueueTypeByEntityType(tbMsg.getOriginator().getEntityType()); if (edgeId != null && edgeQueueEntityType != null) { try { - saveEventToEdgeQueue(tenantId, edgeId, edgeQueueEntityType, tbMsg.getType(), mapper.writeValueAsString(tbMsg), callback); + saveEventToEdgeQueue(tenantId, edgeId, edgeQueueEntityType, tbMsg.getType(), Base64.encodeBase64String(TbMsg.toByteArray(tbMsg)), callback); } catch (IOException e) { log.error("Error while saving custom tbMsg into Edge Queue", e); } @@ -723,7 +724,7 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic } }; - private PaginatedRemover customerEdgeUnasigner = new PaginatedRemover() { + private PaginatedRemover customerEdgeUnassigner = new PaginatedRemover() { @Override protected List findEntities(TenantId tenantId, CustomerId id, TextPageLink pageLink) { From 1a084a80f8c9684022e1efcd96162fca776dec79 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Wed, 13 May 2020 18:00:45 +0300 Subject: [PATCH 09/52] Added group field --- common/edge-api/src/main/proto/edge.proto | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/common/edge-api/src/main/proto/edge.proto b/common/edge-api/src/main/proto/edge.proto index cd5281700d..a8aa95f824 100644 --- a/common/edge-api/src/main/proto/edge.proto +++ b/common/edge-api/src/main/proto/edge.proto @@ -154,6 +154,7 @@ message DashboardUpdateMsg { int64 idLSB = 3; string title = 4; string configuration = 5; + string groupName = 6; } message DeviceUpdateMsg { @@ -164,6 +165,7 @@ message DeviceUpdateMsg { string credentialsType = 5; string credentialsId = 6; string credentialsValue = 7; + string groupName = 8; } message AssetUpdateMsg { @@ -171,6 +173,7 @@ message AssetUpdateMsg { string name = 2; string type = 3; string label = 4; + string groupName = 5; } message EntityViewUpdateMsg { @@ -180,6 +183,7 @@ message EntityViewUpdateMsg { string relatedName = 4; string relatedType = 5; EntityType relatedEntityType = 6; + string groupName = 7; } message AlarmUpdateMsg { From 31a1e310f13fa38db55c4254fbb8013928e1357f Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Fri, 15 May 2020 19:36:32 +0300 Subject: [PATCH 10/52] Rule chain refactoring --- .../server/common/data/DashboardInfo.java | 6 +++--- .../thingsboard/server/common/data/EdgeUtils.java | 13 +++++-------- .../server/common/data/rule/RuleChain.java | 6 +++--- .../server/dao/rule/BaseRuleChainService.java | 11 ++++++----- 4 files changed, 17 insertions(+), 19 deletions(-) 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 0a3ebe0abd..263e756082 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 @@ -135,15 +135,15 @@ public class DashboardInfo extends SearchTextBased implements HasNa if (this.assignedEdges == null) { this.assignedEdges = new HashSet<>(); } - return EdgeUtils.addAssignedEdge(this.assignedEdges, edge); + return EdgeUtils.addAssignedEdge(this.assignedEdges, edge.toShortEdgeInfo()); } public boolean updateAssignedEdge(Edge edge) { - return EdgeUtils.updateAssignedEdge(this.assignedEdges, edge); + return EdgeUtils.updateAssignedEdge(this.assignedEdges, edge.toShortEdgeInfo()); } public boolean removeAssignedEdge(Edge edge) { - return EdgeUtils.removeAssignedEdge(this.assignedEdges, edge); + return EdgeUtils.removeAssignedEdge(this.assignedEdges, edge.toShortEdgeInfo()); } @Override diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EdgeUtils.java b/common/data/src/main/java/org/thingsboard/server/common/data/EdgeUtils.java index fd0c21b2cb..10008bc1a6 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/EdgeUtils.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/EdgeUtils.java @@ -1,13 +1,13 @@ package org.thingsboard.server.common.data; -import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.id.EdgeId; import java.util.Set; public final class EdgeUtils { - private EdgeUtils() {} + private EdgeUtils() { + } public static boolean isAssignedToEdge(Set assignedEdges, EdgeId edgeId) { return assignedEdges != null && assignedEdges.contains(new ShortEdgeInfo(edgeId, null, null)); @@ -24,8 +24,7 @@ public final class EdgeUtils { return null; } - public static boolean addAssignedEdge(Set assignedEdges, Edge edge) { - ShortEdgeInfo edgeInfo = edge.toShortEdgeInfo(); + public static boolean addAssignedEdge(Set assignedEdges, ShortEdgeInfo edgeInfo) { if (assignedEdges != null && assignedEdges.contains(edgeInfo)) { return false; } else { @@ -38,8 +37,7 @@ public final class EdgeUtils { } } - public static boolean updateAssignedEdge(Set assignedEdges, Edge edge) { - ShortEdgeInfo edgeInfo = edge.toShortEdgeInfo(); + public static boolean updateAssignedEdge(Set assignedEdges, ShortEdgeInfo edgeInfo) { if (assignedEdges != null && assignedEdges.contains(edgeInfo)) { assignedEdges.remove(edgeInfo); assignedEdges.add(edgeInfo); @@ -49,8 +47,7 @@ public final class EdgeUtils { } } - public static boolean removeAssignedEdge(Set assignedEdges, Edge edge) { - ShortEdgeInfo edgeInfo = edge.toShortEdgeInfo(); + public static boolean removeAssignedEdge(Set assignedEdges, ShortEdgeInfo edgeInfo) { if (assignedEdges != null && assignedEdges.contains(edgeInfo)) { assignedEdges.remove(edgeInfo); return true; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java index b81f3f7a9a..49508e2971 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java @@ -104,14 +104,14 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo im if (this.assignedEdges == null) { this.assignedEdges = new HashSet<>(); } - return EdgeUtils.addAssignedEdge(this.assignedEdges, edge); + return EdgeUtils.addAssignedEdge(this.assignedEdges, edge.toShortEdgeInfo()); } public boolean updateAssignedEdge(Edge edge) { - return EdgeUtils.updateAssignedEdge(this.assignedEdges, edge); + return EdgeUtils.updateAssignedEdge(this.assignedEdges, edge.toShortEdgeInfo()); } public boolean removeAssignedEdge(Edge edge) { - return EdgeUtils.removeAssignedEdge(this.assignedEdges, edge); + return EdgeUtils.removeAssignedEdge(this.assignedEdges, edge.toShortEdgeInfo()); } } 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 22c4856fd6..ea23781e62 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 @@ -46,6 +46,7 @@ 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.dao.edge.EdgeDao; +import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; @@ -78,7 +79,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC private TenantDao tenantDao; @Autowired - private EdgeDao edgeDao; + private EdgeService edgeService; @Override public RuleChain saveRuleChain(RuleChain ruleChain) { @@ -395,7 +396,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC @Override public RuleChain assignRuleChainToEdge(TenantId tenantId, RuleChainId ruleChainId, EdgeId edgeId) { RuleChain ruleChain = findRuleChainById(tenantId, ruleChainId); - Edge edge = edgeDao.findById(tenantId, edgeId.getId()); + Edge edge = edgeService.findEdgeById(tenantId, edgeId); if (edge == null) { throw new DataValidationException("Can't assign ruleChain to non-existent edge!"); } @@ -417,7 +418,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC @Override public RuleChain unassignRuleChainFromEdge(TenantId tenantId, RuleChainId ruleChainId, EdgeId edgeId, boolean remove) { RuleChain ruleChain = findRuleChainById(tenantId, ruleChainId); - Edge edge = edgeDao.findById(tenantId, edgeId.getId()); + Edge edge = edgeService.findEdgeById(tenantId, edgeId); if (edge == null) { throw new DataValidationException("Can't unassign rule chain from non-existent edge!"); } @@ -441,7 +442,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC public void unassignEdgeRuleChains(TenantId tenantId, EdgeId edgeId) { log.trace("Executing unassignEdgeRuleChains, edgeId [{}]", edgeId); Validator.validateId(edgeId, "Incorrect edgeId " + edgeId); - Edge edge = edgeDao.findById(tenantId, edgeId.getId()); + Edge edge = edgeService.findEdgeById(tenantId, edgeId); if (edge == null) { throw new DataValidationException("Can't unassign ruleChains from non-existent edge!"); } @@ -452,7 +453,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC public void updateEdgeRuleChains(TenantId tenantId, EdgeId edgeId) { log.trace("Executing updateEdgeRuleChains, edgeId [{}]", edgeId); Validator.validateId(edgeId, "Incorrect edgeId " + edgeId); - Edge edge = edgeDao.findById(tenantId, edgeId.getId()); + Edge edge = edgeService.findEdgeById(tenantId, edgeId); if (edge == null) { throw new DataValidationException("Can't update ruleChains for non-existent edge!"); } From eceb9f130903b52c0fe74f375170de3a0f28f42d Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Fri, 22 May 2020 18:52:44 +0300 Subject: [PATCH 11/52] Renamed class --- .../rpc/constructor/EntityViewUpdateMsgConstructor.java | 7 ++++--- common/edge-api/src/main/proto/edge.proto | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityViewUpdateMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityViewUpdateMsgConstructor.java index ef5445ac81..857986cb1f 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityViewUpdateMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityViewUpdateMsgConstructor.java @@ -26,6 +26,7 @@ import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.device.DeviceService; +import org.thingsboard.server.gen.edge.EdgeEntityType; import org.thingsboard.server.gen.edge.EntityViewUpdateMsg; import org.thingsboard.server.gen.edge.UpdateMsgType; @@ -42,17 +43,17 @@ public class EntityViewUpdateMsgConstructor { public EntityViewUpdateMsg constructEntityViewUpdatedMsg(UpdateMsgType msgType, EntityView entityView) { String relatedName; String relatedType; - org.thingsboard.server.gen.edge.EntityType relatedEntityType; + EdgeEntityType relatedEntityType; if (entityView.getEntityId().getEntityType().equals(EntityType.DEVICE)) { Device device = deviceService.findDeviceById(entityView.getTenantId(), new DeviceId(entityView.getEntityId().getId())); relatedName = device.getName(); relatedType = device.getType(); - relatedEntityType = org.thingsboard.server.gen.edge.EntityType.DEVICE; + relatedEntityType = EdgeEntityType.DEVICE; } else { Asset asset = assetService.findAssetById(entityView.getTenantId(), new AssetId(entityView.getEntityId().getId())); relatedName = asset.getName(); relatedType = asset.getType(); - relatedEntityType = org.thingsboard.server.gen.edge.EntityType.ASSET; + relatedEntityType = EdgeEntityType.ASSET; } EntityViewUpdateMsg.Builder builder = EntityViewUpdateMsg.newBuilder() .setMsgType(msgType) diff --git a/common/edge-api/src/main/proto/edge.proto b/common/edge-api/src/main/proto/edge.proto index a8aa95f824..5fca55f9fa 100644 --- a/common/edge-api/src/main/proto/edge.proto +++ b/common/edge-api/src/main/proto/edge.proto @@ -182,7 +182,7 @@ message EntityViewUpdateMsg { string type = 3; string relatedName = 4; string relatedType = 5; - EntityType relatedEntityType = 6; + EdgeEntityType relatedEntityType = 6; string groupName = 7; } @@ -238,7 +238,7 @@ message RuleChainMetadataRequestMsg { int64 ruleChainIdLSB = 2; } -enum EntityType { +enum EdgeEntityType { DEVICE = 0; ASSET = 1; } From 7ec1e369d59930d8f8fbce992673b5ae066ecb64 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Sat, 23 May 2020 19:51:03 +0300 Subject: [PATCH 12/52] Added user updated msg --- .../service/edge/EdgeContextComponent.java | 5 ++ .../service/edge/rpc/EdgeGrpcSession.java | 15 ++++- .../RuleChainUpdateMsgConstructor.java | 5 +- .../constructor/UserUpdateMsgConstructor.java | 62 +++++++++++++++++++ common/edge-api/src/main/proto/edge.proto | 18 +++--- 5 files changed, 91 insertions(+), 14 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/UserUpdateMsgConstructor.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 26b3d317c7..1e51106717 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 @@ -35,6 +35,7 @@ import org.thingsboard.server.service.edge.rpc.constructor.AssetUpdateMsgConstru import org.thingsboard.server.service.edge.rpc.constructor.DashboardUpdateMsgConstructor; import org.thingsboard.server.service.edge.rpc.constructor.DeviceUpdateMsgConstructor; import org.thingsboard.server.service.edge.rpc.constructor.EntityViewUpdateMsgConstructor; +import org.thingsboard.server.service.edge.rpc.constructor.UserUpdateMsgConstructor; import org.thingsboard.server.service.edge.rpc.init.InitEdgeService; import org.thingsboard.server.service.edge.rpc.constructor.RuleChainUpdateMsgConstructor; import org.thingsboard.server.service.queue.TbClusterService; @@ -120,6 +121,10 @@ public class EdgeContextComponent { @Autowired private DashboardUpdateMsgConstructor dashboardUpdateMsgConstructor; + @Lazy + @Autowired + private UserUpdateMsgConstructor userUpdateMsgConstructor; + @Lazy @Autowired private EdgeEventStorageSettings edgeEventStorageSettings; 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 249071058a..f701b6506b 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 @@ -349,6 +349,10 @@ public final class EdgeGrpcSession implements Closeable { Alarm alarm = objectMapper.readValue(entry.getData(), Alarm.class); onAlarmUpdated(msgType, alarm); break; + case USER: + User user = objectMapper.readValue(entry.getData(), User.class); + onUserUpdated(msgType, user); + break; } } @@ -405,7 +409,7 @@ public final class EdgeGrpcSession implements Closeable { private void onRuleChainUpdated(UpdateMsgType msgType, RuleChain ruleChain) { EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() - .setRuleChainUpdateMsg(ctx.getRuleChainUpdateMsgConstructor().constructRuleChainUpdatedMsg(edge, msgType, ruleChain)) + .setRuleChainUpdateMsg(ctx.getRuleChainUpdateMsgConstructor().constructRuleChainUpdatedMsg(edge.getRootRuleChainId(), msgType, ruleChain)) .build(); outputStream.onNext(ResponseMsg.newBuilder() .setEntityUpdateMsg(entityUpdateMsg) @@ -443,6 +447,15 @@ public final class EdgeGrpcSession implements Closeable { .build()); } + private void onUserUpdated(UpdateMsgType msgType, User user) { + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setUserUpdateMsg(ctx.getUserUpdateMsgConstructor().constructUserUpdatedMsg(msgType, user)) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + } + private UpdateMsgType getResponseMsgType(String msgType) { if (msgType.equals(SessionMsgType.POST_TELEMETRY_REQUEST.name()) || msgType.equals(SessionMsgType.POST_ATTRIBUTES_REQUEST.name()) || diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainUpdateMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainUpdateMsgConstructor.java index 92828c9e7e..204ea18bcd 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainUpdateMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainUpdateMsgConstructor.java @@ -20,6 +20,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.rule.NodeConnectionInfo; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo; @@ -42,13 +43,13 @@ public class RuleChainUpdateMsgConstructor { private static final ObjectMapper objectMapper = new ObjectMapper(); - public RuleChainUpdateMsg constructRuleChainUpdatedMsg(Edge edge, UpdateMsgType msgType, RuleChain ruleChain) { + public RuleChainUpdateMsg constructRuleChainUpdatedMsg(RuleChainId edgeRootRuleChainId, 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(ruleChain.getId().equals(edge.getRootRuleChainId())) + .setRoot(ruleChain.getId().equals(edgeRootRuleChainId)) .setDebugMode(ruleChain.isDebugMode()) .setConfiguration(JacksonUtil.toString(ruleChain.getConfiguration())); if (ruleChain.getFirstRuleNodeId() != null) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/UserUpdateMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/UserUpdateMsgConstructor.java new file mode 100644 index 0000000000..8ec8b7c9ea --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/UserUpdateMsgConstructor.java @@ -0,0 +1,62 @@ +/** + * Copyright © 2016-2020 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.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.security.UserCredentials; +import org.thingsboard.server.dao.user.UserService; +import org.thingsboard.server.dao.util.mapping.JacksonUtil; +import org.thingsboard.server.gen.edge.UpdateMsgType; +import org.thingsboard.server.gen.edge.UserUpdateMsg; + +@Component +@Slf4j +public class UserUpdateMsgConstructor { + + @Autowired + private UserService userService; + + public UserUpdateMsg constructUserUpdatedMsg(UpdateMsgType msgType, User user) { + UserUpdateMsg.Builder builder = UserUpdateMsg.newBuilder() + .setMsgType(msgType) + .setEmail(user.getEmail()) + .setAuthority(user.getAuthority().name()) + .setEnabled(false); + if (user.getFirstName() != null) { + builder.setFirstName(user.getFirstName()); + } + if (user.getLastName() != null) { + builder.setLastName(user.getLastName()); + } + if (user.getAdditionalInfo() != null) { + builder.setAdditionalInfo(JacksonUtil.toString(user.getAdditionalInfo())); + } + if (user.getAdditionalInfo() != null) { + builder.setAdditionalInfo(JacksonUtil.toString(user.getAdditionalInfo())); + } + if (msgType.equals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE) || + msgType.equals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE)) { + UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getTenantId(), user.getId()); + if (userCredentials != null) { + builder.setEnabled(userCredentials.isEnabled()).setPassword(userCredentials.getPassword()); + } + } + return builder.build(); + } +} diff --git a/common/edge-api/src/main/proto/edge.proto b/common/edge-api/src/main/proto/edge.proto index 5fca55f9fa..37e35bcea2 100644 --- a/common/edge-api/src/main/proto/edge.proto +++ b/common/edge-api/src/main/proto/edge.proto @@ -220,17 +220,13 @@ message CustomerUpdateMsg { message UserUpdateMsg { UpdateMsgType msgType = 1; - int64 idMSB = 2; - int64 idLSB = 3; - int64 customerIdMSB = 4; - int64 customerIdLSB = 5; - string email = 7; - string authority = 8; - string firstName = 9; - string lastName = 10; - string additionalInfo = 11; - bool enabled = 12; - string password = 13; + string email = 2; + string authority = 3; + string firstName = 4; + string lastName = 5; + string additionalInfo = 6; + bool enabled = 7; + string password = 8; } message RuleChainMetadataRequestMsg { From eb2ebf58d91e06961b51ef4ad281fdff1e7a9026 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Sat, 23 May 2020 20:49:13 +0300 Subject: [PATCH 13/52] Fixed comp issue --- .../rpc/constructor/UserUpdateMsgConstructor.java | 2 +- .../edge/rpc/init/DefaultInitEdgeService.java | 2 +- .../thingsboard/server/common/data/EdgeUtils.java | 15 +++++++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/UserUpdateMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/UserUpdateMsgConstructor.java index 8ec8b7c9ea..c227e840a9 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/UserUpdateMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/UserUpdateMsgConstructor.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/edge/rpc/init/DefaultInitEdgeService.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/DefaultInitEdgeService.java index 13afc300cd..848b67be84 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/DefaultInitEdgeService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/DefaultInitEdgeService.java @@ -230,7 +230,7 @@ public class DefaultInitEdgeService implements InitEdgeService { for (RuleChain ruleChain : pageData.getData()) { RuleChainUpdateMsg ruleChainUpdateMsg = ruleChainUpdateMsgConstructor.constructRuleChainUpdatedMsg( - edge, + edge.getRootRuleChainId(), UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, ruleChain); EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EdgeUtils.java b/common/data/src/main/java/org/thingsboard/server/common/data/EdgeUtils.java index 10008bc1a6..31449a7801 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/EdgeUtils.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/EdgeUtils.java @@ -1,3 +1,18 @@ +/** + * Copyright © 2016-2020 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 org.thingsboard.server.common.data.id.EdgeId; From 487bf9999eef982f764f6b2b19942ae64a174861 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Sun, 24 May 2020 19:46:02 +0300 Subject: [PATCH 14/52] Added user group name --- common/edge-api/src/main/proto/edge.proto | 1 + 1 file changed, 1 insertion(+) diff --git a/common/edge-api/src/main/proto/edge.proto b/common/edge-api/src/main/proto/edge.proto index 37e35bcea2..b721887d3d 100644 --- a/common/edge-api/src/main/proto/edge.proto +++ b/common/edge-api/src/main/proto/edge.proto @@ -227,6 +227,7 @@ message UserUpdateMsg { string additionalInfo = 6; bool enabled = 7; string password = 8; + string groupName = 9; } message RuleChainMetadataRequestMsg { From 7ec2b8d3106ee80b9aa2d80d6c1f6b55f5a42fc9 Mon Sep 17 00:00:00 2001 From: deaflynx Date: Mon, 25 May 2020 16:43:43 +0300 Subject: [PATCH 15/52] Edge assignment cleaned all over CE entities, except Edge page. Fixed some locals for Unassign edge --- msa/js-executor/package-lock.json | 1386 +++++++++++++++-- msa/web-ui/package-lock.json | 12 +- ui/package-lock.json | 2 +- ui/src/app/asset/asset.controller.js | 85 +- ui/src/app/asset/assign-to-edge.controller.js | 123 -- ui/src/app/asset/assign-to-edge.tpl.html | 76 - ui/src/app/asset/index.js | 2 - ui/src/app/customer/customer.controller.js | 20 - ui/src/app/dashboard/dashboards.controller.js | 73 - ui/src/app/dashboard/dashboards.tpl.html | 3 +- ui/src/app/dashboard/index.js | 2 - .../manage-assigned-edges.controller.js | 69 - .../dashboard/manage-assigned-edges.tpl.html | 51 - .../app/device/assign-to-edge.controller.js | 123 -- ui/src/app/device/assign-to-edge.tpl.html | 76 - ui/src/app/device/device.controller.js | 75 +- ui/src/app/device/index.js | 2 - .../entity-view/assign-to-edge.controller.js | 123 -- .../app/entity-view/assign-to-edge.tpl.html | 76 - .../app/entity-view/entity-view.controller.js | 115 -- ui/src/app/entity-view/index.js | 2 - ui/src/app/locale/locale.constant-en_US.json | 9 +- ui/src/app/rulechain/index.js | 2 - .../manage-assigned-edges.controller.js | 69 - .../rulechain/manage-assigned-edges.tpl.html | 51 - ui/src/app/rulechain/rulechains.controller.js | 74 - 26 files changed, 1295 insertions(+), 1406 deletions(-) delete mode 100644 ui/src/app/asset/assign-to-edge.controller.js delete mode 100644 ui/src/app/asset/assign-to-edge.tpl.html delete mode 100644 ui/src/app/dashboard/manage-assigned-edges.controller.js delete mode 100644 ui/src/app/dashboard/manage-assigned-edges.tpl.html delete mode 100644 ui/src/app/device/assign-to-edge.controller.js delete mode 100644 ui/src/app/device/assign-to-edge.tpl.html delete mode 100644 ui/src/app/entity-view/assign-to-edge.controller.js delete mode 100644 ui/src/app/entity-view/assign-to-edge.tpl.html delete mode 100644 ui/src/app/rulechain/manage-assigned-edges.controller.js delete mode 100644 ui/src/app/rulechain/manage-assigned-edges.tpl.html diff --git a/msa/js-executor/package-lock.json b/msa/js-executor/package-lock.json index 77d1d60b39..2d7b2b2726 100644 --- a/msa/js-executor/package-lock.json +++ b/msa/js-executor/package-lock.json @@ -1,9 +1,173 @@ { "name": "thingsboard-js-executor", - "version": "2.4.3", + "version": "2.5.0", "lockfileVersion": 1, "requires": true, "dependencies": { + "@azure/abort-controller": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.0.1.tgz", + "integrity": "sha512-wP2Jw6uPp8DEDy0n4KNidvwzDjyVV2xnycEIq7nPzj1rHyb/r+t3OPeNT1INZePP2wy5ZqlwyuyOMTi0ePyY1A==", + "requires": { + "tslib": "^1.9.3" + } + }, + "@azure/amqp-common": { + "version": "1.0.0-preview.15", + "resolved": "https://registry.npmjs.org/@azure/amqp-common/-/amqp-common-1.0.0-preview.15.tgz", + "integrity": "sha512-EoxNsVR7yLioNKRz5JBwQAE9pEdPVGCmmQbPKkZHP72vE5NhaLnOwHOCrk/311cuhJ8aQ60eiLUtF9J2XrEZyA==", + "requires": { + "@types/async-lock": "^1.1.0", + "@types/is-buffer": "^2.0.0", + "async-lock": "^1.1.3", + "buffer": "^5.2.1", + "debug": "^3.1.0", + "events": "^3.0.0", + "is-buffer": "^2.0.3", + "jssha": "^2.3.1", + "process": "^0.11.10", + "rhea": "^1.0.18", + "rhea-promise": "^0.1.15", + "stream-browserify": "^2.0.2", + "tslib": "^1.9.3", + "url": "^0.11.0", + "util": "^0.11.1" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "is-buffer": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", + "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "@azure/core-auth": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.1.2.tgz", + "integrity": "sha512-IUbP/f3v96dpHgXUwsAjUwDzjlUjawyUhWhGKKB6Qxy+iqppC/pVBPyc6kdpyTe7H30HN+4H3f0lar7Wp9Hx6A==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-tracing": "1.0.0-preview.8", + "@opentelemetry/api": "^0.6.1", + "tslib": "^1.10.0" + } + }, + "@azure/core-http": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@azure/core-http/-/core-http-1.1.2.tgz", + "integrity": "sha512-xeZpTs6caBIrRipqZs70jgrA+mAFxII5XrBzbOCELPs18n4QWfchB20F94ITAk3GuFVDaSBsOhVL3GP1J+ncGg==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.1.2", + "@azure/core-tracing": "1.0.0-preview.8", + "@azure/logger": "^1.0.0", + "@opentelemetry/api": "^0.6.1", + "@types/node-fetch": "^2.5.0", + "@types/tunnel": "^0.0.1", + "cross-env": "^6.0.3", + "form-data": "^3.0.0", + "node-fetch": "^2.6.0", + "process": "^0.11.10", + "tough-cookie": "^3.0.1", + "tslib": "^1.10.0", + "tunnel": "^0.0.6", + "uuid": "^3.3.2", + "xml2js": "^0.4.19" + }, + "dependencies": { + "form-data": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz", + "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "tough-cookie": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", + "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", + "requires": { + "ip-regex": "^2.1.0", + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + } + } + }, + "@azure/core-tracing": { + "version": "1.0.0-preview.8", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.8.tgz", + "integrity": "sha512-ZKUpCd7Dlyfn7bdc+/zC/sf0aRIaNQMDuSj2RhYRFe3p70hVAnYGp3TX4cnG2yoEALp/LTj/XnZGQ8Xzf6Ja/Q==", + "requires": { + "@opencensus/web-types": "0.0.7", + "@opentelemetry/api": "^0.6.1", + "tslib": "^1.10.0" + } + }, + "@azure/logger": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.0.tgz", + "integrity": "sha512-g2qLDgvmhyIxR3JVS8N67CyIOeFRKQlX/llxYJQr1OSGQqM3HTpVP8MjmjcEKbL/OIt2N9C9UFaNQuKOw1laOA==", + "requires": { + "tslib": "^1.9.3" + } + }, + "@azure/service-bus": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@azure/service-bus/-/service-bus-1.1.7.tgz", + "integrity": "sha512-wns3egBrP6UyT9CIPkM66KsOVJwit7VJT0P/t8PPPfUaO6yx3bEeZyVDq6WMiibnbIkgHtW85xXml4WDb+nPMw==", + "requires": { + "@azure/amqp-common": "1.0.0-preview.15", + "@azure/core-http": "^1.0.0", + "@opentelemetry/types": "^0.2.0", + "@types/is-buffer": "^2.0.0", + "@types/long": "^4.0.0", + "buffer": "^5.2.1", + "debug": "^4.1.1", + "is-buffer": "^2.0.3", + "long": "^4.0.0", + "process": "^0.11.10", + "rhea": "^1.0.21", + "rhea-promise": "^0.1.15", + "tslib": "^1.10.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "is-buffer": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", + "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, "@babel/parser": { "version": "7.4.5", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.5.tgz", @@ -19,6 +183,146 @@ "regenerator-runtime": "^0.13.2" } }, + "@google-cloud/paginator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-2.0.3.tgz", + "integrity": "sha512-kp/pkb2p/p0d8/SKUu4mOq8+HGwF8NPzHWkj+VKrIPQPyMRw8deZtrO/OcSiy9C/7bpfU5Txah5ltUNfPkgEXg==", + "requires": { + "arrify": "^2.0.0", + "extend": "^3.0.2" + } + }, + "@google-cloud/precise-date": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@google-cloud/precise-date/-/precise-date-1.0.3.tgz", + "integrity": "sha512-wWnDGh9y3cJHLuVEY8t6un78vizzMWsS7oIWKeFtPj+Ndy+dXvHW0HTx29ZUhen+tswSlQYlwFubvuRP5kKdzQ==" + }, + "@google-cloud/projectify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-1.0.4.tgz", + "integrity": "sha512-ZdzQUN02eRsmTKfBj9FDL0KNDIFNjBn/d6tHQmA/+FImH5DO6ZV8E7FzxMgAUiVAUq41RFAkb25p1oHOZ8psfg==" + }, + "@google-cloud/promisify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-1.0.4.tgz", + "integrity": "sha512-VccZDcOql77obTnFh0TbNED/6ZbbmHDf8UMNnzO1d5g9V0Htfm4k5cllY8P1tJsRKC3zWYGRLaViiupcgVjBoQ==" + }, + "@google-cloud/pubsub": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@google-cloud/pubsub/-/pubsub-1.7.3.tgz", + "integrity": "sha512-v+KdeaOS17WtHnsDf2bPGxKDT9HIRPYo3n+WsAEmvAzDHnh8q65mFcuYoQxuy2iRhmN/1ql2a0UU2tAAL7XZ8Q==", + "requires": { + "@google-cloud/paginator": "^2.0.0", + "@google-cloud/precise-date": "^1.0.0", + "@google-cloud/projectify": "^1.0.0", + "@google-cloud/promisify": "^1.0.0", + "@types/duplexify": "^3.6.0", + "@types/long": "^4.0.0", + "arrify": "^2.0.0", + "async-each": "^1.0.1", + "extend": "^3.0.2", + "google-auth-library": "^5.5.0", + "google-gax": "^1.14.2", + "is-stream-ended": "^0.1.4", + "lodash.snakecase": "^4.1.1", + "p-defer": "^3.0.0", + "protobufjs": "^6.8.1" + } + }, + "@grpc/grpc-js": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.0.4.tgz", + "integrity": "sha512-Qawt6HUrEmljQMPWnLnIXpcjelmtIAydi3M9awiG02WWJ1CmIvFEx4IOC1EsWUWUlabOGksRbpfvoIeZKFTNXw==", + "requires": { + "google-auth-library": "^6.0.0", + "semver": "^6.2.0" + }, + "dependencies": { + "gaxios": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-3.0.3.tgz", + "integrity": "sha512-PkzQludeIFhd535/yucALT/Wxyj/y2zLyrMwPcJmnLHDugmV49NvAi/vb+VUq/eWztATZCNcb8ue+ywPG+oLuw==", + "requires": { + "abort-controller": "^3.0.0", + "extend": "^3.0.2", + "https-proxy-agent": "^5.0.0", + "is-stream": "^2.0.0", + "node-fetch": "^2.3.0" + } + }, + "gcp-metadata": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.1.0.tgz", + "integrity": "sha512-r57SV28+olVsflPlKyVig3Muo/VDlcsObMtvDGOEtEJXj+DDE8bEl0coIkXh//hbkSDTvo+f5lbihZOndYXQQQ==", + "requires": { + "gaxios": "^3.0.0", + "json-bigint": "^0.3.0" + } + }, + "google-auth-library": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-6.0.0.tgz", + "integrity": "sha512-uLydy1t6SHN/EvYUJrtN3GCHFrnJ0c8HJjOxXiGjoTuYHIoCUT3jVxnzmjHwVnSdkfE9Akasm2rM6qG1COTXfQ==", + "requires": { + "arrify": "^2.0.0", + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "fast-text-encoding": "^1.0.0", + "gaxios": "^3.0.0", + "gcp-metadata": "^4.0.0", + "gtoken": "^5.0.0", + "jws": "^4.0.0", + "lru-cache": "^5.0.0" + } + }, + "google-p12-pem": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-3.0.1.tgz", + "integrity": "sha512-VlQgtozgNVVVcYTXS36eQz4PXPt9gIPqLOhHN0QiV6W6h4qSCNVKPtKC5INtJsaHHF2r7+nOIa26MJeJMTaZEQ==", + "requires": { + "node-forge": "^0.9.0" + } + }, + "gtoken": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.0.1.tgz", + "integrity": "sha512-33w4FNDkUcyIOq/TqyC+drnKdI4PdXmWp9lZzssyEQKuvu9ZFN3KttaSnDKo52U3E51oujVGop93mKxmqO8HHg==", + "requires": { + "gaxios": "^3.0.0", + "google-p12-pem": "^3.0.0", + "jws": "^4.0.0", + "mime": "^2.2.0" + } + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "requires": { + "yallist": "^3.0.2" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "@grpc/proto-loader": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.5.4.tgz", + "integrity": "sha512-HTM4QpI9B2XFkPz7pjwMyMgZchJ93TVkL3kWPW8GDMDKYxsMnmf4w2TNMJK7+KNiYHS5cJrCEAFlF+AwtXWVPA==", + "requires": { + "lodash.camelcase": "^4.3.0", + "protobufjs": "^6.8.6" + } + }, "@mrmlnc/readdir-enhanced": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", @@ -35,12 +339,110 @@ "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==", "dev": true }, + "@opencensus/web-types": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/@opencensus/web-types/-/web-types-0.0.7.tgz", + "integrity": "sha512-xB+w7ZDAu3YBzqH44rCmG9/RlrOmFuDPt/bpf17eJr8eZSrLt7nc7LnWdxM9Mmoj/YKMHpxRg28txu3TcpiL+g==" + }, + "@opentelemetry/api": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-0.6.1.tgz", + "integrity": "sha512-wpufGZa7tTxw7eAsjXJtiyIQ42IWQdX9iUQp7ACJcKo1hCtuhLU+K2Nv1U6oRwT1oAlZTE6m4CgWKZBhOiau3Q==", + "requires": { + "@opentelemetry/context-base": "^0.6.1" + } + }, + "@opentelemetry/context-base": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-base/-/context-base-0.6.1.tgz", + "integrity": "sha512-5bHhlTBBq82ti3qPT15TRxkYTFPPQWbnkkQkmHPtqiS1XcTB69cEKd3Jm7Cfi/vkPoyxapmePE9tyA7EzLt8SQ==" + }, + "@opentelemetry/types": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/types/-/types-0.2.0.tgz", + "integrity": "sha512-GtwNB6BNDdsIPAYEdpp3JnOGO/3AJxjPvny53s3HERBdXSJTGQw8IRhiaTEX0b3w9P8+FwFZde4k+qkjn67aVw==" + }, + "@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=" + }, + "@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=" + }, + "@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=", + "requires": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=" + }, + "@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=" + }, + "@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=" + }, + "@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=" + }, + "@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=" + }, + "@types/async-lock": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/async-lock/-/async-lock-1.1.2.tgz", + "integrity": "sha512-j9n4bb6RhgFIydBe0+kpjnBPYumDaDyU8zvbWykyVMkku+c2CSu31MZkLeaBfqIwU+XCxlDpYDfyMQRkM0AkeQ==" + }, + "@types/duplexify": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@types/duplexify/-/duplexify-3.6.0.tgz", + "integrity": "sha512-5zOA53RUlzN74bvrSGwjudssD9F3a797sDZQkiYpUOxW+WHaXTCPz4/d5Dgi6FKnOqZ2CpaTo0DhgIfsXAOE/A==", + "requires": { + "@types/node": "*" + } + }, "@types/events": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==", "dev": true }, + "@types/fs-extra": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.1.tgz", + "integrity": "sha512-TcUlBem321DFQzBNuz8p0CLLKp0VvF/XH9E4KHNmgwyp4E3AfgI5cjiIVZWlbfThBop2qxFIh4+LeY6hVWWZ2w==", + "requires": { + "@types/node": "*" + } + }, "@types/glob": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", @@ -52,6 +454,19 @@ "@types/node": "*" } }, + "@types/is-buffer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/is-buffer/-/is-buffer-2.0.0.tgz", + "integrity": "sha512-0f7N/e3BAz32qDYvgB4d2cqv1DqUwvGxHkXsrucICn8la1Vb6Yl6Eg8mPScGwUiqHJeE7diXlzaK+QMA9m4Gxw==", + "requires": { + "@types/node": "*" + } + }, + "@types/long": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", + "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==" + }, "@types/minimatch": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", @@ -61,8 +476,36 @@ "@types/node": { "version": "10.12.10", "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.10.tgz", - "integrity": "sha512-8xZEYckCbUVgK8Eg7lf5Iy4COKJ5uXlnIOnePN0WUwSQggy9tolM+tDJf7wMOnT/JT/W9xDYIaYggt3mRV2O5w==", - "dev": true + "integrity": "sha512-8xZEYckCbUVgK8Eg7lf5Iy4COKJ5uXlnIOnePN0WUwSQggy9tolM+tDJf7wMOnT/JT/W9xDYIaYggt3mRV2O5w==" + }, + "@types/node-fetch": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.7.tgz", + "integrity": "sha512-o2WVNf5UhWRkxlf6eq+jMZDu7kjgpgJfl4xVNlvryc95O/6F2ld8ztKX+qu+Rjyet93WAWm5LjeX9H5FGkODvw==", + "requires": { + "@types/node": "*", + "form-data": "^3.0.0" + }, + "dependencies": { + "form-data": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz", + "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + } + } + }, + "@types/tunnel": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.1.tgz", + "integrity": "sha512-AOqu6bQu5MSWwYvehMXLukFHnupHrpZ8nvgae5Ggie9UwzDR1CCwoXgSSWNZJuyOlCdfdsWMA5F2LlmvyoTv8A==", + "requires": { + "@types/node": "*" + } }, "abbrev": { "version": "1.1.1", @@ -70,11 +513,41 @@ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true }, + "abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "requires": { + "event-target-shim": "^5.0.0" + } + }, + "agent-base": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.0.tgz", + "integrity": "sha512-j1Q7cSCqN+AwrmDd+pzgqc0/NpC655x2bUf5ZjRIO77DcNBFmh+OgRNzF6OKdCC9RSCb19fGd99+bhXFdkRNqw==", + "requires": { + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, "ajv": { "version": "6.10.2", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", - "dev": true, "requires": { "fast-deep-equal": "^2.0.1", "fast-json-stable-stringify": "^2.0.0", @@ -82,6 +555,42 @@ "uri-js": "^4.2.2" } }, + "amqplib": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/amqplib/-/amqplib-0.5.6.tgz", + "integrity": "sha512-J4TR0WAMPBHN+tgTuhNsSObfM9eTVTZm/FNw0LyaGfbiLsBxqSameDNYpChUFXW4bnTKHDXy0ab+nuLhumnRrQ==", + "requires": { + "bitsyntax": "~0.1.0", + "bluebird": "^3.5.2", + "buffer-more-ints": "~1.0.0", + "readable-stream": "1.x >=1.1.9", + "safe-buffer": "~5.1.2", + "url-parse": "~1.4.3" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, "ansi-align": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", @@ -190,11 +699,15 @@ "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", "dev": true }, + "arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==" + }, "asn1": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "dev": true, "requires": { "safer-buffer": "~2.1.0" } @@ -202,8 +715,7 @@ "assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" }, "assign-symbols": { "version": "1.0.0", @@ -222,14 +734,17 @@ "async-each": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", - "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", - "dev": true + "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=" + }, + "async-lock": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.2.4.tgz", + "integrity": "sha512-UBQJC2pbeyGutIfYmErGc9RaJYnpZ1FHaxuKwb0ahvGiiCkPUf3p67Io+YLPmmv3RHY+mF6JEtNW8FlHsraAaA==" }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, "atob": { "version": "2.1.2", @@ -237,17 +752,121 @@ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true }, + "aws-sdk": { + "version": "2.683.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.683.0.tgz", + "integrity": "sha512-mi1175pJMbQhCWhBEQ5ccQ3SDE+SJzbapuAQtcb7tdLLV7dPKf4zQXhpqK1uG0dysm8NhsNtgxwA2diYOKWlTg==", + "requires": { + "buffer": "4.9.1", + "events": "1.1.1", + "ieee754": "1.1.13", + "jmespath": "0.15.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "uuid": "3.3.2", + "xml2js": "0.4.19" + }, + "dependencies": { + "buffer": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" + }, + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + }, + "sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" + }, + "url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "xml2js": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~9.0.1" + } + }, + "xmlbuilder": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" + } + } + }, "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" }, "aws4": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", - "dev": true + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" + }, + "azure-common": { + "version": "0.9.22", + "resolved": "https://registry.npmjs.org/azure-common/-/azure-common-0.9.22.tgz", + "integrity": "sha512-0r9tK9D+1xl2/VPVtfmGmtkMqfooiBLS87fX+Ab0hOCPVVe/6CgVC4in0wSf2Ta8r65DbvxV5P4/t8fp8Q3EsQ==", + "requires": { + "dateformat": "1.0.2-1.2.3", + "duplexer": "~0.1.1", + "envconf": "~0.0.4", + "request": "^2.81.0", + "through": "~2.3.4", + "tunnel": "~0.0.2", + "underscore": "1.4.x", + "validator": "^9.4.1", + "xml2js": "^0.4.19", + "xmlbuilder": "0.4.3" + }, + "dependencies": { + "underscore": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz", + "integrity": "sha1-YaajIBBiKvoHljvzJSA88SI51gQ=" + }, + "xmlbuilder": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-0.4.3.tgz", + "integrity": "sha1-xGFLp04K0ZbmCcknLNnh3bKKilg=" + } + } + }, + "azure-sb": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/azure-sb/-/azure-sb-0.11.1.tgz", + "integrity": "sha512-ZYgPeSDMD99i/Em+6wT78zvBkJ/dbh2ypb4DbqQ1Flaif5vWJFzC/iKxxcq/vq+THWoO3+UbqWa0JNXnW3zAvw==", + "requires": { + "azure-common": "^0.9.22", + "mpns": "2.1.3", + "underscore": "^1.8.3", + "wns": "~0.5.3" + } }, "balanced-match": { "version": "1.0.0", @@ -310,21 +929,45 @@ } } }, + "base64-js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" + }, "bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "dev": true, "requires": { "tweetnacl": "^0.14.3" } }, + "bignumber.js": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-7.2.1.tgz", + "integrity": "sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ==" + }, "binary-extensions": { "version": "1.12.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.12.0.tgz", "integrity": "sha512-DYWGk01lDcxeS/K9IHPGWfT8PsJmbXRtRd2Sx72Tnb8pcYZQFF1oSDb8hJtS1vhp212q1Rzi5dUf9+nq0o9UIg==", "dev": true }, + "bitsyntax": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/bitsyntax/-/bitsyntax-0.1.0.tgz", + "integrity": "sha512-ikAdCnrloKmFOugAfxWws89/fPc+nw0OOG1IzIE72uSOg/A3cYptKCjSUhDTuj7fhsJtzkzlv7l3b8PzRHLN0Q==", + "requires": { + "buffer-more-ints": "~1.0.0", + "debug": "~2.6.9", + "safe-buffer": "~5.1.2" + } + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, "boxen": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", @@ -412,6 +1055,25 @@ } } }, + "buffer": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", + "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + }, + "buffer-more-ints": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-more-ints/-/buffer-more-ints-1.0.0.tgz", + "integrity": "sha512-EMetuGFz5SLsT0QTnXzINh4Ksr+oo4i+UGTXEshiGCQWnsgSs7ZhJ8fzlwQ+OzEMs0MpDAMr1hxnblp5a4vcHg==" + }, "byline": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz", @@ -456,8 +1118,7 @@ "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, "chalk": { "version": "2.4.1", @@ -590,7 +1251,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, "requires": { "delayed-stream": "~1.0.0" } @@ -649,6 +1309,52 @@ "capture-stack-trace": "^1.0.0" } }, + "cross-env": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-6.0.3.tgz", + "integrity": "sha512-+KqxF6LCvfhWvADcDPqo64yVIB31gv/jQulX2NGzKS/g3GEVz6/pt4wjHFtFWsHMddebWD/sDthJemzM4MaAag==", + "requires": { + "cross-spawn": "^7.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.2.tgz", + "integrity": "sha512-PD6G8QG3S4FK/XCGFbEQrDqO2AnMMsy0meR7lerlIOHAAbkuavGU/pOqprrlvfTNjvowivTeBsjebAL0NSoMxw==", + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } + } + } + }, "cross-spawn": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", @@ -675,16 +1381,19 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, "requires": { "assert-plus": "^1.0.0" } }, + "dateformat": { + "version": "1.0.2-1.2.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.2-1.2.3.tgz", + "integrity": "sha1-sCIMAt6YYXQztyhRz0fePfLNvuk=" + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, "requires": { "ms": "2.0.0" } @@ -751,8 +1460,7 @@ "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, "diagnostics": { "version": "1.1.1", @@ -782,35 +1490,71 @@ "is-obj": "^1.0.0" } }, + "duplexer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=" + }, "duplexer3": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", "dev": true }, + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, "ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "dev": true, "requires": { "jsbn": "~0.1.0", "safer-buffer": "^2.1.0" } }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, "enabled": { "version": "1.0.2", - "resolved": "http://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz", "integrity": "sha1-ll9lE9LC0cX0ZStkouM5ZGf8L5M=", "requires": { "env-variable": "0.0.x" } }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, "env-variable": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/env-variable/-/env-variable-0.0.5.tgz", "integrity": "sha512-zoB603vQReOFvTg5xMl9I1P2PnHsHQQKTEowsKKD7nseUfJq6UWzK+4YtlWUO1nhiQUxe6XMkk+JleSZD1NZFA==" }, + "envconf": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/envconf/-/envconf-0.0.4.tgz", + "integrity": "sha1-hWda+6I3xD+Y3i1GrcDlMqTc9Is=" + }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -862,6 +1606,16 @@ "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", "dev": true }, + "event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" + }, + "events": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.1.0.tgz", + "integrity": "sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg==" + }, "execa": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", @@ -921,8 +1675,7 @@ "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, "extend-shallow": { "version": "3.0.2", @@ -1013,14 +1766,12 @@ "extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" }, "fast-deep-equal": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", - "dev": true + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" }, "fast-glob": { "version": "2.2.7", @@ -1039,8 +1790,7 @@ "fast-json-stable-stringify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", - "dev": true + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" }, "fast-levenshtein": { "version": "2.0.6", @@ -1053,9 +1803,14 @@ "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.6.tgz", "integrity": "sha512-q8BZ89jjc+mz08rSxROs8VsrBBcn1SIw1kq9NjolL509tkABRk9io01RAjSaEv1Xb2uFLt8VtRiZbGp5H8iDtg==" }, + "fast-text-encoding": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.2.tgz", + "integrity": "sha512-5rQdinSsycpzvAoHga2EDn+LRX1d5xLFsuNG0Kg61JrAT/tASXcLL0nf/33v+sAxlQcfYmWbTURa1mmAf55jGw==" + }, "fecha": { "version": "2.3.3", - "resolved": "http://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz", "integrity": "sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg==" }, "file-stream-rotator": { @@ -1098,14 +1853,12 @@ "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" }, "form-data": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.6", @@ -1189,12 +1942,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1209,17 +1964,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -1336,7 +2094,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -1348,6 +2107,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -1362,6 +2122,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -1473,7 +2234,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -1485,6 +2247,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -1606,6 +2369,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -1677,9 +2441,37 @@ } } }, + "gaxios": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-2.3.4.tgz", + "integrity": "sha512-US8UMj8C5pRnao3Zykc4AAVr+cffoNKRTg9Rsf2GiuZCW69vgJj38VK2PzlPuQU73FZ/nTk9/Av6/JGcE1N9vA==", + "requires": { + "abort-controller": "^3.0.0", + "extend": "^3.0.2", + "https-proxy-agent": "^5.0.0", + "is-stream": "^2.0.0", + "node-fetch": "^2.3.0" + }, + "dependencies": { + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" + } + } + }, + "gcp-metadata": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-3.5.0.tgz", + "integrity": "sha512-ZQf+DLZ5aKcRpLzYUyBS3yo3N0JSa82lNDO8rj3nMSlovLcz2riKFBsYgDzeXcv75oo5eqB2lx+B14UvPoCRnA==", + "requires": { + "gaxios": "^2.1.0", + "json-bigint": "^0.3.0" + } + }, "get-stream": { "version": "3.0.0", - "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", "dev": true }, @@ -1693,7 +2485,6 @@ "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, "requires": { "assert-plus": "^1.0.0" } @@ -1772,9 +2563,72 @@ } } }, + "google-auth-library": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-5.10.1.tgz", + "integrity": "sha512-rOlaok5vlpV9rSiUu5EpR0vVpc+PhN62oF4RyX/6++DG1VsaulAFEMlDYBLjJDDPI6OcNOCGAKy9UVB/3NIDXg==", + "requires": { + "arrify": "^2.0.0", + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "fast-text-encoding": "^1.0.0", + "gaxios": "^2.1.0", + "gcp-metadata": "^3.4.0", + "gtoken": "^4.1.0", + "jws": "^4.0.0", + "lru-cache": "^5.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "requires": { + "yallist": "^3.0.2" + } + } + } + }, + "google-gax": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-1.15.3.tgz", + "integrity": "sha512-3JKJCRumNm3x2EksUTw4P1Rad43FTpqrtW9jzpf3xSMYXx+ogaqTM1vGo7VixHB4xkAyATXVIa3OcNSh8H9zsQ==", + "requires": { + "@grpc/grpc-js": "~1.0.3", + "@grpc/proto-loader": "^0.5.1", + "@types/fs-extra": "^8.0.1", + "@types/long": "^4.0.0", + "abort-controller": "^3.0.0", + "duplexify": "^3.6.0", + "google-auth-library": "^5.0.0", + "is-stream-ended": "^0.1.4", + "lodash.at": "^4.6.0", + "lodash.has": "^4.5.2", + "node-fetch": "^2.6.0", + "protobufjs": "^6.8.9", + "retry-request": "^4.0.0", + "semver": "^6.0.0", + "walkdir": "^0.4.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "google-p12-pem": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-2.0.4.tgz", + "integrity": "sha512-S4blHBQWZRnEW44OcR7TL9WR+QCqByRvhNDZ/uuQfpxywfupikf/miba8js1jZi6ZOGv5slgSuoshCWh6EMDzg==", + "requires": { + "node-forge": "^0.9.0" + } + }, "got": { "version": "6.7.1", - "resolved": "http://registry.npmjs.org/got/-/got-6.7.1.tgz", + "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", "dev": true, "requires": { @@ -1797,17 +2651,26 @@ "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", "dev": true }, + "gtoken": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-4.1.4.tgz", + "integrity": "sha512-VxirzD0SWoFUo5p8RDP8Jt2AGyOmyYcT/pOUgDKJCK+iSw0TMqwrVfY37RXTNmoKwrzmDHSk0GMT9FsgVmnVSA==", + "requires": { + "gaxios": "^2.1.0", + "google-p12-pem": "^2.0.0", + "jws": "^4.0.0", + "mime": "^2.2.0" + } + }, "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" }, "har-validator": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", - "dev": true, "requires": { "ajv": "^6.5.5", "har-schema": "^2.0.0" @@ -1855,13 +2718,41 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, "requires": { "assert-plus": "^1.0.0", "jsprim": "^1.2.2", "sshpk": "^1.7.0" } }, + "https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "requires": { + "agent-base": "6", + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + }, "ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", @@ -1917,6 +2808,11 @@ "p-is-promise": "^2.0.0" } }, + "ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=" + }, "is-accessor-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", @@ -2064,7 +2960,7 @@ }, "is-obj": { "version": "1.0.1", - "resolved": "http://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", "dev": true }, @@ -2103,11 +2999,15 @@ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" }, + "is-stream-ended": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-stream-ended/-/is-stream-ended-0.1.4.tgz", + "integrity": "sha512-xj0XPvmr7bQFTvirqnFr50o0hQIh6ZItDqloxt5aJrR4NQsYeSsyFQERYGCAzfindAcnKjINnwEEgLx4IqVzQw==" + }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" }, "is-windows": { "version": "1.0.2", @@ -2123,8 +3023,7 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, "isobject": { "version": "3.0.1", @@ -2135,8 +3034,12 @@ "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "jmespath": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", + "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=" }, "js-yaml": { "version": "3.12.0", @@ -2150,26 +3053,30 @@ "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "json-bigint": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-0.3.0.tgz", + "integrity": "sha1-DM2RLEuCcNBfBW+9E4FLU9OCWx4=", + "requires": { + "bignumber.js": "^7.0.0" + } }, "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" }, "json5": { "version": "1.0.1", @@ -2192,7 +3099,6 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, "requires": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", @@ -2200,10 +3106,34 @@ "verror": "1.10.0" } }, + "jssha": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/jssha/-/jssha-2.4.2.tgz", + "integrity": "sha512-/jsi/9C0S70zfkT/4UlKQa5E1xKurDnXcQizcww9JSR/Fv+uIbWM2btG+bFcL3iNoK9jIGS0ls9HWLr1iw0kFg==" + }, + "jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "requires": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, "kafkajs": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/kafkajs/-/kafkajs-1.11.0.tgz", - "integrity": "sha512-dLRCcFIBygZucR+e8U2ZqH2wgMrAu114K0szUyUseJoeOii3cG5bHZPIdqKecXxI6begPVCfGS3R0nJY4zHW2A==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/kafkajs/-/kafkajs-1.12.0.tgz", + "integrity": "sha512-Izkd9iFRgeeKaHEgVpGQH08ygzCbHSxTbnu8W3G3uiNaVjGibUTmTwjv1Qf2M8NORXcPfzwVyg6bBlVj4SKr9g==", "requires": { "long": "^4.0.0" } @@ -2246,12 +3176,32 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" }, + "lodash.at": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.at/-/lodash.at-4.6.0.tgz", + "integrity": "sha1-k83OZk8KGZTqM9181A4jr9EbD/g=" + }, + "lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=" + }, "lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", "dev": true }, + "lodash.has": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/lodash.has/-/lodash.has-4.5.2.tgz", + "integrity": "sha1-0Z9NwQlQWMzL4rDN9O4P5Ko3yGI=" + }, + "lodash.snakecase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", + "integrity": "sha1-OdcUo1NXFHg3rv1ktdy7Fr7Nj40=" + }, "logform": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/logform/-/logform-1.10.0.tgz", @@ -2343,17 +3293,20 @@ "to-regex": "^3.0.2" } }, + "mime": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.5.tgz", + "integrity": "sha512-3hQhEUF027BuxZjQA3s7rIv/7VCQPa27hN9u9g87sEkWaKwQPuXOkVKtOeiyUrnWqTDiOs8Ed2rwg733mB0R5w==" + }, "mime-db": { "version": "1.40.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", - "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==", - "dev": true + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" }, "mime-types": { "version": "2.1.24", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", - "dev": true, "requires": { "mime-db": "1.40.0" } @@ -2369,7 +3322,7 @@ }, "minimist": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" }, "mixin-deep": { @@ -2415,11 +3368,15 @@ "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.2.tgz", "integrity": "sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y=" }, + "mpns": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/mpns/-/mpns-2.1.3.tgz", + "integrity": "sha512-gPLNoVqwYoKUmNYZ2shMSdaE2XvHSRxWNzyG4DUi6Av7MSujyeOw/nj61nnQeuV/vke5E0Dni468xn0qxTHIZQ==" + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "multistream": { "version": "2.1.1", @@ -2457,6 +3414,16 @@ "to-regex": "^3.0.1" } }, + "node-fetch": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", + "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" + }, + "node-forge": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.1.tgz", + "integrity": "sha512-G6RlQt5Sb4GMBzXvhfkeFmbqR6MzhtnT7VTHuLadjkii3rdYHNdw0m8zA4BTxVIh68FicCQ2NSUANpsqkr9jvQ==" + }, "nodemon": { "version": "1.18.7", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-1.18.7.tgz", @@ -2522,8 +3489,7 @@ "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" }, "object-copy": { "version": "0.1.0", @@ -2583,7 +3549,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "requires": { "wrappy": "1" } @@ -2613,6 +3578,11 @@ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, + "p-defer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-3.0.0.tgz", + "integrity": "sha512-ugZxsxmtTln604yeYd29EGrNhazN2lywetzpKhfmQjW/VJmhpDmWbiX+h0zL8V91R0UXkhb3KtPmyq9PZw3aYw==" + }, "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", @@ -2651,7 +3621,7 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, @@ -2685,8 +3655,7 @@ "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, "pify": { "version": "3.0.0", @@ -2795,6 +3764,11 @@ "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", "dev": true }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" + }, "process-nextick-args": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", @@ -2806,6 +3780,33 @@ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true }, + "protobufjs": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.9.0.tgz", + "integrity": "sha512-LlGVfEWDXoI/STstRDdZZKb/qusoAWUnmLg9R8OLSO473mBLWHowx8clbX5/+mKDEI+v7GzjoK9tRPZMMcoTrg==", + "requires": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.1", + "@types/node": "^13.7.0", + "long": "^4.0.0" + }, + "dependencies": { + "@types/node": { + "version": "13.13.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.9.tgz", + "integrity": "sha512-EPZBIGed5gNnfWCiwEIwTE2Jdg4813odnG8iNPMQGrqVxrI+wL68SPtPeCX+ZxGBaA6pKAVc6jaKgP/Q0QzfdQ==" + } + } + }, "pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", @@ -2815,8 +3816,7 @@ "psl": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.2.0.tgz", - "integrity": "sha512-GEn74ZffufCmkDDLNcl3uuyF/aSD6exEyh1v/ZSdAomB82t6G9hzJVRx0jBmLDW+VfZqks3aScmMw9DszwUalA==", - "dev": true + "integrity": "sha512-GEn74ZffufCmkDDLNcl3uuyF/aSD6exEyh1v/ZSdAomB82t6G9hzJVRx0jBmLDW+VfZqks3aScmMw9DszwUalA==" }, "pstree.remy": { "version": "1.1.2", @@ -2827,14 +3827,22 @@ "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + }, + "querystringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz", + "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==" }, "rc": { "version": "1.2.8", @@ -2930,7 +3938,6 @@ "version": "2.88.0", "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", - "dev": true, "requires": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", @@ -2963,6 +3970,11 @@ "throttleit": "^1.0.0" } }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + }, "resolve": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.6.0.tgz", @@ -2984,6 +3996,63 @@ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", "dev": true }, + "retry-request": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-4.1.1.tgz", + "integrity": "sha512-BINDzVtLI2BDukjWmjAIRZ0oglnCAkpP2vQjM3jdLhmT62h0xnQgciPwBRDAvHqpkPT2Wo1XuUyLyn6nbGrZQQ==", + "requires": { + "debug": "^4.1.1", + "through2": "^3.0.1" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "rhea": { + "version": "1.0.21", + "resolved": "https://registry.npmjs.org/rhea/-/rhea-1.0.21.tgz", + "integrity": "sha512-9ddxyJR0nlWmynukzZTWN+bSYWu7KLHVMkIH/7PpFG5RHfV5t7zXIfZ6rqJSJe9wBAgnNr2Xz41KM2nPujWiFQ==", + "requires": { + "debug": "0.8.0 - 3.5.0" + } + }, + "rhea-promise": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/rhea-promise/-/rhea-promise-0.1.15.tgz", + "integrity": "sha512-+6uilZXSJGyiqVeHQI3Krv6NTAd8cWRCY2uyCxmzR4/5IFtBqqFem1HV2OiwSj0Gu7OFChIJDfH2JyjN7J0vRA==", + "requires": { + "debug": "^3.1.0", + "rhea": "^1.0.4", + "tslib": "^1.9.3" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -2991,7 +4060,7 @@ }, "safe-regex": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, "requires": { @@ -3001,8 +4070,12 @@ "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, "semver": { "version": "5.6.0", @@ -3226,7 +4299,6 @@ "version": "1.16.1", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "dev": true, "requires": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", @@ -3265,6 +4337,15 @@ } } }, + "stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, "stream-meter": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/stream-meter/-/stream-meter-1.0.4.tgz", @@ -3274,6 +4355,11 @@ "readable-stream": "^2.1.4" } }, + "stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -3284,7 +4370,7 @@ }, "strip-eof": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, @@ -3323,6 +4409,19 @@ "integrity": "sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw=", "dev": true }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "through2": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", + "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", + "requires": { + "readable-stream": "2 || 3" + } + }, "timed-out": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", @@ -3384,7 +4483,6 @@ "version": "2.4.3", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", - "dev": true, "requires": { "psl": "^1.1.24", "punycode": "^1.4.1" @@ -3393,8 +4491,7 @@ "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" } } }, @@ -3403,11 +4500,20 @@ "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" }, + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" + }, + "tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" + }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, "requires": { "safe-buffer": "^5.0.1" } @@ -3415,8 +4521,7 @@ "tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" }, "type-check": { "version": "0.3.2", @@ -3442,6 +4547,11 @@ "debug": "^2.2.0" } }, + "underscore": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.10.2.tgz", + "integrity": "sha512-N4P+Q/BuyuEKFJ43B9gYuOj4TQUHXX+j2FqguVOpjkssLUUrnJofCcBccJSCoeturDoZU6GorDTHSvUDlSQbTg==" + }, "union-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", @@ -3577,7 +4687,6 @@ "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "dev": true, "requires": { "punycode": "^2.1.0" } @@ -3588,6 +4697,31 @@ "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", "dev": true }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + } + } + }, + "url-parse": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz", + "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==", + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, "url-parse-lax": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", @@ -3603,6 +4737,14 @@ "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", "dev": true }, + "util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "requires": { + "inherits": "2.0.3" + } + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -3611,25 +4753,38 @@ "uuid": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", - "dev": true + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" }, "uuid-parse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/uuid-parse/-/uuid-parse-1.0.0.tgz", "integrity": "sha1-9GV3F2JLDkuIrzb5jYlYmlu+5Wk=" }, + "uuid-random": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/uuid-random/-/uuid-random-1.3.0.tgz", + "integrity": "sha512-FSIlv8RFRPOjcHeDYStV7u6aJRfp+THrcWkbAJpw51JCyQLDxsFz+4dHgTYP8hSpZeSMXBpb/1qrK4bodXpSRA==" + }, + "validator": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/validator/-/validator-9.4.1.tgz", + "integrity": "sha512-YV5KjzvRmSyJ1ee/Dm5UED0G+1L4GZnLN3w6/T+zZm8scVua4sOhYKWTUrKa0H/tMiJyO9QLHMPN+9mB/aMunA==" + }, "verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, "requires": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", "extsprintf": "^1.2.0" } }, + "walkdir": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/walkdir/-/walkdir-0.4.1.tgz", + "integrity": "sha512-3eBwRyEln6E1MSzcxcVpQIhRG8Q1jLvEqRmCZqS3dsfXEDR/AhOF4d+jHg1qvDCpYaVRZjENPQyrVxAkQqxPgQ==" + }, "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", @@ -3729,6 +4884,11 @@ "triple-beam": "^1.2.0" } }, + "wns": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/wns/-/wns-0.5.4.tgz", + "integrity": "sha512-WYiJ7khIwUGBD5KAm+YYmwJDDRzFRs4YGAjtbFSoRIdbn9Jcix3p9khJmpvBTXGommaKkvduAn+pc9l4d9yzVQ==" + }, "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", @@ -3738,8 +4898,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "write-file-atomic": { "version": "2.3.0", @@ -3758,11 +4917,24 @@ "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=", "dev": true }, + "xml2js": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", + "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + } + }, + "xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" + }, "yallist": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", - "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", - "dev": true + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==" } } } diff --git a/msa/web-ui/package-lock.json b/msa/web-ui/package-lock.json index 1646e35050..59235f6911 100644 --- a/msa/web-ui/package-lock.json +++ b/msa/web-ui/package-lock.json @@ -1,6 +1,6 @@ { "name": "thingsboard-web-ui", - "version": "2.4.3", + "version": "2.5.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -1149,7 +1149,7 @@ }, "fecha": { "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz", + "resolved": "http://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz", "integrity": "sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg==" }, "file-stream-rotator": { @@ -2354,7 +2354,7 @@ }, "json5": { "version": "1.0.1", - "resolved": "http://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", "requires": { "minimist": "^1.2.0" @@ -2581,7 +2581,7 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, "requires": { @@ -2590,7 +2590,7 @@ "dependencies": { "minimist": { "version": "0.0.8", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true } @@ -2859,7 +2859,7 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, diff --git a/ui/package-lock.json b/ui/package-lock.json index 3319d85cfe..2e38738133 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -1,6 +1,6 @@ { "name": "thingsboard", - "version": "2.4.3", + "version": "2.5.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/ui/src/app/asset/asset.controller.js b/ui/src/app/asset/asset.controller.js index a317952032..d863133d4f 100644 --- a/ui/src/app/asset/asset.controller.js +++ b/ui/src/app/asset/asset.controller.js @@ -19,7 +19,6 @@ import addAssetTemplate from './add-asset.tpl.html'; import assetCard from './asset-card.tpl.html'; import assignToCustomerTemplate from './assign-to-customer.tpl.html'; import addAssetsToCustomerTemplate from './add-assets-to-customer.tpl.html'; -import assignToEdgeTemplate from './assign-to-edge.tpl.html'; import addAssetsToEdgeTemplate from './add-assets-to-edge.tpl.html'; /* eslint-enable import/no-unresolved, import/default */ @@ -219,32 +218,6 @@ export function AssetController($rootScope, userService, assetService, customerS } }); - assetActionsList.push({ - onAction: function ($event, item) { - assignToEdge($event, [ item.id.id ]); - }, - name: function() { return $translate.instant('action.assign') }, - details: function() { return $translate.instant('asset.assign-to-edge') }, - icon: "wifi_tethering", - isEnabled: function(asset) { - return asset && (!asset.edgeId || asset.edgeId.id === types.id.nullUid); - } - } - ); - - assetActionsList.push({ - onAction: function ($event, item) { - unassignFromEdge($event, item, false); - }, - name: function() { return $translate.instant('action.unassign') }, - details: function() { return $translate.instant('asset.unassign-from-edge') }, - icon: "portable_wifi_off", - isEnabled: function(asset) { - return asset && asset.edgeId && asset.edgeId.id !== types.id.nullUid; - } - } - ); - assetActionsList.push( { onAction: function ($event, item) { @@ -269,19 +242,6 @@ export function AssetController($rootScope, userService, assetService, customerS } ); - assetGroupActionsList.push( - { - onAction: function ($event, items) { - assignAssetsToEdge($event, items); - }, - name: function() { return $translate.instant('asset.assign-assets') }, - details: function(selectedCount) { - return $translate.instant('asset.assign-assets-text', {count: selectedCount}, "messageformat"); - }, - icon: "wifi_tethering" - } - ); - assetGroupActionsList.push( { onAction: function ($event) { @@ -391,7 +351,7 @@ export function AssetController($rootScope, userService, assetService, customerS }, name: function() { return $translate.instant('asset.unassign-assets') }, details: function(selectedCount) { - return $translate.instant('asset.unassign-assets-action-title', {count: selectedCount}, "messageformat"); + return $translate.instant('asset.unassign-assets-from-edge-action-title', {count: selectedCount}, "messageformat"); }, icon: "assignment_return" } @@ -619,41 +579,6 @@ export function AssetController($rootScope, userService, assetService, customerS }); } - function assignToEdge($event, assetIds) { - if ($event) { - $event.stopPropagation(); - } - var pageSize = 10; - edgeService.getEdges({limit: pageSize, textSearch: ''}).then( - function success(_edges) { - var edges = { - pageSize: pageSize, - data: _edges.data, - nextPageLink: _edges.nextPageLink, - selection: null, - hasNext: _edges.hasNext, - pending: false - }; - if (edges.hasNext) { - edges.nextPageLink.limit = pageSize; - } - $mdDialog.show({ - controller: 'AssignAssetToEdgeController', - controllerAs: 'vm', - templateUrl: assignToEdgeTemplate, - locals: {assetIds: assetIds, edges: edges}, - parent: angular.element($document[0].body), - fullscreen: true, - targetEvent: $event - }).then(function () { - vm.grid.refreshList(); - }, function () { - }); - }, - function fail() { - }); - } - function addAssetsToEdge($event) { if ($event) { $event.stopPropagation(); @@ -690,14 +615,6 @@ export function AssetController($rootScope, userService, assetService, customerS }); } - function assignAssetsToEdge($event, items) { - var assetIds = []; - for (var id in items.selections) { - assetIds.push(id); - } - assignToEdge($event, assetIds); - } - function unassignFromEdge($event, asset) { if ($event) { $event.stopPropagation(); diff --git a/ui/src/app/asset/assign-to-edge.controller.js b/ui/src/app/asset/assign-to-edge.controller.js deleted file mode 100644 index 3351b46ae9..0000000000 --- a/ui/src/app/asset/assign-to-edge.controller.js +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright © 2016-2020 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. - */ -/*@ngInject*/ -export default function AssignAssetToEdgeController(edgeService, assetService, $mdDialog, $q, assetIds, edges) { - - var vm = this; - - vm.edges = edges; - vm.searchText = ''; - - vm.assign = assign; - vm.cancel = cancel; - vm.isEdgeSelected = isEdgeSelected; - vm.hasData = hasData; - vm.noData = noData; - vm.searchEdgeTextUpdated = searchEdgeTextUpdated; - vm.toggleEdgeSelection = toggleEdgeSelection; - - vm.theEdges = { - getItemAtIndex: function (index) { - if (index > vm.edges.data.length) { - vm.theEdges.fetchMoreItems_(index); - return null; - } - var item = vm.edges.data[index]; - if (item) { - item.indexNumber = index + 1; - } - return item; - }, - - getLength: function () { - if (vm.edges.hasNext) { - return vm.edges.data.length + vm.edges.nextPageLink.limit; - } else { - return vm.edges.data.length; - } - }, - - fetchMoreItems_: function () { - if (vm.edges.hasNext && !vm.edges.pending) { - vm.edges.pending = true; - edgeService.getEdges(vm.edges.nextPageLink).then( - function success(edges) { - vm.edges.data = vm.edges.data.concat(edges.data); - vm.edges.nextPageLink = edges.nextPageLink; - vm.edges.hasNext = edges.hasNext; - if (vm.edges.hasNext) { - vm.edges.nextPageLink.limit = vm.edges.pageSize; - } - vm.edges.pending = false; - }, - function fail() { - vm.edges.hasNext = false; - vm.edges.pending = false; - }); - } - } - }; - - function cancel() { - $mdDialog.cancel(); - } - - function assign() { - var tasks = []; - for (var i=0;i 0; - } - - function toggleEdgeSelection($event, edge) { - $event.stopPropagation(); - if (vm.isEdgeSelected(edge)) { - vm.edges.selection = null; - } else { - vm.edges.selection = edge; - } - } - - function isEdgeSelected(edge) { - return vm.edges.selection != null && edge && - edge.id.id === vm.edges.selection.id.id; - } - - function searchEdgeTextUpdated() { - vm.edges = { - pageSize: vm.edges.pageSize, - data: [], - nextPageLink: { - limit: vm.edges.pageSize, - textSearch: vm.searchText - }, - selection: null, - hasNext: true, - pending: false - }; - } -} diff --git a/ui/src/app/asset/assign-to-edge.tpl.html b/ui/src/app/asset/assign-to-edge.tpl.html deleted file mode 100644 index 851809ba2d..0000000000 --- a/ui/src/app/asset/assign-to-edge.tpl.html +++ /dev/null @@ -1,76 +0,0 @@ - - -
- -
-

asset.assign-asset-to-edge

- - - - -
-
- - - -
-
- asset.assign-to-edge-text - - - - search - - - -
- edge.no-edges-text - - - - - {{ edge.name }} - - - -
-
-
-
- - - - {{ 'action.assign' | translate }} - - {{ 'action.cancel' | - translate }} - - -
-
\ No newline at end of file diff --git a/ui/src/app/asset/index.js b/ui/src/app/asset/index.js index 857534aabe..63bbdd6917 100644 --- a/ui/src/app/asset/index.js +++ b/ui/src/app/asset/index.js @@ -24,7 +24,6 @@ import {AssetController, AssetCardController} from './asset.controller'; import AssignAssetToCustomerController from './assign-to-customer.controller'; import AddAssetsToCustomerController from './add-assets-to-customer.controller'; import AssetDirective from './asset.directive'; -import AssignAssetToEdgeController from './assign-to-edge.controller'; import AddAssetsToEdgeController from './add-assets-to-edge.controller'; export default angular.module('thingsboard.asset', [ @@ -39,7 +38,6 @@ export default angular.module('thingsboard.asset', [ .controller('AssetCardController', AssetCardController) .controller('AssignAssetToCustomerController', AssignAssetToCustomerController) .controller('AddAssetsToCustomerController', AddAssetsToCustomerController) - .controller('AssignAssetToEdgeController', AssignAssetToEdgeController) .controller('AddAssetsToEdgeController', AddAssetsToEdgeController) .directive('tbAsset', AssetDirective) .name; diff --git a/ui/src/app/customer/customer.controller.js b/ui/src/app/customer/customer.controller.js index 3eff84d19f..42a3801efa 100644 --- a/ui/src/app/customer/customer.controller.js +++ b/ui/src/app/customer/customer.controller.js @@ -77,20 +77,6 @@ export default function CustomerController(customerService, $state, $stateParams }, icon: "dashboard" }, - { - onAction: function ($event, item) { - openCustomerEdges($event, item); - }, - name: function() { return $translate.instant('edge.edges') }, - details: function(customer) { - if (customer && customer.additionalInfo && customer.additionalInfo.isPublic) { - return $translate.instant('customer.manage-public-edges') - } else { - return $translate.instant('customer.manage-customer-edges') - } - }, - icon: "wifi_tethering" - }, { onAction: function ($event, item) { vm.grid.deleteItem($event, item); @@ -230,10 +216,4 @@ export default function CustomerController(customerService, $state, $stateParams $state.go('home.customers.dashboards', {customerId: customer.id.id}); } - function openCustomerEdges($event, customer) { - if ($event) { - $event.stopPropagation(); - } - $state.go('home.customers.edges', {customerId: customer.id.id}); - } } diff --git a/ui/src/app/dashboard/dashboards.controller.js b/ui/src/app/dashboard/dashboards.controller.js index 252070c3a1..51eed98d4c 100644 --- a/ui/src/app/dashboard/dashboards.controller.js +++ b/ui/src/app/dashboard/dashboards.controller.js @@ -20,7 +20,6 @@ import dashboardCard from './dashboard-card.tpl.html'; import addDashboardsToCustomerTemplate from './add-dashboards-to-customer.tpl.html'; import makeDashboardPublicDialogTemplate from './make-dashboard-public-dialog.tpl.html'; import manageAssignedCustomersTemplate from './manage-assigned-customers.tpl.html'; -import manageAssignedEdgesTemplate from './manage-assigned-edges.tpl.html'; import addDashboardsToEdgeTemplate from './add-dashboards-to-edge.tpl.html'; /* eslint-enable import/no-unresolved, import/default */ @@ -222,14 +221,6 @@ export function DashboardsController(userService, dashboardService, customerServ return dashboard; } }); - dashboardActionsList.push({ - onAction: function ($event, item) { - manageAssignedEdges($event, item); - }, - name: function() { return $translate.instant('action.assign') }, - details: function() { return $translate.instant('dashboard.manage-assigned-edges') }, - icon: "wifi_tethering" - }); /*dashboardActionsList.push({ onAction: function ($event, item) { @@ -265,32 +256,6 @@ export function DashboardsController(userService, dashboardService, customerServ } ); - dashboardGroupActionsList.push( - { - onAction: function ($event, items) { - assignDashboardsToEdges($event, items); - }, - name: function() { return $translate.instant('dashboard.assign-dashboards') }, - details: function(selectedCount) { - return $translate.instant('dashboard.assign-dashboards-to-edge-text', {count: selectedCount}, "messageformat"); - }, - icon: "wifi_tethering" - } - ); - - dashboardGroupActionsList.push( - { - onAction: function ($event, items) { - unassignDashboardsFromEdges($event, items); - }, - name: function() { return $translate.instant('dashboard.unassign-dashboards') }, - details: function(selectedCount) { - return $translate.instant('dashboard.unassign-dashboards-from-edge-action-text', {count: selectedCount}, "messageformat"); - }, - icon: "portable_wifi_off" - } - ); - dashboardGroupActionsList.push( { onAction: function ($event, items) { @@ -714,26 +679,6 @@ export function DashboardsController(userService, dashboardService, customerServ } } - function manageAssignedEdges($event, dashboard) { - showManageAssignedEdgesDialog($event, [dashboard.id.id], 'manage', dashboard.assignedEdgesIds); - } - - function assignDashboardsToEdges($event, items) { - var dashboardIds = []; - for (var id in items.selections) { - dashboardIds.push(id); - } - showManageAssignedEdgesDialog($event, dashboardIds, 'assign'); - } - - function unassignDashboardsFromEdges($event, items) { - var dashboardIds = []; - for (var id in items.selections) { - dashboardIds.push(id); - } - showManageAssignedEdgesDialog($event, dashboardIds, 'unassign'); - } - function unassignDashboardsFromEdge($event, items, edgeId) { var confirm = $mdDialog.confirm() .targetEvent($event) @@ -753,24 +698,6 @@ export function DashboardsController(userService, dashboardService, customerServ }); } - function showManageAssignedEdgesDialog($event, dashboardIds, actionType, assignedEdges) { - if ($event) { - $event.stopPropagation(); - } - $mdDialog.show({ - controller: 'ManageAssignedEdgesToDashboardController', - controllerAs: 'vm', - templateUrl: manageAssignedEdgesTemplate, - locals: {actionType: actionType, dashboardIds: dashboardIds, assignedEdges: assignedEdges}, - parent: angular.element($document[0].body), - fullscreen: true, - targetEvent: $event - }).then(function () { - vm.grid.refreshList(); - }, function () { - }); - } - function addDashboardsToEdge($event) { if ($event) { $event.stopPropagation(); diff --git a/ui/src/app/dashboard/dashboards.tpl.html b/ui/src/app/dashboard/dashboards.tpl.html index 7df2557f1c..2fb4e35e72 100644 --- a/ui/src/app/dashboard/dashboards.tpl.html +++ b/ui/src/app/dashboard/dashboards.tpl.html @@ -32,8 +32,7 @@ on-manage-assigned-customers="vm.manageAssignedCustomers(event, vm.grid.detailsConfig.currentItem)" on-unassign-from-customer="vm.unassignFromCustomer(event, vm.grid.detailsConfig.currentItem, vm.customerId)" on-export-dashboard="vm.exportDashboard(event, vm.grid.detailsConfig.currentItem)" - on-delete-dashboard="vm.grid.deleteItem(event, vm.grid.detailsConfig.currentItem)" - on-manage-assigned-edges="vm.manageAssignedEdges(event, vm.grid.detailsConfig.currentItem)"> + on-delete-dashboard="vm.grid.deleteItem(event, vm.grid.detailsConfig.currentItem)"> - -
- -
-

{{vm.titleText}}

- - - - -
-
- - - -
-
- {{vm.labelText}} - -
-
-
- - - - {{ vm.actionName | translate }} - - {{ 'action.cancel' | - translate }} - - -
-
\ No newline at end of file diff --git a/ui/src/app/device/assign-to-edge.controller.js b/ui/src/app/device/assign-to-edge.controller.js deleted file mode 100644 index 1cc4da732d..0000000000 --- a/ui/src/app/device/assign-to-edge.controller.js +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright © 2016-2020 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. - */ -/*@ngInject*/ -export default function AssignDeviceToEdgeController(edgeService, deviceService, $mdDialog, $q, deviceIds, edges) { - - var vm = this; - - vm.edges = edges; - vm.searchText = ''; - - vm.assign = assign; - vm.cancel = cancel; - vm.isEdgeSelected = isEdgeSelected; - vm.hasData = hasData; - vm.noData = noData; - vm.searchEdgeTextUpdated = searchEdgeTextUpdated; - vm.toggleEdgeSelection = toggleEdgeSelection; - - vm.theEdges = { - getItemAtIndex: function (index) { - if (index > vm.edges.data.length) { - vm.theEdges.fetchMoreItems_(index); - return null; - } - var item = vm.edges.data[index]; - if (item) { - item.indexNumber = index + 1; - } - return item; - }, - - getLength: function () { - if (vm.edges.hasNext) { - return vm.edges.data.length + vm.edges.nextPageLink.limit; - } else { - return vm.edges.data.length; - } - }, - - fetchMoreItems_: function () { - if (vm.edges.hasNext && !vm.edges.pending) { - vm.edges.pending = true; - edgeService.getEdges(vm.edges.nextPageLink).then( - function success(edges) { - vm.edges.data = vm.edges.data.concat(edges.data); - vm.edges.nextPageLink = edges.nextPageLink; - vm.edges.hasNext = edges.hasNext; - if (vm.edges.hasNext) { - vm.edges.nextPageLink.limit = vm.edges.pageSize; - } - vm.edges.pending = false; - }, - function fail() { - vm.edges.hasNext = false; - vm.edges.pending = false; - }); - } - } - }; - - function cancel() { - $mdDialog.cancel(); - } - - function assign() { - var tasks = []; - for (var i=0;i 0; - } - - function toggleEdgeSelection($event, edge) { - $event.stopPropagation(); - if (vm.isEdgeSelected(edge)) { - vm.edges.selection = null; - } else { - vm.edges.selection = edge; - } - } - - function isEdgeSelected(edge) { - return vm.edges.selection != null && edge && - edge.id.id === vm.edges.selection.id.id; - } - - function searchEdgeTextUpdated() { - vm.edges = { - pageSize: vm.edges.pageSize, - data: [], - nextPageLink: { - limit: vm.edges.pageSize, - textSearch: vm.searchText - }, - selection: null, - hasNext: true, - pending: false - }; - } -} diff --git a/ui/src/app/device/assign-to-edge.tpl.html b/ui/src/app/device/assign-to-edge.tpl.html deleted file mode 100644 index 402b76540e..0000000000 --- a/ui/src/app/device/assign-to-edge.tpl.html +++ /dev/null @@ -1,76 +0,0 @@ - - -
- -
-

device.assign-device-to-edge

- - - - -
-
- - - -
-
- device.assign-to-edge-text - - - - search - - - -
- edge.no-edges-text - - - - - {{ edge.name }} - - - -
-
-
-
- - - - {{ 'action.assign' | translate }} - - {{ 'action.cancel' | - translate }} - - -
-
\ No newline at end of file diff --git a/ui/src/app/device/device.controller.js b/ui/src/app/device/device.controller.js index fdd1cf56d7..a0f9c1b8c0 100644 --- a/ui/src/app/device/device.controller.js +++ b/ui/src/app/device/device.controller.js @@ -20,7 +20,6 @@ import deviceCard from './device-card.tpl.html'; import assignToCustomerTemplate from './assign-to-customer.tpl.html'; import addDevicesToCustomerTemplate from './add-devices-to-customer.tpl.html'; import deviceCredentialsTemplate from './device-credentials.tpl.html'; -import assignToEdgeTemplate from './assign-to-edge.tpl.html'; import addDevicesToEdgeTemplate from './add-devices-to-edge.tpl.html'; /* eslint-enable import/no-unresolved, import/default */ @@ -220,21 +219,6 @@ export function DeviceController($rootScope, userService, deviceService, custome } }); - - deviceActionsList.push( - { - onAction: function ($event, item) { - assignToEdge($event, [ item.id.id ]); - }, - name: function() { return $translate.instant('action.assign') }, - details: function() { return $translate.instant('device.assign-to-edge') }, - icon: "wifi_tethering", - isEnabled: function(device) { - return device && (!device.edgeId || device.edgeId.id === types.id.nullUid); - } - } - ); - deviceActionsList.push( { onAction: function ($event, item) { @@ -284,20 +268,6 @@ export function DeviceController($rootScope, userService, deviceService, custome } ); - deviceGroupActionsList.push( - { - onAction: function ($event, items) { - assignDevicesToEdge($event, items); - }, - name: function() { return $translate.instant('device.assign-devices') }, - details: function(selectedCount) { - return $translate.instant('device.assign-devices-text', {count: selectedCount}, "messageformat"); - }, - icon: "wifi_tethering" - } - ); - - deviceGroupActionsList.push( { onAction: function ($event) { @@ -428,7 +398,7 @@ export function DeviceController($rootScope, userService, deviceService, custome }, name: function() { return $translate.instant('device.unassign-devices') }, details: function(selectedCount) { - return $translate.instant('device.unassign-devices-action-title', {count: selectedCount}, "messageformat"); + return $translate.instant('device.unassign-devices-from-edge-action-title', {count: selectedCount}, "messageformat"); }, icon: "assignment_return" } @@ -672,41 +642,6 @@ export function DeviceController($rootScope, userService, deviceService, custome }); } - function assignToEdge($event, deviceIds) { - if ($event) { - $event.stopPropagation(); - } - var pageSize = 10; - edgeService.getEdges({limit: pageSize, textSearch: ''}).then( - function success(_edges) { - var edges = { - pageSize: pageSize, - data: _edges.data, - nextPageLink: _edges.nextPageLink, - selection: null, - hasNext: _edges.hasNext, - pending: false - }; - if (edges.hasNext) { - edges.nextPageLink.limit = pageSize; - } - $mdDialog.show({ - controller: 'AssignDeviceToEdgeController', - controllerAs: 'vm', - templateUrl: assignToEdgeTemplate, - locals: {deviceIds: deviceIds, edges: edges}, - parent: angular.element($document[0].body), - fullscreen: true, - targetEvent: $event - }).then(function () { - vm.grid.refreshList(); - }, function () { - }); - }, - function fail() { - }); - } - function addDevicesToEdge($event) { if ($event) { $event.stopPropagation(); @@ -743,14 +678,6 @@ export function DeviceController($rootScope, userService, deviceService, custome }); } - function assignDevicesToEdge($event, items) { - var deviceIds = []; - for (var id in items.selections) { - deviceIds.push(id); - } - assignToEdge($event, deviceIds); - } - function unassignFromEdge($event, device) { if ($event) { $event.stopPropagation(); diff --git a/ui/src/app/device/index.js b/ui/src/app/device/index.js index 409ffc97c5..8918686564 100644 --- a/ui/src/app/device/index.js +++ b/ui/src/app/device/index.js @@ -25,7 +25,6 @@ import AssignDeviceToCustomerController from './assign-to-customer.controller'; import AddDevicesToCustomerController from './add-devices-to-customer.controller'; import ManageDeviceCredentialsController from './device-credentials.controller'; import DeviceDirective from './device.directive'; -import AssignDeviceToEdgeController from './assign-to-edge.controller'; import AddDevicesToEdgeController from './add-devices-to-edge.controller'; export default angular.module('thingsboard.device', [ @@ -41,7 +40,6 @@ export default angular.module('thingsboard.device', [ .controller('AssignDeviceToCustomerController', AssignDeviceToCustomerController) .controller('AddDevicesToCustomerController', AddDevicesToCustomerController) .controller('ManageDeviceCredentialsController', ManageDeviceCredentialsController) - .controller('AssignDeviceToEdgeController', AssignDeviceToEdgeController) .controller('AddDevicesToEdgeController', AddDevicesToEdgeController) .directive('tbDevice', DeviceDirective) .name; diff --git a/ui/src/app/entity-view/assign-to-edge.controller.js b/ui/src/app/entity-view/assign-to-edge.controller.js deleted file mode 100644 index 90d0383743..0000000000 --- a/ui/src/app/entity-view/assign-to-edge.controller.js +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright © 2016-2020 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. - */ -/*@ngInject*/ -export default function AssignEntityViewToEdgeController(edgeService, entityViewService, $mdDialog, $q, entityViewIds, edges) { - - var vm = this; - - vm.edges = edges; - vm.searchText = ''; - - vm.assign = assign; - vm.cancel = cancel; - vm.isEdgeSelected = isEdgeSelected; - vm.hasData = hasData; - vm.noData = noData; - vm.searchEdgeTextUpdated = searchEdgeTextUpdated; - vm.toggleEdgeSelection = toggleEdgeSelection; - - vm.theEdges = { - getItemAtIndex: function (index) { - if (index > vm.edges.data.length) { - vm.theEdges.fetchMoreItems_(index); - return null; - } - var item = vm.edges.data[index]; - if (item) { - item.indexNumber = index + 1; - } - return item; - }, - - getLength: function () { - if (vm.edges.hasNext) { - return vm.edges.data.length + vm.edges.nextPageLink.limit; - } else { - return vm.edges.data.length; - } - }, - - fetchMoreItems_: function () { - if (vm.edges.hasNext && !vm.edges.pending) { - vm.edges.pending = true; - edgeService.getEdges(vm.edges.nextPageLink).then( - function success(edges) { - vm.edges.data = vm.edges.data.concat(edges.data); - vm.edges.nextPageLink = edges.nextPageLink; - vm.edges.hasNext = edges.hasNext; - if (vm.edges.hasNext) { - vm.edges.nextPageLink.limit = vm.edges.pageSize; - } - vm.edges.pending = false; - }, - function fail() { - vm.edges.hasNext = false; - vm.edges.pending = false; - }); - } - } - }; - - function cancel() { - $mdDialog.cancel(); - } - - function assign() { - var tasks = []; - for (var i=0; i < entityViewIds.length;i++) { - tasks.push(entityViewService.assignEntityViewToEdge(vm.edges.selection.id.id, entityViewIds[i])); - } - $q.all(tasks).then(function () { - $mdDialog.hide(); - }); - } - - function noData() { - return vm.edges.data.length == 0 && !vm.edges.hasNext; - } - - function hasData() { - return vm.edges.data.length > 0; - } - - function toggleEdgeSelection($event, edge) { - $event.stopPropagation(); - if (vm.isEdgeSelected(edge)) { - vm.edges.selection = null; - } else { - vm.edges.selection = edge; - } - } - - function isEdgeSelected(edge) { - return vm.edges.selection != null && edge && - edge.id.id === vm.edges.selection.id.id; - } - - function searchEdgeTextUpdated() { - vm.edges = { - pageSize: vm.edges.pageSize, - data: [], - nextPageLink: { - limit: vm.edges.pageSize, - textSearch: vm.searchText - }, - selection: null, - hasNext: true, - pending: false - }; - } -} diff --git a/ui/src/app/entity-view/assign-to-edge.tpl.html b/ui/src/app/entity-view/assign-to-edge.tpl.html deleted file mode 100644 index bf46c93238..0000000000 --- a/ui/src/app/entity-view/assign-to-edge.tpl.html +++ /dev/null @@ -1,76 +0,0 @@ - - -
- -
-

entity-view.assign-entity-view-to-edge

- - - - -
-
- - - -
-
- entity-view.assign-to-edge-text - - - - search - - - -
- edge.no-edges-text - - - - - {{ edge.name }} - - - -
-
-
-
- - - - {{ 'action.assign' | translate }} - - {{ 'action.cancel' | - translate }} - - -
-
\ No newline at end of file diff --git a/ui/src/app/entity-view/entity-view.controller.js b/ui/src/app/entity-view/entity-view.controller.js index b92dd1dfb4..4420a3b82d 100644 --- a/ui/src/app/entity-view/entity-view.controller.js +++ b/ui/src/app/entity-view/entity-view.controller.js @@ -19,7 +19,6 @@ import addEntityViewTemplate from './add-entity-view.tpl.html'; import entityViewCard from './entity-view-card.tpl.html'; import assignToCustomerTemplate from './assign-to-customer.tpl.html'; import addEntityViewsToCustomerTemplate from './add-entity-views-to-customer.tpl.html'; -import assignToEdgeTemplate from './assign-to-edge.tpl.html'; import addEntityViewsToEdgeTemplate from './add-entity-views-to-edge.tpl.html'; /* eslint-enable import/no-unresolved, import/default */ @@ -195,32 +194,6 @@ export function EntityViewController($rootScope, userService, entityViewService, } }); - entityViewActionsList.push({ - onAction: function ($event, item) { - assignToEdge($event, [ item.id.id ]); - }, - name: function() { return $translate.instant('action.assign') }, - details: function() { return $translate.instant('entity-view.assign-to-edge') }, - icon: "wifi_tethering", - isEnabled: function(entityView) { - return entityView && (!entityView.edgeId || entityView.edgeId.id === types.id.nullUid); - } - } - ); - - entityViewActionsList.push({ - onAction: function ($event, item) { - unassignFromEdge($event, item, false); - }, - name: function() { return $translate.instant('action.unassign') }, - details: function() { return $translate.instant('entity-view.unassign-from-edge') }, - icon: "portable_wifi_off", - isEnabled: function(entityView) { - return entityView && entityView.edgeId && entityView.edgeId.id !== types.id.nullUid; - } - } - ); - entityViewActionsList.push( { onAction: function ($event, item) { @@ -245,19 +218,6 @@ export function EntityViewController($rootScope, userService, entityViewService, } ); - entityViewGroupActionsList.push( - { - onAction: function ($event, items) { - assignEntityViewsToEdge($event, items); - }, - name: function() { return $translate.instant('entity-view.assign-assets') }, - details: function(selectedCount) { - return $translate.instant('entity-view.assign-entity-views-text', {count: selectedCount}, "messageformat"); - }, - icon: "wifi_tethering" - } - ); - entityViewGroupActionsList.push( { onAction: function ($event) { @@ -334,17 +294,6 @@ export function EntityViewController($rootScope, userService, entityViewService, return {"edgeId": edgeId, "topIndex": vm.topIndex}; }; - entityViewActionsList.push( - { - onAction: function ($event, item) { - unassignFromEdge($event, item, false); - }, - name: function() { return $translate.instant('action.unassign') }, - details: function() { return $translate.instant('entity-view.unassign-from-edge') }, - icon: "assignment_return" - } - ); - entityViewGroupActionsList.push( { onAction: function ($event, items) { @@ -580,41 +529,6 @@ export function EntityViewController($rootScope, userService, entityViewService, }); } - function assignToEdge($event, entityViewIds) { - if ($event) { - $event.stopPropagation(); - } - var pageSize = 10; - edgeService.getEdges({limit: pageSize, textSearch: ''}).then( - function success(_edges) { - var edges = { - pageSize: pageSize, - data: _edges.data, - nextPageLink: _edges.nextPageLink, - selection: null, - hasNext: _edges.hasNext, - pending: false - }; - if (edges.hasNext) { - edges.nextPageLink.limit = pageSize; - } - $mdDialog.show({ - controller: 'AssignEntityViewToEdgeController', - controllerAs: 'vm', - templateUrl: assignToEdgeTemplate, - locals: {entityViewIds: entityViewIds, edges: edges}, - parent: angular.element($document[0].body), - fullscreen: true, - targetEvent: $event - }).then(function () { - vm.grid.refreshList(); - }, function () { - }); - }, - function fail() { - }); - } - function addEntityViewsToEdge($event) { if ($event) { $event.stopPropagation(); @@ -651,35 +565,6 @@ export function EntityViewController($rootScope, userService, entityViewService, }); } - function assignEntityViewsToEdge($event, items) { - var entityViewIds = []; - for (var id in items.selections) { - entityViewIds.push(id); - } - assignToEdge($event, entityViewIds); - } - - function unassignFromEdge($event, entityView) { - if ($event) { - $event.stopPropagation(); - } - var title = $translate.instant('entity-view.unassign-entity-view-from-edge-title', {entityViewName: entityView.name}); - var content = $translate.instant('entity-view.unassign-entity-view-from-edge-text'); - var label = $translate.instant('entity-view.unassign-entity-view'); - var confirm = $mdDialog.confirm() - .targetEvent($event) - .title(title) - .htmlContent(content) - .ariaLabel(label) - .cancel($translate.instant('action.no')) - .ok($translate.instant('action.yes')); - $mdDialog.show(confirm).then(function () { - entityViewService.unassignEntityViewFromEdge(entityView.id.id).then(function success() { - vm.grid.refreshList(); - }); - }); - } - function unassignEntityViewsFromEdge($event, items) { var confirm = $mdDialog.confirm() .targetEvent($event) diff --git a/ui/src/app/entity-view/index.js b/ui/src/app/entity-view/index.js index 1e14ba48e8..9bc2d55a2f 100644 --- a/ui/src/app/entity-view/index.js +++ b/ui/src/app/entity-view/index.js @@ -24,7 +24,6 @@ import {EntityViewController, EntityViewCardController} from './entity-view.cont import AssignEntityViewToCustomerController from './assign-to-customer.controller'; import AddEntityViewsToCustomerController from './add-entity-views-to-customer.controller'; import EntityViewDirective from './entity-view.directive'; -import AssignEntityViewToEdgeController from './assign-to-edge.controller'; import AddEntityViewsToEdgeController from './add-entity-views-to-edge.controller'; export default angular.module('thingsboard.entityView', [ @@ -39,7 +38,6 @@ export default angular.module('thingsboard.entityView', [ .controller('EntityViewCardController', EntityViewCardController) .controller('AssignEntityViewToCustomerController', AssignEntityViewToCustomerController) .controller('AddEntityViewsToCustomerController', AddEntityViewsToCustomerController) - .controller('AssignEntityViewToEdgeController', AssignEntityViewToEdgeController) .controller('AddEntityViewsToEdgeController', AddEntityViewsToEdgeController) .directive('tbEntityView', EntityViewDirective) .name; diff --git a/ui/src/app/locale/locale.constant-en_US.json b/ui/src/app/locale/locale.constant-en_US.json index 4f0b3f68e2..9fef9f1679 100644 --- a/ui/src/app/locale/locale.constant-en_US.json +++ b/ui/src/app/locale/locale.constant-en_US.json @@ -292,7 +292,8 @@ "assign-to-edge": "Assign to edge", "assign-to-edge-text": "Please select the edge to assign the asset(s)", "unassign-asset-from-edge-title": "Are you sure you want to unassign the asset '{{assetName}}'?", - "unassign-asset-from-edge-text": "After the confirmation the asset will be unassigned and won't be accessible by the edge." + "unassign-asset-from-edge-text": "After the confirmation the asset will be unassigned and won't be accessible by the edge.", + "unassign-assets-from-edge-action-title": "Unassign { count, plural, 1 {1 asset} other {# assets} } from edge" }, "attribute": { "attributes": "Attributes", @@ -746,7 +747,8 @@ "assign-to-edge": "Assign to edge", "assign-to-edge-text": "Please select the edge to assign the device(s)", "unassign-device-from-edge-title": "Are you sure you want to unassign the device '{{deviceName}}'?", - "unassign-device-from-edge-text": "After the confirmation the device will be unassigned and won't be accessible by the edge." + "unassign-device-from-edge-text": "After the confirmation the device will be unassigned and won't be accessible by the edge.", + "unassign-devices-from-edge-action-title": "Unassign { count, plural, 1 {1 device} other {# devices} } from edge" }, "dialog": { "close": "Close dialog" @@ -1051,7 +1053,8 @@ "assign-to-edge": "Assign to edge", "assign-to-edge-text": "Please select the edge to assign the entity view(s)", "unassign-entity-view-from-edge-title": "Are you sure you want to unassign the entity view '{{entityViewName}}'?", - "unassign-entity-view-from-edge-text": "After the confirmation the entity view will be unassigned and won't be accessible by the edge." + "unassign-entity-view-from-edge-text": "After the confirmation the entity view will be unassigned and won't be accessible by the edge.", + "unassign-entity-views-from-edge-action-title": "Unassign { count, plural, 1 {1 entity view} other {# entity views} } from edge" }, "event": { "event-type": "Event type", diff --git a/ui/src/app/rulechain/index.js b/ui/src/app/rulechain/index.js index bb3c27dd1b..7c68bf7489 100644 --- a/ui/src/app/rulechain/index.js +++ b/ui/src/app/rulechain/index.js @@ -25,7 +25,6 @@ import LinkDirective from './link.directive'; import MessageTypeAutocompleteDirective from './message-type-autocomplete.directive'; import NodeScriptTest from './script/node-script-test.service'; import AddRuleChainsToEdgeController from './add-rulechains-to-edge.controller'; -import ManageAssignedEdgesToRuleChainController from "./manage-assigned-edges.controller"; export default angular.module('thingsboard.ruleChain', []) .config(RuleChainRoutes) @@ -34,7 +33,6 @@ export default angular.module('thingsboard.ruleChain', []) .controller('AddRuleNodeController', AddRuleNodeController) .controller('AddRuleNodeLinkController', AddRuleNodeLinkController) .controller('NodeScriptTestController', NodeScriptTestController) - .controller('ManageAssignedEdgesToRuleChainController', ManageAssignedEdgesToRuleChainController) .controller('AddRuleChainsToEdgeController', AddRuleChainsToEdgeController) .directive('tbRuleChain', RuleChainDirective) .directive('tbRuleNodeDefinedConfig', RuleNodeDefinedConfigDirective) diff --git a/ui/src/app/rulechain/manage-assigned-edges.controller.js b/ui/src/app/rulechain/manage-assigned-edges.controller.js deleted file mode 100644 index f9a46c0d2b..0000000000 --- a/ui/src/app/rulechain/manage-assigned-edges.controller.js +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright © 2016-2020 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. - */ -/*@ngInject*/ -export default function ManageAssignedEdgesToRuleChainController($mdDialog, $q, types, ruleChainService, actionType, ruleChainIds, assignedEdges) { - - var vm = this; - - vm.types = types; - vm.actionType = actionType; - vm.ruleChainIds = ruleChainIds; - vm.assignedEdges = assignedEdges; - if (actionType != 'manage') { - vm.assignedEdges = []; - } - - if (actionType == 'manage') { - vm.titleText = 'rulechain.manage-assigned-edges'; - vm.labelText = 'rulechain.assigned-edges'; - vm.actionName = 'action.update'; - } else if (actionType == 'assign') { - vm.titleText = 'rulechain.assign-to-edges'; - vm.labelText = 'rulechain.assign-to-edges-text'; - vm.actionName = 'action.assign'; - } else if (actionType == 'unassign') { - vm.titleText = 'rulechain.unassign-from-edges'; - vm.labelText = 'rulechain.unassign-from-edges-text'; - vm.actionName = 'action.unassign'; - } - - vm.submit = submit; - vm.cancel = cancel; - - function cancel () { - $mdDialog.cancel(); - } - - function submit () { - var tasks = []; - for (var i=0;i - -
- -
-

{{vm.titleText}}

- - - - -
-
- - - -
-
- {{vm.labelText}} - -
-
-
- - - - {{ vm.actionName | translate }} - - {{ 'action.cancel' | - translate }} - - -
-
\ No newline at end of file diff --git a/ui/src/app/rulechain/rulechains.controller.js b/ui/src/app/rulechain/rulechains.controller.js index 34f5e0487a..f3e159566c 100644 --- a/ui/src/app/rulechain/rulechains.controller.js +++ b/ui/src/app/rulechain/rulechains.controller.js @@ -17,7 +17,6 @@ import addRuleChainTemplate from './add-rulechain.tpl.html'; import ruleChainCard from './rulechain-card.tpl.html'; -import manageAssignedEdgesTemplate from "./manage-assigned-edges.tpl.html"; import addRuleChainsToEdgeTemplate from "./add-rulechains-to-edge.tpl.html"; /* eslint-enable import/no-unresolved, import/default */ @@ -183,15 +182,6 @@ export default function RuleChainsController(ruleChainService, userService, edge return deleteRuleChain(ruleChainId); }; - ruleChainActionsList.push({ - onAction: function ($event, item) { - manageAssignedEdges($event, item); - }, - name: function() { return $translate.instant('action.assign') }, - details: function() { return $translate.instant('rulechain.manage-assigned-edges') }, - icon: "wifi_tethering" - }); - ruleChainActionsList.push({ onAction: function ($event, item) { vm.grid.deleteItem($event, item); @@ -212,32 +202,6 @@ export default function RuleChainsController(ruleChainService, userService, edge isEnabled: isNonRootRuleChain }); - ruleChainGroupActionsList.push( - { - onAction: function ($event, items) { - assignRuleChainsToEdges($event, items); - }, - name: function() { return $translate.instant('rulechain.assign-rulechains') }, - details: function(selectedCount) { - return $translate.instant('rulechain.assign-rulechains-to-edge-text', {count: selectedCount}, "messageformat"); - }, - icon: "wifi_tethering" - } - ); - - ruleChainGroupActionsList.push( - { - onAction: function ($event, items) { - unassignRuleChainsFromEdges($event, items); - }, - name: function() { return $translate.instant('rulechain.unassign-rulechains') }, - details: function(selectedCount) { - return $translate.instant('rulechain.unassign-rulechains-from-edge-action-text', {count: selectedCount}, "messageformat"); - }, - icon: "portable_wifi_off" - } - ); - ruleChainGroupActionsList.push( { onAction: function ($event) { @@ -454,26 +418,6 @@ export default function RuleChainsController(ruleChainService, userService, edge }); } - function manageAssignedEdges($event, ruleChain) { - showManageAssignedEdgesDialog($event, [ruleChain.id.id], 'manage', ruleChain.assignedEdgesIds); - } - - function assignRuleChainsToEdges($event, items) { - var ruleChainIds = []; - for (var id in items.selections) { - ruleChainIds.push(id); - } - showManageAssignedEdgesDialog($event, ruleChainIds, 'assign'); - } - - function unassignRuleChainsFromEdges($event, items) { - var ruleChainIds = []; - for (var id in items.selections) { - ruleChainIds.push(id); - } - showManageAssignedEdgesDialog($event, ruleChainIds, 'unassign'); - } - function unassignRuleChainsFromEdge($event, items, edgeId) { var confirm = $mdDialog.confirm() .targetEvent($event) @@ -493,24 +437,6 @@ export default function RuleChainsController(ruleChainService, userService, edge }); } - function showManageAssignedEdgesDialog($event, ruleChainIds, actionType, assignedEdges) { - if ($event) { - $event.stopPropagation(); - } - $mdDialog.show({ - controller: 'ManageAssignedEdgesToRuleChainController', - controllerAs: 'vm', - templateUrl: manageAssignedEdgesTemplate, - locals: {actionType: actionType, ruleChainIds: ruleChainIds, assignedEdges: assignedEdges}, - parent: angular.element($document[0].body), - fullscreen: true, - targetEvent: $event - }).then(function () { - vm.grid.refreshList(); - }, function () { - }); - } - function addRuleChainsToEdge($event) { if ($event) { $event.stopPropagation(); From 6ab3714ac51706c684ca60e35665fdd53f901d82 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Wed, 27 May 2020 09:22:25 +0300 Subject: [PATCH 16/52] Added default rule chain for server side --- .../server/controller/EdgeController.java | 4 +- .../controller/RuleChainController.java | 202 +++++------------- .../CassandraDatabaseUpgradeService.java | 8 - .../install/SqlDatabaseUpgradeService.java | 6 - .../server/dao/edge/EdgeService.java | 6 +- .../server/dao/rule/RuleChainService.java | 9 +- .../server/common/data/rule/RuleChain.java | 27 --- .../server/dao/edge/CassandraEdgeDao.java | 29 +++ .../thingsboard/server/dao/edge/EdgeDao.java | 11 + .../server/dao/edge/EdgeServiceImpl.java | 39 +++- .../server/dao/model/ModelConstants.java | 1 - .../dao/model/nosql/RuleChainEntity.java | 30 --- .../server/dao/model/sql/RuleChainEntity.java | 28 --- .../server/dao/rule/BaseRuleChainService.java | 133 ++++++------ .../dao/rule/CassandraRuleChainDao.java | 15 ++ .../server/dao/rule/RuleChainDao.java | 8 + .../server/dao/sql/edge/JpaEdgeDao.java | 27 +++ .../server/dao/sql/rule/JpaRuleChainDao.java | 15 ++ .../resources/sql/schema-entities-hsql.sql | 4 +- .../main/resources/sql/schema-entities.sql | 4 +- ui/src/app/api/rule-chain.service.js | 109 ++++------ ui/src/app/rulechain/rulechains.controller.js | 27 ++- 22 files changed, 327 insertions(+), 415 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java b/application/src/main/java/org/thingsboard/server/controller/EdgeController.java index bab992f456..bb9106719d 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EdgeController.java @@ -96,7 +96,7 @@ public class EdgeController extends BaseController { if (created) { ruleChainService.assignRuleChainToEdge(tenantId, defaultRootEdgeRuleChain.getId(), result.getId()); - edgeService.setRootRuleChain(tenantId, result, defaultRootEdgeRuleChain.getId()); + edgeService.setEdgeRootRuleChain(tenantId, result, defaultRootEdgeRuleChain.getId()); } logEntityAction(result.getId(), result, null, created ? ActionType.ADDED : ActionType.UPDATED, null); @@ -281,7 +281,7 @@ public class EdgeController extends BaseController { accessControlService.checkPermission(getCurrentUser(), Resource.EDGE, Operation.WRITE, edge.getId(), edge); - Edge updatedEdge = edgeService.setRootRuleChain(getTenantId(), edge, ruleChainId); + Edge updatedEdge = edgeService.setEdgeRootRuleChain(getTenantId(), edge, ruleChainId); logEntityAction(updatedEdge.getId(), updatedEdge, null, ActionType.UPDATED, null); diff --git a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java index f1486a8c19..3d6b1f3349 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java +++ b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.controller; -import com.datastax.driver.core.utils.UUIDs; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -40,7 +39,6 @@ import org.thingsboard.server.actors.tenant.DebugTbRateLimits; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.Event; -import org.thingsboard.server.common.data.ShortEdgeInfo; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.exception.ThingsboardException; @@ -62,13 +60,11 @@ import org.thingsboard.server.common.msg.TbMsgDataType; import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.dao.event.EventService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.service.script.JsInvokeService; import org.thingsboard.server.service.script.RuleNodeJsScriptEngine; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -450,189 +446,75 @@ public class RuleChainController extends BaseController { } } - @PreAuthorize("hasAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/ruleChain/{ruleChainId}/edges", method = RequestMethod.POST) + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") + @RequestMapping(value = "/edge/{edgeId}/ruleChains", params = {"limit"}, method = RequestMethod.GET) @ResponseBody - public RuleChain updateRuleChainEdges(@PathVariable(RULE_CHAIN_ID) String strRuleChainId, - @RequestBody String[] strEdgeIds) throws ThingsboardException { - checkParameter(RULE_CHAIN_ID, strRuleChainId); + public TimePageData getEdgeRuleChains( + @PathVariable("edgeId") String strEdgeId, + @RequestParam int limit, + @RequestParam(required = false) Long startTime, + @RequestParam(required = false) Long endTime, + @RequestParam(required = false, defaultValue = "false") boolean ascOrder, + @RequestParam(required = false) String offset) throws ThingsboardException { + checkParameter("edgeId", strEdgeId); try { - RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); - RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.ASSIGN_TO_EDGE); - - Set edgeIds = new HashSet<>(); - if (strEdgeIds != null) { - for (String strEdgeId : strEdgeIds) { - edgeIds.add(new EdgeId(toUUID(strEdgeId))); - } - } - - Set addedEdgeIds = new HashSet<>(); - Set removedEdgeIds = new HashSet<>(); - for (EdgeId edgeId : edgeIds) { - if (!ruleChain.isAssignedToEdge(edgeId)) { - addedEdgeIds.add(edgeId); - } - } - - Set assignedEdges = ruleChain.getAssignedEdges(); - if (assignedEdges != null) { - for (ShortEdgeInfo edgeInfo : assignedEdges) { - if (!edgeIds.contains(edgeInfo.getEdgeId())) { - removedEdgeIds.add(edgeInfo.getEdgeId()); - } - } - } - - if (addedEdgeIds.isEmpty() && removedEdgeIds.isEmpty()) { - return ruleChain; - } else { - RuleChain savedRuleChain = null; - for (EdgeId edgeId : addedEdgeIds) { - savedRuleChain = checkNotNull(ruleChainService.assignRuleChainToEdge(getCurrentUser().getTenantId(), ruleChainId, edgeId)); - ShortEdgeInfo edgeInfo = savedRuleChain.getAssignedEdgeInfo(edgeId); - logEntityAction(ruleChainId, savedRuleChain, - null, - ActionType.ASSIGNED_TO_EDGE, null, strRuleChainId, edgeId.toString(), edgeInfo.getTitle()); - } - for (EdgeId edgeId : removedEdgeIds) { - ShortEdgeInfo edgeInfo = ruleChain.getAssignedEdgeInfo(edgeId); - savedRuleChain = checkNotNull(ruleChainService.unassignRuleChainFromEdge(getCurrentUser().getTenantId(), ruleChainId, edgeId, false)); - logEntityAction(ruleChainId, ruleChain, - null, - ActionType.UNASSIGNED_FROM_EDGE, null, strRuleChainId, edgeId.toString(), edgeInfo.getTitle()); - - } - return savedRuleChain; - } + TenantId tenantId = getCurrentUser().getTenantId(); + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); + checkEdgeId(edgeId, Operation.READ); + TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset); + return checkNotNull(ruleChainService.findRuleChainsByTenantIdAndEdgeId(tenantId, edgeId, pageLink).get()); } catch (Exception e) { - - logEntityAction(emptyId(EntityType.RULE_CHAIN), null, - null, - ActionType.ASSIGNED_TO_EDGE, e, strRuleChainId); - throw handleException(e); } } - @PreAuthorize("hasAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/ruleChain/{ruleChainId}/edges/add", method = RequestMethod.POST) + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") + @RequestMapping(value = "/ruleChain/{ruleChainId}/defaultRootEdge", method = RequestMethod.POST) @ResponseBody - public RuleChain addRuleChainEdges(@PathVariable(RULE_CHAIN_ID) String strRuleChainId, - @RequestBody String[] strEdgeIds) throws ThingsboardException { + public RuleChain setDefaultRootEdgeRuleChain(@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException { checkParameter(RULE_CHAIN_ID, strRuleChainId); try { RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); - RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.ASSIGN_TO_EDGE); - - Set edgeIds = new HashSet<>(); - if (strEdgeIds != null) { - for (String strEdgeId : strEdgeIds) { - EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); - if (!ruleChain.isAssignedToEdge(edgeId)) { - edgeIds.add(edgeId); - } - } - } - - if (edgeIds.isEmpty()) { - return ruleChain; - } else { - RuleChain savedRuleChain = null; - for (EdgeId edgeId : edgeIds) { - savedRuleChain = checkNotNull(ruleChainService.assignRuleChainToEdge(getCurrentUser().getTenantId(), ruleChainId, edgeId)); - ShortEdgeInfo edgeInfo = savedRuleChain.getAssignedEdgeInfo(edgeId); - logEntityAction(ruleChainId, savedRuleChain, - null, - ActionType.ASSIGNED_TO_EDGE, null, strRuleChainId, edgeId.toString(), edgeInfo.getTitle()); - } - return savedRuleChain; - } + RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.WRITE); + ruleChainService.setDefaultRootEdgeRuleChain(getTenantId(), ruleChainId); + return ruleChain; } catch (Exception e) { - - logEntityAction(emptyId(EntityType.RULE_CHAIN), null, + logEntityAction(emptyId(EntityType.RULE_CHAIN), null, - ActionType.ASSIGNED_TO_EDGE, e, strRuleChainId); - + null, + ActionType.UPDATED, e, strRuleChainId); throw handleException(e); } } @PreAuthorize("hasAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/ruleChain/{ruleChainId}/edges/remove", method = RequestMethod.POST) + @RequestMapping(value = "/ruleChain/{ruleChainId}/defaultEdge", method = RequestMethod.POST) @ResponseBody - public RuleChain removeRuleChainEdges(@PathVariable(RULE_CHAIN_ID) String strRuleChainId, - @RequestBody String[] strEdgeIds) throws ThingsboardException { + public RuleChain addDefaultEdgeRuleChain(@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException { checkParameter(RULE_CHAIN_ID, strRuleChainId); try { RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); - RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.UNASSIGN_FROM_EDGE); - - Set edgeIds = new HashSet<>(); - if (strEdgeIds != null) { - for (String strEdgeId : strEdgeIds) { - EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); - if (ruleChain.isAssignedToEdge(edgeId)) { - edgeIds.add(edgeId); - } - } - } - - if (edgeIds.isEmpty()) { - return ruleChain; - } else { - RuleChain savedRuleChain = null; - for (EdgeId edgeId : edgeIds) { - ShortEdgeInfo edgeInfo = ruleChain.getAssignedEdgeInfo(edgeId); - savedRuleChain = checkNotNull(ruleChainService.unassignRuleChainFromEdge(getCurrentUser().getTenantId(), ruleChainId, edgeId, false)); - logEntityAction(ruleChainId, ruleChain, - null, - ActionType.UNASSIGNED_FROM_EDGE, null, strRuleChainId, edgeId.toString(), edgeInfo.getTitle()); - - } - return savedRuleChain; - } + RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.WRITE); + ruleChainService.addDefaultEdgeRuleChain(getTenantId(), ruleChainId); + return ruleChain; } catch (Exception e) { - - logEntityAction(emptyId(EntityType.RULE_CHAIN), null, + logEntityAction(emptyId(EntityType.RULE_CHAIN), null, - ActionType.UNASSIGNED_FROM_EDGE, e, strRuleChainId); - - throw handleException(e); - } - } - - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/edge/{edgeId}/ruleChains", params = { "limit" }, method = RequestMethod.GET) - @ResponseBody - public TimePageData getEdgeRuleChains( - @PathVariable("edgeId") String strEdgeId, - @RequestParam int limit, - @RequestParam(required = false) Long startTime, - @RequestParam(required = false) Long endTime, - @RequestParam(required = false, defaultValue = "false") boolean ascOrder, - @RequestParam(required = false) String offset) throws ThingsboardException { - checkParameter("edgeId", strEdgeId); - try { - TenantId tenantId = getCurrentUser().getTenantId(); - EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); - checkEdgeId(edgeId, Operation.READ); - TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset); - return checkNotNull(ruleChainService.findRuleChainsByTenantIdAndEdgeId(tenantId, edgeId, pageLink).get()); - } catch (Exception e) { + null, + ActionType.UPDATED, e, strRuleChainId); throw handleException(e); } } - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/ruleChain/{ruleChainId}/defaultRootEdge", method = RequestMethod.POST) + @PreAuthorize("hasAuthority('TENANT_ADMIN')") + @RequestMapping(value = "/ruleChain/{ruleChainId}/defaultEdge", method = RequestMethod.DELETE) @ResponseBody - public RuleChain setDefaultRootEdgeRuleChain(@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException { + public RuleChain removeDefaultEdgeRuleChain(@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException { checkParameter(RULE_CHAIN_ID, strRuleChainId); try { RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.WRITE); - ruleChainService.setDefaultRootEdgeRuleChain(getTenantId(), ruleChainId); + ruleChainService.removeDefaultEdgeRuleChain(getTenantId(), ruleChainId); return ruleChain; } catch (Exception e) { logEntityAction(emptyId(EntityType.RULE_CHAIN), @@ -642,4 +524,16 @@ public class RuleChainController extends BaseController { throw handleException(e); } } + + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") + @RequestMapping(value = "/ruleChain/defaultEdgeRuleChains", method = RequestMethod.GET) + @ResponseBody + public List getDefaultEdgeRuleChains() throws ThingsboardException { + try { + TenantId tenantId = getCurrentUser().getTenantId(); + return checkNotNull(ruleChainService.findDefaultEdgeRuleChainsByTenantId(tenantId)).get(); + } catch (Exception e) { + throw handleException(e); + } + } } diff --git a/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java index 56b4e011cb..5227efcfa3 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java @@ -323,14 +323,6 @@ public class CassandraDatabaseUpgradeService extends AbstractCassandraDatabaseUp cluster.getSession().execute("alter table entity_view add edge_id text"); Thread.sleep(2500); } catch (InvalidQueryException e) {} - try { - cluster.getSession().execute("alter table dashboard add assigned_edges text"); - Thread.sleep(2500); - } catch (InvalidQueryException e) {} - try { - cluster.getSession().execute("alter table rule_chain add assigned_edges text"); - Thread.sleep(2500); - } catch (InvalidQueryException e) {} try { cluster.getSession().execute("alter table rule_chain add type text"); Thread.sleep(2500); 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 bff44636d6..c9966d0cbb 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 @@ -247,12 +247,6 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService try { conn.createStatement().execute("ALTER TABLE entity_view ADD edge_id varchar(31)"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script } catch (Exception e) {} - try { - conn.createStatement().execute("ALTER TABLE dashboard ADD assigned_edges varchar(10000000)"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script - } catch (Exception e) {} - try { - conn.createStatement().execute("ALTER TABLE rule_chain ADD assigned_edges varchar(10000000)"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script - } catch (Exception e) {} try { conn.createStatement().execute("ALTER TABLE rule_chain ADD type varchar(255) DEFAULT 'SYSTEM'"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script } catch (Exception e) {} diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java index 29e317fe59..8b4baffbae 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java @@ -29,11 +29,13 @@ import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.page.TimePageData; import org.thingsboard.server.common.data.page.TimePageLink; +import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.msg.TbMsg; import java.io.IOException; import java.util.List; import java.util.Optional; +import java.util.UUID; public interface EdgeService { @@ -77,7 +79,9 @@ public interface EdgeService { TimePageData findQueueEvents(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink); - Edge setRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) throws IOException; + Edge setEdgeRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) throws IOException; + + ListenableFuture> findEdgesByTenantIdAndRuleChainId(TenantId tenantId, RuleChainId ruleChainId, TimePageLink pageLink); } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java index 8cb77d541b..d0e28a4a57 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java @@ -73,11 +73,16 @@ public interface RuleChainService { void unassignEdgeRuleChains(TenantId tenantId, EdgeId edgeId); - void updateEdgeRuleChains(TenantId tenantId, EdgeId edgeId); - ListenableFuture> findRuleChainsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink); RuleChain getDefaultRootEdgeRuleChain(TenantId tenantId); boolean setDefaultRootEdgeRuleChain(TenantId tenantId, RuleChainId ruleChainId); + + boolean addDefaultEdgeRuleChain(TenantId tenantId, RuleChainId ruleChainId); + + boolean removeDefaultEdgeRuleChain(TenantId tenantId, RuleChainId ruleChainId); + + ListenableFuture> findDefaultEdgeRuleChainsByTenantId(TenantId tenantId); + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java index 49508e2971..cedc458edb 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java @@ -48,7 +48,6 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo im private boolean root; private boolean debugMode; private transient JsonNode configuration; - private Set assignedEdges; @JsonIgnore private byte[] configurationBytes; @@ -68,7 +67,6 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo im this.type = ruleChain.getType(); this.firstRuleNodeId = ruleChain.getFirstRuleNodeId(); this.root = ruleChain.isRoot(); - this.assignedEdges = ruleChain.getAssignedEdges(); this.setConfiguration(ruleChain.getConfiguration()); } @@ -89,29 +87,4 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo im public void setConfiguration(JsonNode data) { setJson(data, json -> this.configuration = json, bytes -> this.configurationBytes = bytes); } - - - - public boolean isAssignedToEdge(EdgeId edgeId) { - return EdgeUtils.isAssignedToEdge(this.assignedEdges, edgeId); - } - - public ShortEdgeInfo getAssignedEdgeInfo(EdgeId edgeId) { - return EdgeUtils.getAssignedEdgeInfo(this.assignedEdges, edgeId); - } - - public boolean addAssignedEdge(Edge edge) { - if (this.assignedEdges == null) { - this.assignedEdges = new HashSet<>(); - } - return EdgeUtils.addAssignedEdge(this.assignedEdges, edge.toShortEdgeInfo()); - } - - public boolean updateAssignedEdge(Edge edge) { - return EdgeUtils.updateAssignedEdge(this.assignedEdges, edge.toShortEdgeInfo()); - } - - public boolean removeAssignedEdge(Edge edge) { - return EdgeUtils.removeAssignedEdge(this.assignedEdges, edge.toShortEdgeInfo()); - } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/CassandraEdgeDao.java b/dao/src/main/java/org/thingsboard/server/dao/edge/CassandraEdgeDao.java index 80d51303b1..42253db06c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/CassandraEdgeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/CassandraEdgeDao.java @@ -15,16 +15,29 @@ */ package org.thingsboard.server.dao.edge; +import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.MoreExecutors; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.EntitySubtype; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.id.EdgeId; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.common.data.page.TimePageLink; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; +import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.dao.model.nosql.EdgeEntity; import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTextDao; +import org.thingsboard.server.dao.relation.RelationDao; import org.thingsboard.server.dao.util.NoSqlDao; +import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.UUID; @@ -36,6 +49,9 @@ import static org.thingsboard.server.dao.model.ModelConstants.EDGE_COLUMN_FAMILY @NoSqlDao public class CassandraEdgeDao extends CassandraAbstractSearchTextDao implements EdgeDao { + @Autowired + private RelationDao relationDao; + @Override protected Class getColumnFamilyClass() { return EdgeEntity.class; @@ -91,4 +107,17 @@ public class CassandraEdgeDao extends CassandraAbstractSearchTextDao findByRoutingKey(UUID tenantId, String routingKey) { return Optional.empty(); } + + @Override + public ListenableFuture> findEdgesByTenantIdAndRuleChainId(UUID tenantId, UUID ruleChainId, TimePageLink pageLink) { + log.debug("Try to find edges by tenantId [{}], ruleChainId [{}] and pageLink [{}]", tenantId, ruleChainId, pageLink); + ListenableFuture> relations = relationDao.findAllByToAndType(new TenantId(tenantId), new RuleChainId(ruleChainId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE); + return Futures.transformAsync(relations, input -> { + List> edgeFutures = new ArrayList<>(input.size()); + for (EntityRelation relation : input) { + edgeFutures.add(findByIdAsync(new TenantId(tenantId), relation.getTo().getId())); + } + return Futures.successfulAsList(edgeFutures); + }, MoreExecutors.directExecutor()); + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java index 34d06b338f..0002b96ad0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java @@ -20,6 +20,8 @@ import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.common.data.page.TimePageLink; +import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.dao.Dao; import java.util.List; @@ -124,4 +126,13 @@ public interface EdgeDao extends Dao { */ Optional findByRoutingKey(UUID tenantId, String routingKey); + /** + * Find edges by tenantId, ruleChainId and page link. + * + * @param tenantId the tenantId + * @param ruleChainId the ruleChainId + * @param pageLink the page link + * @return the list of rule chain objects + */ + ListenableFuture> findEdgesByTenantIdAndRuleChainId(UUID tenantId, UUID ruleChainId, TimePageLink pageLink); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java index ef384a9fba..3bfb1d114e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java @@ -62,6 +62,7 @@ import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntitySearchDirection; 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.msg.TbMsg; import org.thingsboard.server.common.msg.session.SessionMsgType; import org.thingsboard.server.dao.asset.AssetService; @@ -531,10 +532,20 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic case DataConstants.ENTITY_CREATED: case DataConstants.ENTITY_UPDATED: RuleChain ruleChain = mapper.readValue(tbMsg.getData(), RuleChain.class); - if (ruleChain.getAssignedEdges() != null && !ruleChain.getAssignedEdges().isEmpty()) { - for (ShortEdgeInfo assignedEdge : ruleChain.getAssignedEdges()) { - pushEventToEdge(tenantId, assignedEdge.getEdgeId(), EdgeQueueEntityType.RULE_CHAIN, tbMsg, callback); - } + if (RuleChainType.EDGE.equals(ruleChain.getType())) { + ListenableFuture> future = findEdgesByTenantIdAndRuleChainId(tenantId, ruleChain.getId(), new TimePageLink(Integer.MAX_VALUE)); + Futures.transform(future, edges -> { + if (edges != null && edges.getData() != null && !edges.getData().isEmpty()) { + try { + for (Edge edge : edges.getData()) { + pushEventToEdge(tenantId, edge.getId(), EdgeQueueEntityType.RULE_CHAIN, tbMsg, callback); + } + } catch (IOException e) { + log.error("Can't push event to edge", e); + } + } + return null; + }, MoreExecutors.directExecutor()); } break; default: @@ -628,10 +639,9 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic } @Override - public Edge setRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) throws IOException { + public Edge setEdgeRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) throws IOException { edge.setRootRuleChainId(ruleChainId); Edge savedEdge = saveEdge(edge); - ruleChainService.updateEdgeRuleChains(tenantId, savedEdge.getId()); RuleChain ruleChain = ruleChainService.findRuleChainById(tenantId, ruleChainId); saveEventToEdgeQueue(tenantId, edge.getId(), EdgeQueueEntityType.RULE_CHAIN, DataConstants.ENTITY_UPDATED, mapper.writeValueAsString(ruleChain), new FutureCallback() { @Override @@ -647,6 +657,23 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic return savedEdge; } + @Override + public ListenableFuture> findEdgesByTenantIdAndRuleChainId(TenantId tenantId, RuleChainId ruleChainId, TimePageLink pageLink) { + log.trace("Executing findEdgesByTenantIdAndRuleChainId, tenantId [{}], ruleChainId [{}], pageLink [{}]", tenantId, ruleChainId, pageLink); + Validator.validateId(tenantId, "Incorrect tenantId " + tenantId); + Validator.validateId(ruleChainId, "Incorrect ruleChainId " + ruleChainId); + Validator.validatePageLink(pageLink, "Incorrect page link " + pageLink); + ListenableFuture> edges = edgeDao.findEdgesByTenantIdAndRuleChainId(tenantId.getId(), ruleChainId.getId(), pageLink); + + return Futures.transform(edges, new Function, TimePageData>() { + @Nullable + @Override + public TimePageData apply(@Nullable List edges) { + return new TimePageData<>(edges, pageLink); + } + }, MoreExecutors.directExecutor()); + } + private DataValidator edgeValidator = new DataValidator() { 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 67ed99cac3..5e5f8398b5 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 @@ -347,7 +347,6 @@ public class ModelConstants { public static final String RULE_CHAIN_FIRST_RULE_NODE_ID_PROPERTY = "first_rule_node_id"; public static final String RULE_CHAIN_ROOT_PROPERTY = "root"; public static final String RULE_CHAIN_CONFIGURATION_PROPERTY = "configuration"; - public static final String RULE_CHAIN_ASSIGNED_EDGES_PROPERTY = "assigned_edges"; public static final String RULE_CHAIN_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "rule_chain_by_tenant_and_search_text"; public static final String RULE_CHAIN_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "rule_chain_by_tenant_by_type_and_search_text"; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/RuleChainEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/RuleChainEntity.java index 9a03f5c582..9232a8f9ff 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/RuleChainEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/RuleChainEntity.java @@ -20,17 +20,12 @@ import com.datastax.driver.mapping.annotations.ClusteringColumn; import com.datastax.driver.mapping.annotations.Column; import com.datastax.driver.mapping.annotations.PartitionKey; import com.datastax.driver.mapping.annotations.Table; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; import lombok.ToString; import lombok.extern.slf4j.Slf4j; -import org.springframework.util.StringUtils; -import org.thingsboard.server.common.data.ShortEdgeInfo; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.TenantId; @@ -41,14 +36,11 @@ import org.thingsboard.server.dao.model.SearchTextEntity; import org.thingsboard.server.dao.model.type.JsonCodec; import org.thingsboard.server.dao.model.type.RuleChainTypeCodec; -import java.io.IOException; -import java.util.HashSet; import java.util.UUID; import static org.thingsboard.server.dao.model.ModelConstants.ADDITIONAL_INFO_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.DEBUG_MODE; import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY; -import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_ASSIGNED_EDGES_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_COLUMN_FAMILY_NAME; import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_CONFIGURATION_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_FIRST_RULE_NODE_ID_PROPERTY; @@ -64,10 +56,6 @@ import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPER @ToString public class RuleChainEntity implements SearchTextEntity { - private static final ObjectMapper objectMapper = new ObjectMapper(); - private static final JavaType assignedEdgesType = - objectMapper.getTypeFactory().constructCollectionType(HashSet.class, ShortEdgeInfo.class); - @PartitionKey @Column(name = ID_PROPERTY) private UUID id; @@ -93,10 +81,6 @@ public class RuleChainEntity implements SearchTextEntity { @Column(name = ADDITIONAL_INFO_PROPERTY, codec = JsonCodec.class) private JsonNode additionalInfo; - @Getter @Setter - @Column(name = RULE_CHAIN_ASSIGNED_EDGES_PROPERTY) - private String assignedEdges; - public RuleChainEntity() { } @@ -113,13 +97,6 @@ public class RuleChainEntity implements SearchTextEntity { this.debugMode = ruleChain.isDebugMode(); this.configuration = ruleChain.getConfiguration(); this.additionalInfo = ruleChain.getAdditionalInfo(); - if (ruleChain.getAssignedEdges() != null) { - try { - this.assignedEdges = objectMapper.writeValueAsString(ruleChain.getAssignedEdges()); - } catch (JsonProcessingException e) { - log.error("Unable to serialize assigned edges to string!", e); - } - } } @Override @@ -208,13 +185,6 @@ public class RuleChainEntity implements SearchTextEntity { ruleChain.setDebugMode(this.debugMode); ruleChain.setConfiguration(this.configuration); ruleChain.setAdditionalInfo(this.additionalInfo); - if (!StringUtils.isEmpty(assignedEdges)) { - try { - ruleChain.setAssignedEdges(objectMapper.readValue(assignedEdges, assignedEdgesType)); - } catch (IOException e) { - log.warn("Unable to parse assigned edges!", e); - } - } return ruleChain; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleChainEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleChainEntity.java index ce62554fa1..1695bf7370 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleChainEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleChainEntity.java @@ -16,17 +16,12 @@ package org.thingsboard.server.dao.model.sql; import com.datastax.driver.core.utils.UUIDs; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.extern.slf4j.Slf4j; import org.hibernate.annotations.Type; import org.hibernate.annotations.TypeDef; -import org.springframework.util.StringUtils; -import org.thingsboard.server.common.data.ShortEdgeInfo; import org.thingsboard.server.common.data.UUIDConverter; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; @@ -44,8 +39,6 @@ import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; import javax.persistence.Table; -import java.io.IOException; -import java.util.HashSet; import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_TYPE_PROPERTY; @@ -57,10 +50,6 @@ import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_TYPE_PR @Table(name = ModelConstants.RULE_CHAIN_COLUMN_FAMILY_NAME) public class RuleChainEntity extends BaseSqlEntity implements SearchTextEntity { - private static final ObjectMapper objectMapper = new ObjectMapper(); - private static final JavaType assignedEdgesType = - objectMapper.getTypeFactory().constructCollectionType(HashSet.class, ShortEdgeInfo.class); - @Column(name = ModelConstants.RULE_CHAIN_TENANT_ID_PROPERTY) private String tenantId; @@ -91,9 +80,6 @@ public class RuleChainEntity extends BaseSqlEntity implements SearchT @Column(name = ModelConstants.ADDITIONAL_INFO_PROPERTY) private JsonNode additionalInfo; - @Column(name = ModelConstants.RULE_CHAIN_ASSIGNED_EDGES_PROPERTY) - private String assignedEdges; - public RuleChainEntity() { } @@ -112,13 +98,6 @@ public class RuleChainEntity extends BaseSqlEntity implements SearchT this.debugMode = ruleChain.isDebugMode(); this.configuration = ruleChain.getConfiguration(); this.additionalInfo = ruleChain.getAdditionalInfo(); - if (ruleChain.getAssignedEdges() != null) { - try { - this.assignedEdges = objectMapper.writeValueAsString(ruleChain.getAssignedEdges()); - } catch (JsonProcessingException e) { - log.error("Unable to serialize assigned edges to string!", e); - } - } } @Override @@ -145,13 +124,6 @@ public class RuleChainEntity extends BaseSqlEntity implements SearchT ruleChain.setDebugMode(debugMode); ruleChain.setConfiguration(configuration); ruleChain.setAdditionalInfo(additionalInfo); - if (!StringUtils.isEmpty(assignedEdges)) { - try { - ruleChain.setAssignedEdges(objectMapper.readValue(assignedEdges, assignedEdgesType)); - } catch (IOException e) { - log.warn("Unable to parse assigned edges!", e); - } - } return ruleChain; } } 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 ea23781e62..a86d10326e 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 @@ -19,6 +19,7 @@ import com.google.common.base.Function; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; +import jnr.ffi.annotations.In; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -49,6 +50,7 @@ import org.thingsboard.server.dao.edge.EdgeDao; import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.relation.RelationDao; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.service.TimePaginatedRemover; @@ -62,6 +64,9 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; +import static org.thingsboard.server.dao.service.Validator.validateId; +import static org.thingsboard.server.dao.service.Validator.validateString; + /** * Created by igor on 3/12/18. */ @@ -69,6 +74,8 @@ import java.util.concurrent.ExecutionException; @Slf4j public class BaseRuleChainService extends AbstractEntityService implements RuleChainService { + public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; + @Autowired private RuleChainDao ruleChainDao; @@ -376,11 +383,18 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC if (ruleChain.isRoot()) { throw new DataValidationException("Deletion of Root Tenant Rule Chain is prohibited!"); } - if (ruleChain.getAssignedEdges() != null && !ruleChain.getAssignedEdges().isEmpty()) { - for (ShortEdgeInfo assignedEdge : ruleChain.getAssignedEdges()) { - if (assignedEdge.getRootRuleChainId() != null && assignedEdge.getRootRuleChainId().equals(ruleChainId)) { - throw new DataValidationException("Can't delete rule chain that is root for edge [" + assignedEdge.getTitle() + "]. Please assign another root rule chain first to the edge!"); + if (RuleChainType.EDGE.equals(ruleChain.getType())) { + try { + TimePageData edges = edgeService.findEdgesByTenantIdAndRuleChainId(tenantId, ruleChainId, new TimePageLink(Integer.MAX_VALUE)).get(); + if (edges != null && edges.getData() != null && !edges.getData().isEmpty()) { + for (Edge edge : edges.getData()) { + if (edge.getRootRuleChainId() != null && edge.getRootRuleChainId().equals(ruleChainId)) { + throw new DataValidationException("Can't delete rule chain that is root for edge [" + edge.getName() + "]. Please assign another root rule chain first to the edge!"); + } + } } + } catch (InterruptedException | ExecutionException e) { + log.error("Can't get edges by tenant id [{}] and rule chain id [{}]", tenantId.getId(), ruleChainId.getId(), e); } } } @@ -403,14 +417,11 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC if (!edge.getTenantId().getId().equals(ruleChain.getTenantId().getId())) { throw new DataValidationException("Can't assign ruleChain to edge from different tenant!"); } - if (ruleChain.addAssignedEdge(edge)) { - try { - createRelation(tenantId, new EntityRelation(edgeId, ruleChainId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE)); - } catch (ExecutionException | InterruptedException e) { - log.warn("[{}] Failed to create ruleChain relation. Edge Id: [{}]", ruleChainId, edgeId); - throw new RuntimeException(e); - } - ruleChain = saveRuleChain(ruleChain); + try { + createRelation(tenantId, new EntityRelation(edgeId, ruleChainId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE)); + } catch (ExecutionException | InterruptedException e) { + log.warn("[{}] Failed to create ruleChain relation. Edge Id: [{}]", ruleChainId, edgeId); + throw new RuntimeException(e); } return ruleChain; } @@ -425,17 +436,13 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC if (!remove && edge.getRootRuleChainId() != null && edge.getRootRuleChainId().equals(ruleChainId)) { throw new DataValidationException("Can't unassign root rule chain from edge [" + edge.getName() + "]. Please assign another root rule chain first!"); } - if (ruleChain.removeAssignedEdge(edge)) { - try { - deleteRelation(tenantId, new EntityRelation(edgeId, ruleChainId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE)); - } catch (ExecutionException | InterruptedException e) { - log.warn("[{}] Failed to delete rule chain relation. Edge Id: [{}]", ruleChainId, edgeId); - throw new RuntimeException(e); - } - return saveRuleChain(ruleChain); - } else { - return ruleChain; + try { + deleteRelation(tenantId, new EntityRelation(edgeId, ruleChainId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE)); + } catch (ExecutionException | InterruptedException e) { + log.warn("[{}] Failed to delete rule chain relation. Edge Id: [{}]", ruleChainId, edgeId); + throw new RuntimeException(e); } + return ruleChain; } @Override @@ -449,23 +456,11 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC new EdgeRuleChainsUnassigner(edge).removeEntities(tenantId, edge); } - @Override - public void updateEdgeRuleChains(TenantId tenantId, EdgeId edgeId) { - log.trace("Executing updateEdgeRuleChains, edgeId [{}]", edgeId); - Validator.validateId(edgeId, "Incorrect edgeId " + edgeId); - Edge edge = edgeService.findEdgeById(tenantId, edgeId); - if (edge == null) { - throw new DataValidationException("Can't update ruleChains for non-existent edge!"); - } - new EdgeRuleChainsUpdater(edge).removeEntities(tenantId, edge); - } - - @Override public ListenableFuture> findRuleChainsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink) { log.trace("Executing findRuleChainsByTenantIdAndEdgeId, tenantId [{}], edgeId [{}], pageLink [{}]", tenantId, edgeId, pageLink); Validator.validateId(tenantId, "Incorrect tenantId " + tenantId); - Validator.validateId(edgeId, "Incorrect customerId " + edgeId); + Validator.validateId(edgeId, "Incorrect edgeId " + edgeId); Validator.validatePageLink(pageLink, "Incorrect page link " + pageLink); ListenableFuture> ruleChains = ruleChainDao.findRuleChainsByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink); @@ -499,13 +494,45 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC ruleChainDao.save(tenantId, ruleChain); return true; } catch (ExecutionException | InterruptedException e) { - log.warn("[{}] Failed to set default root edge rule chain, ruleChainId: [{}]", ruleChainId); + log.warn("Failed to set default root edge rule chain, ruleChainId: [{}]", ruleChainId, e); throw new RuntimeException(e); } } return false; } + @Override + public boolean addDefaultEdgeRuleChain(TenantId tenantId, RuleChainId ruleChainId) { + try { + createRelation(tenantId, new EntityRelation(tenantId, ruleChainId, + EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE)); + return true; + } catch (ExecutionException | InterruptedException e) { + log.warn("Failed to add default edge rule chain, ruleChainId: [{}]", ruleChainId, e); + throw new RuntimeException(e); + } + } + + @Override + public boolean removeDefaultEdgeRuleChain(TenantId tenantId, RuleChainId ruleChainId) { + try { + deleteRelation(tenantId, new EntityRelation(tenantId, ruleChainId, + EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN)); + return true; + } catch (ExecutionException | InterruptedException e) { + log.warn("Failed to remove default edge rule chain, ruleChainId: [{}]", ruleChainId, e); + throw new RuntimeException(e); + } + } + + @Override + public ListenableFuture> findDefaultEdgeRuleChainsByTenantId(TenantId tenantId) { + log.trace("Executing findDefaultEdgeRuleChainsByTenantId, tenantId [{}]", tenantId); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + return ruleChainDao.findDefaultEdgeRuleChainsByTenantId(tenantId.getId()); + } + + private void checkRuleNodesAndDelete(TenantId tenantId, RuleChainId ruleChainId) { List nodeRelations = getRuleChainToNodeRelations(tenantId, ruleChainId); for (EntityRelation relation : nodeRelations) { @@ -538,15 +565,6 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC relationService.deleteRelation(tenantId, relation); } - private RuleChain updateAssignedEdge(TenantId tenantId, RuleChainId ruleChainId, Edge edge) { - RuleChain ruleChain = findRuleChainById(tenantId, ruleChainId); - if (ruleChain.updateAssignedEdge(edge)) { - return saveRuleChain(ruleChain); - } else { - return ruleChain; - } - } - private DataValidator ruleChainValidator = new DataValidator() { @Override @@ -616,29 +634,4 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC unassignRuleChainFromEdge(edge.getTenantId(), new RuleChainId(entity.getUuidId()), this.edge.getId(), true); } } - - private class EdgeRuleChainsUpdater extends TimePaginatedRemover { - - private Edge edge; - - EdgeRuleChainsUpdater(Edge edge) { - this.edge = edge; - } - - @Override - protected List findEntities(TenantId tenantId, Edge edge, TimePageLink pageLink) { - try { - return ruleChainDao.findRuleChainsByTenantIdAndEdgeId(edge.getTenantId().getId(), edge.getId().getId(), pageLink).get(); - } catch (InterruptedException | ExecutionException e) { - log.warn("Failed to get ruleChains by tenantId [{}] and edgeId [{}].", edge.getTenantId().getId(), edge.getId().getId()); - throw new RuntimeException(e); - } - } - - @Override - protected void removeEntity(TenantId tenantId, RuleChain entity) { - updateAssignedEdge(edge.getTenantId(), new RuleChainId(entity.getUuidId()), this.edge); - } - - } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/CassandraRuleChainDao.java b/dao/src/main/java/org/thingsboard/server/dao/rule/CassandraRuleChainDao.java index 0a4cec2bf3..8f24b737f2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/CassandraRuleChainDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/CassandraRuleChainDao.java @@ -22,7 +22,9 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.id.EdgeId; +import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.page.TimePageLink; @@ -104,4 +106,17 @@ public class CassandraRuleChainDao extends CassandraAbstractSearchTextDao> findDefaultEdgeRuleChainsByTenantId(UUID tenantId) { + log.debug("Try to find default edge rule chains by tenantId [{}]", tenantId); + ListenableFuture> relations = relationDao.findAllByToAndType(new TenantId(tenantId), new TenantId(tenantId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE); + return Futures.transformAsync(relations, input -> { + List> ruleChainFutures = new ArrayList<>(input.size()); + for (EntityRelation relation : input) { + ruleChainFutures.add(findByIdAsync(new TenantId(tenantId), relation.getTo().getId())); + } + return Futures.successfulAsList(ruleChainFutures); + }, MoreExecutors.directExecutor()); + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java b/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java index b2aa92883c..b883f13fa8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java @@ -58,4 +58,12 @@ public interface RuleChainDao extends Dao { * @return the list of rule chain objects */ ListenableFuture> findRuleChainsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink); + + /** + * Find default edge rule chains by tenantId. + * + * @param tenantId the tenantId + * @return the list of rule chain objects + */ + ListenableFuture> findDefaultEdgeRuleChainsByTenantId(UUID tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java index 09d2ba687e..1bdaab82c1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java @@ -15,7 +15,10 @@ */ package org.thingsboard.server.dao.sql.edge; +import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.MoreExecutors; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.PageRequest; import org.springframework.data.repository.CrudRepository; @@ -24,11 +27,18 @@ import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.UUIDConverter; import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.id.EdgeId; +import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.common.data.page.TimePageLink; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; +import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.edge.EdgeDao; import org.thingsboard.server.dao.model.sql.EdgeEntity; +import org.thingsboard.server.dao.relation.RelationDao; import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; import org.thingsboard.server.dao.util.SqlDao; @@ -45,11 +55,15 @@ import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID_STR; @Component @SqlDao +@Slf4j public class JpaEdgeDao extends JpaAbstractSearchTextDao implements EdgeDao { @Autowired private EdgeRepository edgeRepository; + @Autowired + private RelationDao relationDao; + @Override protected Class getEntityClass() { return EdgeEntity.class; @@ -132,6 +146,19 @@ public class JpaEdgeDao extends JpaAbstractSearchTextDao imple return Optional.ofNullable(edge); } + @Override + public ListenableFuture> findEdgesByTenantIdAndRuleChainId(UUID tenantId, UUID ruleChainId, TimePageLink pageLink) { + log.debug("Try to find edges by tenantId [{}], ruleChainId [{}] and pageLink [{}]", tenantId, ruleChainId, pageLink); + ListenableFuture> relations = relationDao.findAllByToAndType(new TenantId(tenantId), new RuleChainId(ruleChainId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE); + return Futures.transformAsync(relations, input -> { + List> edgeFutures = new ArrayList<>(input.size()); + for (EntityRelation relation : input) { + edgeFutures.add(findByIdAsync(new TenantId(tenantId), relation.getTo().getId())); + } + return Futures.successfulAsList(edgeFutures); + }, MoreExecutors.directExecutor()); + } + private List convertTenantEdgeTypesToDto(UUID tenantId, List types) { List list = Collections.emptyList(); if (types != null && !types.isEmpty()) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java index cac8361df8..c8ca814fe8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java @@ -25,7 +25,9 @@ import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.UUIDConverter; +import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.id.EdgeId; +import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.page.TimePageLink; @@ -103,4 +105,17 @@ public class JpaRuleChainDao extends JpaAbstractSearchTextDao> findDefaultEdgeRuleChainsByTenantId(UUID tenantId) { + log.debug("Try to find default edge rule chains by tenantId [{}]", tenantId); + ListenableFuture> relations = relationDao.findAllByToAndType(new TenantId(tenantId), new TenantId(tenantId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE); + return Futures.transformAsync(relations, input -> { + List> ruleChainsFutures = new ArrayList<>(input.size()); + for (EntityRelation relation : input) { + ruleChainsFutures.add(findByIdAsync(new TenantId(tenantId), relation.getTo().getId())); + } + return Futures.successfulAsList(ruleChainsFutures); + }, MoreExecutors.directExecutor()); + } } diff --git a/dao/src/main/resources/sql/schema-entities-hsql.sql b/dao/src/main/resources/sql/schema-entities-hsql.sql index 6c40ea439f..3930f428f0 100644 --- a/dao/src/main/resources/sql/schema-entities-hsql.sql +++ b/dao/src/main/resources/sql/schema-entities-hsql.sql @@ -111,7 +111,6 @@ CREATE TABLE IF NOT EXISTS dashboard ( id varchar(31) NOT NULL CONSTRAINT dashboard_pkey PRIMARY KEY, configuration varchar(10000000), assigned_customers varchar(1000000), - assigned_edges varchar(10000000), search_text varchar(255), tenant_id varchar(31), title varchar(255) @@ -228,8 +227,7 @@ CREATE TABLE IF NOT EXISTS rule_chain ( root boolean, debug_mode boolean, search_text varchar(255), - tenant_id varchar(31), - assigned_edges varchar(10000000) + tenant_id varchar(31) ); CREATE TABLE IF NOT EXISTS rule_node ( diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index dfe7079864..10746084ae 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -111,7 +111,6 @@ CREATE TABLE IF NOT EXISTS dashboard ( id varchar(31) NOT NULL CONSTRAINT dashboard_pkey PRIMARY KEY, configuration varchar(10000000), assigned_customers varchar(1000000), - assigned_edges varchar(10000000), search_text varchar(255), tenant_id varchar(31), title varchar(255) @@ -228,8 +227,7 @@ CREATE TABLE IF NOT EXISTS rule_chain ( root boolean, debug_mode boolean, search_text varchar(255), - tenant_id varchar(31), - assigned_edges varchar(10000000) + tenant_id varchar(31) ); CREATE TABLE IF NOT EXISTS rule_node ( diff --git a/ui/src/app/api/rule-chain.service.js b/ui/src/app/api/rule-chain.service.js index 41a526c97f..78c891ba06 100644 --- a/ui/src/app/api/rule-chain.service.js +++ b/ui/src/app/api/rule-chain.service.js @@ -36,14 +36,14 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co resolveTargetRuleChains: resolveTargetRuleChains, testScript: testScript, getLatestRuleNodeDebugInput: getLatestRuleNodeDebugInput, - updateRuleChainEdges: updateRuleChainEdges, - addRuleChainEdges: addRuleChainEdges, - removeRuleChainEdges: removeRuleChainEdges, getEdgeRuleChains: getEdgeRuleChains, getEdgesRuleChains: getEdgesRuleChains, assignRuleChainToEdge: assignRuleChainToEdge, unassignRuleChainFromEdge: unassignRuleChainFromEdge, - setDefaultRootEdgeRuleChain: setDefaultRootEdgeRuleChain + setDefaultRootEdgeRuleChain: setDefaultRootEdgeRuleChain, + addDefaultEdgeRuleChain: addDefaultEdgeRuleChain, + removeDefaultEdgeRuleChain: removeDefaultEdgeRuleChain, + getDefaultEdgeRuleChains: getDefaultEdgeRuleChains }; return service; @@ -64,7 +64,7 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co url += '&type=' + type; } $http.get(url, config).then(function success(response) { - deferred.resolve(prepareRuleChains(response.data)); + deferred.resolve(response.data); }, function fail() { deferred.reject(); }); @@ -75,7 +75,7 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co var deferred = $q.defer(); var url = '/api/ruleChain/' + ruleChainId; $http.get(url, config).then(function success(response) { - deferred.resolve(prepareRuleChain(response.data)); + deferred.resolve(response.data); }, function fail() { deferred.reject(); }); @@ -85,8 +85,8 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co function saveRuleChain(ruleChain) { var deferred = $q.defer(); var url = '/api/ruleChain'; - $http.post(url, cleanRuleChain(ruleChain)).then(function success(response) { - deferred.resolve(prepareRuleChain(response.data)); + $http.post(url, ruleChain).then(function success(response) { + deferred.resolve(response.data); }, function fail() { deferred.reject(); }); @@ -273,7 +273,7 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co var deferred = $q.defer(); getRuleChain(ruleChainId, {ignoreErrors: true}).then( (ruleChain) => { - deferred.resolve(prepareRuleChain(ruleChain)); + deferred.resolve(ruleChain); }, () => { deferred.resolve({ @@ -310,39 +310,6 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co return deferred.promise; } - function updateRuleChainEdges(ruleChainId, edgeIds) { - var deferred = $q.defer(); - var url = '/api/ruleChain/' + ruleChainId + '/edges'; - $http.post(url, edgeIds).then(function success(response) { - deferred.resolve(prepareRuleChain(response.data)); - }, function fail() { - deferred.reject(); - }); - return deferred.promise; - } - - function addRuleChainEdges(ruleChainId, edgeIds) { - var deferred = $q.defer(); - var url = '/api/ruleChain/' + ruleChainId + '/edges/add'; - $http.post(url, edgeIds).then(function success(response) { - deferred.resolve(prepareRuleChain(response.data)); - }, function fail() { - deferred.reject(); - }); - return deferred.promise; - } - - function removeRuleChainEdges(ruleChainId, edgeIds) { - var deferred = $q.defer(); - var url = '/api/ruleChain/' + ruleChainId + '/edges/remove'; - $http.post(url, edgeIds).then(function success(response) { - deferred.resolve(prepareRuleChain(response.data)); - }, function fail() { - deferred.reject(); - }); - return deferred.promise; - } - function getEdgesRuleChains(pageLink, config) { return getRuleChains(pageLink, config, types.edgeRuleChainType); } @@ -354,7 +321,6 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co url += '&offset=' + pageLink.idOffset; } $http.get(url, config).then(function success(response) { - response.data = prepareRuleChains(response.data); if (pageLink.textSearch) { response.data.data = $filter('filter')(response.data.data, {title: pageLink.textSearch}); } @@ -369,7 +335,7 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co var deferred = $q.defer(); var url = '/api/edge/' + edgeId + '/ruleChain/' + ruleChainId; $http.post(url, null).then(function success(response) { - deferred.resolve(prepareRuleChain(response.data)); + deferred.resolve(response.data); }, function fail() { deferred.reject(); }); @@ -380,7 +346,7 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co var deferred = $q.defer(); var url = '/api/edge/' + edgeId + '/ruleChain/' + ruleChainId; $http.delete(url).then(function success(response) { - deferred.resolve(prepareRuleChain(response.data)); + deferred.resolve(response.data); }, function fail() { deferred.reject(); }); @@ -398,35 +364,36 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co return deferred.promise; } - function prepareRuleChains(ruleChainsData) { - if (ruleChainsData.data) { - for (var i = 0; i < ruleChainsData.data.length; i++) { - ruleChainsData.data[i] = prepareRuleChain(ruleChainsData.data[i]); - } - } - return ruleChainsData; + function addDefaultEdgeRuleChain(ruleChainId) { + var deferred = $q.defer(); + var url = '/api/ruleChain/' + ruleChainId + '/defaultEdge'; + $http.post(url, null).then(function success(response) { + deferred.resolve(response.data); + }, function fail() { + deferred.reject(); + }); + return deferred.promise; } - function prepareRuleChain(ruleChain) { - ruleChain.assignedEdgesText = ""; - ruleChain.assignedEdgesIds = []; - - if (ruleChain.assignedEdges && ruleChain.assignedEdges.length) { - var assignedEdgesTitles = []; - for (var j = 0; j < ruleChain.assignedEdges.length; j++) { - var assignedEdge = ruleChain.assignedEdges[j]; - ruleChain.assignedEdgesIds.push(assignedEdge.edgeId.id); - assignedEdgesTitles.push(assignedEdge.title); - } - ruleChain.assignedEdgesText = assignedEdgesTitles.join(', '); - } - - return ruleChain; + function removeDefaultEdgeRuleChain(ruleChainId) { + var deferred = $q.defer(); + var url = '/api/ruleChain/' + ruleChainId + '/defaultEdge'; + $http.delete(url).then(function success(response) { + deferred.resolve(response.data); + }, function fail() { + deferred.reject(); + }); + return deferred.promise; } - function cleanRuleChain(ruleChain) { - delete ruleChain.assignedEdgesText; - delete ruleChain.assignedEdgesIds; - return ruleChain; + function getDefaultEdgeRuleChains(config) { + var deferred = $q.defer(); + var url = '/api/ruleChain/defaultEdgeRuleChains'; + $http.get(url, config).then(function success(response) { + deferred.resolve(response.data); + }, function fail() { + deferred.reject(); + }); + return deferred.promise; } } diff --git a/ui/src/app/rulechain/rulechains.controller.js b/ui/src/app/rulechain/rulechains.controller.js index f3e159566c..ff1d393618 100644 --- a/ui/src/app/rulechain/rulechains.controller.js +++ b/ui/src/app/rulechain/rulechains.controller.js @@ -115,7 +115,7 @@ export default function RuleChainsController(ruleChainService, userService, edge if (vm.ruleChainsScope === 'tenant') { fetchRuleChainsFunction = function (pageLink) { - return fetchRuleChains(pageLink, 'SYSTEM'); + return fetchRuleChains(pageLink, types.systemRuleChainType); }; deleteRuleChainFunction = function (ruleChainId) { return deleteRuleChain(ruleChainId); @@ -176,7 +176,7 @@ export default function RuleChainsController(ruleChainService, userService, edge } else if (vm.ruleChainsScope === 'edges') { fetchRuleChainsFunction = function (pageLink) { - return fetchRuleChains(pageLink, 'EDGE'); + return fetchRuleChains(pageLink, types.edgeRuleChainType); }; deleteRuleChainFunction = function (ruleChainId) { return deleteRuleChain(ruleChainId); @@ -316,7 +316,28 @@ export default function RuleChainsController(ruleChainService, userService, edge } function fetchRuleChains(pageLink, type) { - return ruleChainService.getRuleChains(pageLink, null, type); + if (type === types.systemRuleChainType) { + return ruleChainService.getRuleChains(pageLink, null, type); + } else { + var deferred = $q.defer(); + ruleChainService.getRuleChains(pageLink, null, type).then( + // TODO: deaflynx + // function success(response) { + // ruleChainService.getDefaultEdgeRuleChains().then( + // function success(defaultEdgeRuleChains) { + // // response.data merge with defaultEdgeRuleChains + // deferred.resolve(data); + // }, + // function fail() { + // deferred.reject(); + // } + // ); + // }, + function fail() { + deferred.reject(); + }); + return deferred.promise; + } } function saveRuleChain(ruleChain) { From 85a88ee49939b9ef1c3b3bd5304c376ad736ec8c Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Wed, 27 May 2020 14:40:39 +0300 Subject: [PATCH 17/52] Removed assigned edges from dashboard --- .../controller/DashboardController.java | 152 ------------------ .../dao/dashboard/DashboardService.java | 2 - .../server/dao/edge/EdgeService.java | 3 + .../server/common/data/DashboardInfo.java | 26 --- .../dao/dashboard/DashboardServiceImpl.java | 77 ++------- .../server/dao/edge/CassandraEdgeDao.java | 14 ++ .../thingsboard/server/dao/edge/EdgeDao.java | 10 ++ .../server/dao/edge/EdgeServiceImpl.java | 40 ++++- .../server/dao/model/ModelConstants.java | 1 - .../dao/model/nosql/DashboardInfoEntity.java | 29 ---- .../server/dao/model/sql/DashboardEntity.java | 20 --- .../dao/model/sql/DashboardInfoEntity.java | 22 +-- .../server/dao/sql/edge/JpaEdgeDao.java | 14 ++ 13 files changed, 87 insertions(+), 323 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java index c8e364a570..eaa9c97ec8 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java @@ -540,158 +540,6 @@ public class DashboardController extends BaseController { } } - @PreAuthorize("hasAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/dashboard/{dashboardId}/edges", method = RequestMethod.POST) - @ResponseBody - public Dashboard updateDashboardEdges(@PathVariable(DASHBOARD_ID) String strDashboardId, - @RequestBody String[] strEdgeIds) throws ThingsboardException { - checkParameter(DASHBOARD_ID, strDashboardId); - try { - DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); - Dashboard dashboard = checkDashboardId(dashboardId, Operation.ASSIGN_TO_EDGE); - - Set edgeIds = new HashSet<>(); - if (strEdgeIds != null) { - for (String strEdgeId : strEdgeIds) { - edgeIds.add(new EdgeId(toUUID(strEdgeId))); - } - } - - Set addedEdgeIds = new HashSet<>(); - Set removedEdgeIds = new HashSet<>(); - for (EdgeId edgeId : edgeIds) { - if (!dashboard.isAssignedToEdge(edgeId)) { - addedEdgeIds.add(edgeId); - } - } - - Set assignedEdges = dashboard.getAssignedEdges(); - if (assignedEdges != null) { - for (ShortEdgeInfo edgeInfo : assignedEdges) { - if (!edgeIds.contains(edgeInfo.getEdgeId())) { - removedEdgeIds.add(edgeInfo.getEdgeId()); - } - } - } - - if (addedEdgeIds.isEmpty() && removedEdgeIds.isEmpty()) { - return dashboard; - } else { - Dashboard savedDashboard = null; - for (EdgeId edgeId : addedEdgeIds) { - savedDashboard = checkNotNull(dashboardService.assignDashboardToEdge(getCurrentUser().getTenantId(), dashboardId, edgeId)); - ShortEdgeInfo edgeInfo = savedDashboard.getAssignedEdgeInfo(edgeId); - logEntityAction(dashboardId, savedDashboard, - null, - ActionType.ASSIGNED_TO_EDGE, null, strDashboardId, edgeId.toString(), edgeInfo.getTitle()); - } - for (EdgeId edgeId : removedEdgeIds) { - ShortEdgeInfo edgeInfo = dashboard.getAssignedEdgeInfo(edgeId); - savedDashboard = checkNotNull(dashboardService.unassignDashboardFromEdge(getCurrentUser().getTenantId(), dashboardId, edgeId)); - logEntityAction(dashboardId, dashboard, - null, - ActionType.UNASSIGNED_FROM_EDGE, null, strDashboardId, edgeId.toString(), edgeInfo.getTitle()); - - } - return savedDashboard; - } - } catch (Exception e) { - - logEntityAction(emptyId(EntityType.DASHBOARD), null, - null, - ActionType.ASSIGNED_TO_EDGE, e, strDashboardId); - - throw handleException(e); - } - } - - @PreAuthorize("hasAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/dashboard/{dashboardId}/edges/add", method = RequestMethod.POST) - @ResponseBody - public Dashboard addDashboardEdges(@PathVariable(DASHBOARD_ID) String strDashboardId, - @RequestBody String[] strEdgeIds) throws ThingsboardException { - checkParameter(DASHBOARD_ID, strDashboardId); - try { - DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); - Dashboard dashboard = checkDashboardId(dashboardId, Operation.ASSIGN_TO_EDGE); - - Set edgeIds = new HashSet<>(); - if (strEdgeIds != null) { - for (String strEdgeId : strEdgeIds) { - EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); - if (!dashboard.isAssignedToEdge(edgeId)) { - edgeIds.add(edgeId); - } - } - } - - if (edgeIds.isEmpty()) { - return dashboard; - } else { - Dashboard savedDashboard = null; - for (EdgeId edgeId : edgeIds) { - savedDashboard = checkNotNull(dashboardService.assignDashboardToEdge(getCurrentUser().getTenantId(), dashboardId, edgeId)); - ShortEdgeInfo edgeInfo = savedDashboard.getAssignedEdgeInfo(edgeId); - logEntityAction(dashboardId, savedDashboard, - null, - ActionType.ASSIGNED_TO_EDGE, null, strDashboardId, edgeId.toString(), edgeInfo.getTitle()); - } - return savedDashboard; - } - } catch (Exception e) { - - logEntityAction(emptyId(EntityType.DASHBOARD), null, - null, - ActionType.ASSIGNED_TO_EDGE, e, strDashboardId); - - throw handleException(e); - } - } - - @PreAuthorize("hasAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/dashboard/{dashboardId}/edges/remove", method = RequestMethod.POST) - @ResponseBody - public Dashboard removeDashboardEdges(@PathVariable(DASHBOARD_ID) String strDashboardId, - @RequestBody String[] strEdgeIds) throws ThingsboardException { - checkParameter(DASHBOARD_ID, strDashboardId); - try { - DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); - Dashboard dashboard = checkDashboardId(dashboardId, Operation.UNASSIGN_FROM_EDGE); - - Set edgeIds = new HashSet<>(); - if (strEdgeIds != null) { - for (String strEdgeId : strEdgeIds) { - EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); - if (dashboard.isAssignedToEdge(edgeId)) { - edgeIds.add(edgeId); - } - } - } - - if (edgeIds.isEmpty()) { - return dashboard; - } else { - Dashboard savedDashboard = null; - for (EdgeId edgeId : edgeIds) { - ShortEdgeInfo edgeInfo = dashboard.getAssignedEdgeInfo(edgeId); - savedDashboard = checkNotNull(dashboardService.unassignDashboardFromEdge(getCurrentUser().getTenantId(), dashboardId, edgeId)); - logEntityAction(dashboardId, dashboard, - null, - ActionType.UNASSIGNED_FROM_EDGE, null, strDashboardId, edgeId.toString(), edgeInfo.getTitle()); - - } - return savedDashboard; - } - } catch (Exception e) { - - logEntityAction(emptyId(EntityType.DASHBOARD), null, - null, - ActionType.UNASSIGNED_FROM_EDGE, e, strDashboardId); - - throw handleException(e); - } - } - @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/edge/{edgeId}/dashboards", params = { "limit" }, method = RequestMethod.GET) @ResponseBody diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java index 09b809bcdc..f16d551eae 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java @@ -61,7 +61,5 @@ public interface DashboardService { void unassignEdgeDashboards(TenantId tenantId, EdgeId edgeId); - void updateEdgeDashboards(TenantId tenantId, EdgeId edgeId); - ListenableFuture> findDashboardsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink); } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java index 8b4baffbae..d3913cf691 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java @@ -22,6 +22,7 @@ import org.thingsboard.server.common.data.Event; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeSearchQuery; import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; @@ -82,6 +83,8 @@ public interface EdgeService { Edge setEdgeRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) throws IOException; ListenableFuture> findEdgesByTenantIdAndRuleChainId(TenantId tenantId, RuleChainId ruleChainId, TimePageLink pageLink); + + ListenableFuture> findEdgesByTenantIdAndDashboardId(TenantId tenantId, DashboardId dashboardId, TimePageLink pageLink); } 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 263e756082..97889a488e 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,9 +32,6 @@ public class DashboardInfo extends SearchTextBased implements HasNa private String title; private Set assignedCustomers; - @Getter @Setter - private Set assignedEdges; - public DashboardInfo() { super(); } @@ -123,29 +120,6 @@ public class DashboardInfo extends SearchTextBased implements HasNa } } - public boolean isAssignedToEdge(EdgeId edgeId) { - return EdgeUtils.isAssignedToEdge(this.assignedEdges, edgeId); - } - - public ShortEdgeInfo getAssignedEdgeInfo(EdgeId edgeId) { - return EdgeUtils.getAssignedEdgeInfo(this.assignedEdges, edgeId); - } - - public boolean addAssignedEdge(Edge edge) { - if (this.assignedEdges == null) { - this.assignedEdges = new HashSet<>(); - } - return EdgeUtils.addAssignedEdge(this.assignedEdges, edge.toShortEdgeInfo()); - } - - public boolean updateAssignedEdge(Edge edge) { - return EdgeUtils.updateAssignedEdge(this.assignedEdges, edge.toShortEdgeInfo()); - } - - public boolean removeAssignedEdge(Edge edge) { - return EdgeUtils.removeAssignedEdge(this.assignedEdges, edge.toShortEdgeInfo()); - } - @Override @JsonProperty(access = JsonProperty.Access.READ_ONLY) public String getName() { diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java index a067fc72cb..3658dc22e8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java @@ -245,17 +245,13 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb if (!edge.getTenantId().getId().equals(dashboard.getTenantId().getId())) { throw new DataValidationException("Can't assign dashboard to edge from different tenant!"); } - if (dashboard.addAssignedEdge(edge)) { - try { - createRelation(tenantId, new EntityRelation(edgeId, dashboardId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE)); - } catch (ExecutionException | InterruptedException e) { - log.warn("[{}] Failed to create dashboard relation. Edge Id: [{}]", dashboardId, edgeId); - throw new RuntimeException(e); - } - return saveDashboard(dashboard); - } else { - return dashboard; + try { + createRelation(tenantId, new EntityRelation(edgeId, dashboardId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE)); + } catch (ExecutionException | InterruptedException e) { + log.warn("[{}] Failed to create dashboard relation. Edge Id: [{}]", dashboardId, edgeId); + throw new RuntimeException(e); } + return dashboard; } @Override @@ -265,17 +261,13 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb if (edge == null) { throw new DataValidationException("Can't unassign dashboard from non-existent edge!"); } - if (dashboard.removeAssignedEdge(edge)) { - try { - deleteRelation(tenantId, new EntityRelation(edgeId, dashboardId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE)); - } catch (ExecutionException | InterruptedException e) { - log.warn("[{}] Failed to delete dashboard relation. Edge Id: [{}]", dashboardId, edgeId); - throw new RuntimeException(e); - } - return saveDashboard(dashboard); - } else { - return dashboard; + try { + deleteRelation(tenantId, new EntityRelation(edgeId, dashboardId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE)); + } catch (ExecutionException | InterruptedException e) { + log.warn("[{}] Failed to delete dashboard relation. Edge Id: [{}]", dashboardId, edgeId); + throw new RuntimeException(e); } + return dashboard; } @Override @@ -289,17 +281,6 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb new EdgeDashboardsUnassigner(edge).removeEntities(tenantId, edge); } - @Override - public void updateEdgeDashboards(TenantId tenantId, EdgeId edgeId) { - log.trace("Executing updateEdgeDashboards, edgeId [{}]", edgeId); - Validator.validateId(edgeId, "Incorrect edgeId " + edgeId); - Edge edge = edgeDao.findById(tenantId, edgeId.getId()); - if (edge == null) { - throw new DataValidationException("Can't update dashboards for non-existent edge!"); - } - new EdgeDashboardsUpdater(edge).removeEntities(tenantId, edge); - } - @Override public ListenableFuture> findDashboardsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink) { log.trace("Executing findDashboardsByTenantIdAndEdgeId, tenantId [{}], edgeId [{}], pageLink [{}]", tenantId, edgeId, pageLink); @@ -317,15 +298,6 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb }, MoreExecutors.directExecutor()); } - private Dashboard updateAssignedEdge(TenantId tenantId, DashboardId dashboardId, Edge edge) { - Dashboard dashboard = findDashboardById(tenantId, dashboardId); - if (dashboard.updateAssignedEdge(edge)) { - return saveDashboard(dashboard); - } else { - return dashboard; - } - } - private DataValidator dashboardValidator = new DataValidator() { @Override @@ -432,29 +404,4 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb } } - - private class EdgeDashboardsUpdater extends TimePaginatedRemover { - - private Edge edge; - - EdgeDashboardsUpdater(Edge edge) { - this.edge = edge; - } - - @Override - protected List findEntities(TenantId tenantId, Edge edge, TimePageLink pageLink) { - try { - return dashboardInfoDao.findDashboardsByTenantIdAndEdgeId(edge.getTenantId().getId(), edge.getId().getId(), pageLink).get(); - } catch (InterruptedException | ExecutionException e) { - log.warn("Failed to get dashboards by tenantId [{}] and edgeId [{}].", edge.getTenantId().getId(), edge.getId().getId()); - throw new RuntimeException(e); - } - } - - @Override - protected void removeEntity(TenantId tenantId, DashboardInfo entity) { - updateAssignedEdge(edge.getTenantId(), new DashboardId(entity.getUuidId()), this.edge); - } - - } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/CassandraEdgeDao.java b/dao/src/main/java/org/thingsboard/server/dao/edge/CassandraEdgeDao.java index 42253db06c..835917b153 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/CassandraEdgeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/CassandraEdgeDao.java @@ -24,6 +24,7 @@ import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; @@ -120,4 +121,17 @@ public class CassandraEdgeDao extends CassandraAbstractSearchTextDao> findEdgesByTenantIdAndDashboardId(UUID tenantId, UUID dashboardId, TimePageLink pageLink) { + log.debug("Try to find edges by tenantId [{}], dashboardId [{}] and pageLink [{}]", tenantId, dashboardId, pageLink); + ListenableFuture> relations = relationDao.findAllByToAndType(new TenantId(tenantId), new DashboardId(dashboardId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE); + return Futures.transformAsync(relations, input -> { + List> edgeFutures = new ArrayList<>(input.size()); + for (EntityRelation relation : input) { + edgeFutures.add(findByIdAsync(new TenantId(tenantId), relation.getTo().getId())); + } + return Futures.successfulAsList(edgeFutures); + }, MoreExecutors.directExecutor()); + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java index 0002b96ad0..adc844f14e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java @@ -135,4 +135,14 @@ public interface EdgeDao extends Dao { * @return the list of rule chain objects */ ListenableFuture> findEdgesByTenantIdAndRuleChainId(UUID tenantId, UUID ruleChainId, TimePageLink pageLink); + + /** + * Find edges by tenantId, dashboardId and page link. + * + * @param tenantId the tenantId + * @param dashboardId the dashboardId + * @param pageLink the page link + * @return the list of rule chain objects + */ + ListenableFuture> findEdgesByTenantIdAndDashboardId(UUID tenantId, UUID dashboardId, TimePageLink pageLink); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java index 3bfb1d114e..31cbe1c765 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java @@ -32,6 +32,7 @@ import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Dashboard; +import org.thingsboard.server.common.data.DashboardInfo; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntitySubtype; @@ -48,6 +49,7 @@ import org.thingsboard.server.common.data.edge.EdgeQueueEntry; import org.thingsboard.server.common.data.edge.EdgeSearchQuery; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EntityId; @@ -191,9 +193,7 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic public Edge saveEdge(Edge edge) { log.trace("Executing saveEdge [{}]", edge); edgeValidator.validate(edge, Edge::getTenantId); - Edge savedEdge = edgeDao.save(edge.getTenantId(), edge); - dashboardService.updateEdgeDashboards(savedEdge.getTenantId(), savedEdge.getId()); - return savedEdge; + return edgeDao.save(edge.getTenantId(), edge); } @Override @@ -568,11 +568,19 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic case DataConstants.ENTITY_CREATED: case DataConstants.ENTITY_UPDATED: Dashboard dashboard = mapper.readValue(tbMsg.getData(), Dashboard.class); - if (dashboard.getAssignedEdges() != null && !dashboard.getAssignedEdges().isEmpty()) { - for (ShortEdgeInfo assignedEdge : dashboard.getAssignedEdges()) { - pushEventToEdge(tenantId, assignedEdge.getEdgeId(), EdgeQueueEntityType.DASHBOARD, tbMsg, callback); + ListenableFuture> future = findEdgesByTenantIdAndDashboardId(tenantId, dashboard.getId(), new TimePageLink(Integer.MAX_VALUE)); + Futures.transform(future, edges -> { + if (edges != null && edges.getData() != null && !edges.getData().isEmpty()) { + try { + for (Edge edge : edges.getData()) { + pushEventToEdge(tenantId, edge.getId(), EdgeQueueEntityType.DASHBOARD, tbMsg, callback); + } + } catch (IOException e) { + log.error("Can't push event to edge", e); + } } - } + return null; + }, MoreExecutors.directExecutor()); break; } } @@ -674,6 +682,24 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic }, MoreExecutors.directExecutor()); } + @Override + public ListenableFuture> findEdgesByTenantIdAndDashboardId(TenantId tenantId, DashboardId dashboardId, TimePageLink pageLink) { + log.trace("Executing findEdgesByTenantIdAndDashboardId, tenantId [{}], dashboardId [{}], pageLink [{}]", tenantId, dashboardId, pageLink); + Validator.validateId(tenantId, "Incorrect tenantId " + tenantId); + Validator.validateId(dashboardId, "Incorrect dashboardId " + dashboardId); + Validator.validatePageLink(pageLink, "Incorrect page link " + pageLink); + ListenableFuture> edges = edgeDao.findEdgesByTenantIdAndDashboardId(tenantId.getId(), dashboardId.getId(), pageLink); + + return Futures.transform(edges, new Function, TimePageData>() { + @Nullable + @Override + public TimePageData apply(@Nullable List edges) { + return new TimePageData<>(edges, pageLink); + } + }, MoreExecutors.directExecutor()); + } + + private DataValidator edgeValidator = new DataValidator() { 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 5e5f8398b5..7c089f8975 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 @@ -302,7 +302,6 @@ public class ModelConstants { public static final String DASHBOARD_TITLE_PROPERTY = TITLE_PROPERTY; public static final String DASHBOARD_CONFIGURATION_PROPERTY = "configuration"; public static final String DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY = "assigned_customers"; - public static final String DASHBOARD_ASSIGNED_EDGES_PROPERTY = "assigned_edges"; public static final String DASHBOARD_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "dashboard_by_tenant_and_search_text"; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/DashboardInfoEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/DashboardInfoEntity.java index 8c687eaccc..69eb43744c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/DashboardInfoEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/DashboardInfoEntity.java @@ -28,7 +28,6 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.util.StringUtils; import org.thingsboard.server.common.data.DashboardInfo; import org.thingsboard.server.common.data.ShortCustomerInfo; -import org.thingsboard.server.common.data.ShortEdgeInfo; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.model.SearchTextEntity; @@ -38,7 +37,6 @@ import java.util.HashSet; import java.util.UUID; import static org.thingsboard.server.dao.model.ModelConstants.DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY; -import static org.thingsboard.server.dao.model.ModelConstants.DASHBOARD_ASSIGNED_EDGES_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.DASHBOARD_COLUMN_FAMILY_NAME; import static org.thingsboard.server.dao.model.ModelConstants.DASHBOARD_TENANT_ID_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.DASHBOARD_TITLE_PROPERTY; @@ -54,8 +52,6 @@ public class DashboardInfoEntity implements SearchTextEntity { private static final ObjectMapper objectMapper = new ObjectMapper(); private static final JavaType assignedCustomersType = objectMapper.getTypeFactory().constructCollectionType(HashSet.class, ShortCustomerInfo.class); - private static final JavaType assignedEdgesType = - objectMapper.getTypeFactory().constructCollectionType(HashSet.class, ShortEdgeInfo.class); @PartitionKey(value = 0) @Column(name = ID_PROPERTY) @@ -74,9 +70,6 @@ public class DashboardInfoEntity implements SearchTextEntity { @Column(name = DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY) private String assignedCustomers; - @Column(name = DASHBOARD_ASSIGNED_EDGES_PROPERTY) - private String assignedEdges; - public DashboardInfoEntity() { super(); } @@ -96,13 +89,6 @@ public class DashboardInfoEntity implements SearchTextEntity { log.error("Unable to serialize assigned customers to string!", e); } } - if (dashboardInfo.getAssignedEdges() != null) { - try { - this.assignedEdges = objectMapper.writeValueAsString(dashboardInfo.getAssignedEdges()); - } catch (JsonProcessingException e) { - log.error("Unable to serialize assigned edges to string!", e); - } - } } public UUID getUuid() { @@ -137,14 +123,6 @@ public class DashboardInfoEntity implements SearchTextEntity { this.assignedCustomers = assignedCustomers; } - public String getAssignedEdges() { - return assignedEdges; - } - - public void setAssignedEdges(String assignedEdges) { - this.assignedEdges = assignedEdges; - } - @Override public String getSearchTextSource() { return getTitle(); @@ -174,13 +152,6 @@ public class DashboardInfoEntity implements SearchTextEntity { log.warn("Unable to parse assigned customers!", e); } } - if (!StringUtils.isEmpty(assignedEdges)) { - try { - dashboardInfo.setAssignedEdges(objectMapper.readValue(assignedEdges, assignedEdgesType)); - } catch (IOException e) { - log.warn("Unable to parse assigned edges!", e); - } - } return dashboardInfo; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardEntity.java index aef6795904..07cec4d12f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardEntity.java @@ -28,7 +28,6 @@ import org.hibernate.annotations.TypeDef; import org.springframework.util.StringUtils; import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.ShortCustomerInfo; -import org.thingsboard.server.common.data.ShortEdgeInfo; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.model.BaseSqlEntity; @@ -53,8 +52,6 @@ public final class DashboardEntity extends BaseSqlEntity implements S private static final ObjectMapper objectMapper = new ObjectMapper(); private static final JavaType assignedCustomersType = objectMapper.getTypeFactory().constructCollectionType(HashSet.class, ShortCustomerInfo.class); - private static final JavaType assignedEdgesType = - objectMapper.getTypeFactory().constructCollectionType(HashSet.class, ShortEdgeInfo.class); @Column(name = ModelConstants.DASHBOARD_TENANT_ID_PROPERTY) private String tenantId; @@ -68,9 +65,6 @@ public final class DashboardEntity extends BaseSqlEntity implements S @Column(name = ModelConstants.DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY) private String assignedCustomers; - @Column(name = ModelConstants.DASHBOARD_ASSIGNED_EDGES_PROPERTY) - private String assignedEdges; - @Type(type = "json") @Column(name = ModelConstants.DASHBOARD_CONFIGURATION_PROPERTY) private JsonNode configuration; @@ -94,13 +88,6 @@ public final class DashboardEntity extends BaseSqlEntity implements S log.error("Unable to serialize assigned customers to string!", e); } } - if (dashboard.getAssignedEdges() != null) { - try { - this.assignedEdges = objectMapper.writeValueAsString(dashboard.getAssignedEdges()); - } catch (JsonProcessingException e) { - log.error("Unable to serialize assigned edges to string!", e); - } - } this.configuration = dashboard.getConfiguration(); } @@ -129,13 +116,6 @@ public final class DashboardEntity extends BaseSqlEntity implements S log.warn("Unable to parse assigned customers!", e); } } - if (!StringUtils.isEmpty(assignedEdges)) { - try { - dashboard.setAssignedEdges(objectMapper.readValue(assignedEdges, assignedEdgesType)); - } catch (IOException e) { - log.warn("Unable to parse assigned edges!", e); - } - } dashboard.setConfiguration(configuration); return dashboard; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardInfoEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardInfoEntity.java index 0cd3102075..709ab4fee6 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardInfoEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardInfoEntity.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, @@ -25,7 +25,6 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.util.StringUtils; import org.thingsboard.server.common.data.DashboardInfo; import org.thingsboard.server.common.data.ShortCustomerInfo; -import org.thingsboard.server.common.data.ShortEdgeInfo; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.model.BaseSqlEntity; @@ -48,8 +47,6 @@ public class DashboardInfoEntity extends BaseSqlEntity implements private static final ObjectMapper objectMapper = new ObjectMapper(); private static final JavaType assignedCustomersType = objectMapper.getTypeFactory().constructCollectionType(HashSet.class, ShortCustomerInfo.class); - private static final JavaType assignedEdgesType = - objectMapper.getTypeFactory().constructCollectionType(HashSet.class, ShortEdgeInfo.class); @Column(name = ModelConstants.DASHBOARD_TENANT_ID_PROPERTY) private String tenantId; @@ -63,9 +60,6 @@ public class DashboardInfoEntity extends BaseSqlEntity implements @Column(name = ModelConstants.DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY) private String assignedCustomers; - @Column(name = ModelConstants.DASHBOARD_ASSIGNED_EDGES_PROPERTY) - private String assignedEdges; - public DashboardInfoEntity() { super(); } @@ -85,13 +79,6 @@ public class DashboardInfoEntity extends BaseSqlEntity implements log.error("Unable to serialize assigned customers to string!", e); } } - if (dashboardInfo.getAssignedEdges() != null) { - try { - this.assignedEdges = objectMapper.writeValueAsString(dashboardInfo.getAssignedEdges()); - } catch (JsonProcessingException e) { - log.error("Unable to serialize assigned edges to string!", e); - } - } } @Override @@ -123,13 +110,6 @@ public class DashboardInfoEntity extends BaseSqlEntity implements log.warn("Unable to parse assigned customers!", e); } } - if (!StringUtils.isEmpty(assignedEdges)) { - try { - dashboardInfo.setAssignedEdges(objectMapper.readValue(assignedEdges, assignedEdgesType)); - } catch (IOException e) { - log.warn("Unable to parse assigned edges!", e); - } - } return dashboardInfo; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java index 1bdaab82c1..e21a24ee0d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java @@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.UUIDConverter; import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; @@ -159,6 +160,19 @@ public class JpaEdgeDao extends JpaAbstractSearchTextDao imple }, MoreExecutors.directExecutor()); } + @Override + public ListenableFuture> findEdgesByTenantIdAndDashboardId(UUID tenantId, UUID dashboardId, TimePageLink pageLink) { + log.debug("Try to find edges by tenantId [{}], dashboardId [{}] and pageLink [{}]", tenantId, dashboardId, pageLink); + ListenableFuture> relations = relationDao.findAllByToAndType(new TenantId(tenantId), new DashboardId(dashboardId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE); + return Futures.transformAsync(relations, input -> { + List> edgeFutures = new ArrayList<>(input.size()); + for (EntityRelation relation : input) { + edgeFutures.add(findByIdAsync(new TenantId(tenantId), relation.getTo().getId())); + } + return Futures.successfulAsList(edgeFutures); + }, MoreExecutors.directExecutor()); + } + private List convertTenantEdgeTypesToDto(UUID tenantId, List types) { List list = Collections.emptyList(); if (types != null && !types.isEmpty()) { From 5d385809e747c154ae1c70b18510ab6454053314 Mon Sep 17 00:00:00 2001 From: deaflynx Date: Sat, 30 May 2020 17:35:51 +0300 Subject: [PATCH 18/52] Default edge rulechain label fixed. Fixed makeDefaultEdgeRuleChain server side --- .../dao/model/sql/DashboardInfoEntity.java | 2 +- .../dao/rule/CassandraRuleChainDao.java | 2 +- .../server/dao/sql/rule/JpaRuleChainDao.java | 2 +- .../dao/service/BaseRuleChainServiceTest.java | 23 +++ msa/js-executor/package-lock.json | 23 +-- ui/src/app/locale/locale.constant-en_US.json | 9 +- ui/src/app/rulechain/rulechain-card.tpl.html | 10 +- ui/src/app/rulechain/rulechain.routes.js | 4 +- ui/src/app/rulechain/rulechains.controller.js | 133 +++++++++++++++--- 9 files changed, 161 insertions(+), 47 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardInfoEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardInfoEntity.java index 709ab4fee6..f26cf21934 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardInfoEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardInfoEntity.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/dao/src/main/java/org/thingsboard/server/dao/rule/CassandraRuleChainDao.java b/dao/src/main/java/org/thingsboard/server/dao/rule/CassandraRuleChainDao.java index 8f24b737f2..31eca488b3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/CassandraRuleChainDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/CassandraRuleChainDao.java @@ -109,7 +109,7 @@ public class CassandraRuleChainDao extends CassandraAbstractSearchTextDao> findDefaultEdgeRuleChainsByTenantId(UUID tenantId) { log.debug("Try to find default edge rule chains by tenantId [{}]", tenantId); - ListenableFuture> relations = relationDao.findAllByToAndType(new TenantId(tenantId), new TenantId(tenantId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE); + ListenableFuture> relations = relationDao.findAllByFromAndType(new TenantId(tenantId), new TenantId(tenantId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE); return Futures.transformAsync(relations, input -> { List> ruleChainFutures = new ArrayList<>(input.size()); for (EntityRelation relation : input) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java index c8ca814fe8..8afbfabb62 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java @@ -109,7 +109,7 @@ public class JpaRuleChainDao extends JpaAbstractSearchTextDao> findDefaultEdgeRuleChainsByTenantId(UUID tenantId) { log.debug("Try to find default edge rule chains by tenantId [{}]", tenantId); - ListenableFuture> relations = relationDao.findAllByToAndType(new TenantId(tenantId), new TenantId(tenantId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE); + ListenableFuture> relations = relationDao.findAllByFromAndType(new TenantId(tenantId), new TenantId(tenantId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE); return Futures.transformAsync(relations, input -> { List> ruleChainsFutures = new ArrayList<>(input.size()); for (EntityRelation relation : input) { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseRuleChainServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseRuleChainServiceTest.java index 1c5c9658a6..f37017068e 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseRuleChainServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseRuleChainServiceTest.java @@ -23,6 +23,7 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageLink; @@ -318,6 +319,28 @@ public abstract class BaseRuleChainServiceTest extends AbstractServiceTest { ruleChainService.deleteRuleChainById(tenantId, savedRuleChainMetaData.getRuleChainId()); } + @Test + public void testGetDefaultEdgeRuleChains() throws Exception { + RuleChainId ruleChainId = saveRuleChainAndSetDefaultEdge("Default Edge Rule Chain 1"); + saveRuleChainAndSetDefaultEdge("Default Edge Rule Chain 2"); + List result = ruleChainService.findDefaultEdgeRuleChainsByTenantId(tenantId).get(); + Assert.assertEquals(2, result.size()); + + ruleChainService.removeDefaultEdgeRuleChain(tenantId, ruleChainId); + + result = ruleChainService.findDefaultEdgeRuleChainsByTenantId(tenantId).get(); + Assert.assertEquals(1, result.size()); + } + private RuleChainId saveRuleChainAndSetDefaultEdge(String name) { + RuleChain edgeRuleChain = new RuleChain(); + edgeRuleChain.setTenantId(tenantId); + edgeRuleChain.setType(RuleChainType.EDGE); + edgeRuleChain.setName(name); + RuleChain savedEdgeRuleChain = ruleChainService.saveRuleChain(edgeRuleChain); + ruleChainService.addDefaultEdgeRuleChain(tenantId, savedEdgeRuleChain.getId()); + return savedEdgeRuleChain.getId(); + } + private RuleChainMetaData createRuleChainMetadata() throws Exception { RuleChain ruleChain = new RuleChain(); ruleChain.setName("My RuleChain"); diff --git a/msa/js-executor/package-lock.json b/msa/js-executor/package-lock.json index 2d7b2b2726..9b4eb92a82 100644 --- a/msa/js-executor/package-lock.json +++ b/msa/js-executor/package-lock.json @@ -1942,14 +1942,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1964,20 +1962,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -2094,8 +2089,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -2107,7 +2101,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -2122,7 +2115,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -2234,8 +2226,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -2247,7 +2238,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -2369,7 +2359,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", diff --git a/ui/src/app/locale/locale.constant-en_US.json b/ui/src/app/locale/locale.constant-en_US.json index 9fef9f1679..1fbb81066e 100644 --- a/ui/src/app/locale/locale.constant-en_US.json +++ b/ui/src/app/locale/locale.constant-en_US.json @@ -1554,6 +1554,7 @@ "assign-rulechains": "Assign rulechains", "assign-new-rulechain": "Assign new rulechain", "delete-rulechains": "Delete rulechains", + "default": "Default", "unassign-rulechain": "Unassign rulechain", "unassign-rulechains": "Unassign rulechains", "unassign-rulechain-title": "Are you sure you want to unassign the rulechain '{{ruleChainTitle}}'?", @@ -1576,7 +1577,13 @@ "set-default-root-edge": "Make rule chain default root", "set-default-root-edge-rulechain-title": "Are you sure you want to make the rule chain '{{ruleChainName}}' default edge root?", "set-default-root-edge-rulechain-text": "After the confirmation the rule chain will become default edge root and will handle all incoming transport messages.", - "invalid-rulechain-type-error": "Unable to import rule chain: Invalid rule chain type. Expected type is {{expectedRuleChainType}}." + "invalid-rulechain-type-error": "Unable to import rule chain: Invalid rule chain type. Expected type is {{expectedRuleChainType}}.", + "set-default-edge": "Make edge rule chain default", + "set-default-edge-title": "Are you sure you want to make the edge rule chain '{{ruleChainName}}' default?", + "set-default-edge-text": "After the confirmation the edge rule chain will be added to default list and handle all incoming transport messages.", + "remove-default-edge": "Remove edge rule chain from defaults", + "remove-default-edge-title": "Are you sure you want to remove the edge rule chain '{{ruleChainName}}' from default list?", + "remove-default-edge-text": "After the confirmation the edge rule chain will stop handling all incoming transport messages." }, "rulenode": { "details": "Details", diff --git a/ui/src/app/rulechain/rulechain-card.tpl.html b/ui/src/app/rulechain/rulechain-card.tpl.html index 86a7bbbb22..4e28413327 100644 --- a/ui/src/app/rulechain/rulechain-card.tpl.html +++ b/ui/src/app/rulechain/rulechain-card.tpl.html @@ -15,5 +15,11 @@ limitations under the License. --> -
{{'rulechain.assigned-to-edges' | translate}}: '{{vm.item.assignedEdgesText}}'
-
rulechain.root
+
rulechain.root +
+
rulechain.default +
diff --git a/ui/src/app/rulechain/rulechain.routes.js b/ui/src/app/rulechain/rulechain.routes.js index 13c1844f74..8f12031873 100644 --- a/ui/src/app/rulechain/rulechain.routes.js +++ b/ui/src/app/rulechain/rulechain.routes.js @@ -209,7 +209,7 @@ export default function RuleChainRoutes($stateProvider, NodeTemplatePathProvider ruleChainsType: 'edge' }, ncyBreadcrumb: { - label: '{"icon": "settings_ethernet", "label": "{{ vm.edgeRuleChainsTitle }}", "translate": "false"}' + label: '{"icon": "settings_ethernet", "label": "rulechain.edge-rulechains"}' } }).state('home.edges.ruleChains.ruleChain', { url: '/:ruleChainId', @@ -249,4 +249,4 @@ export default function RuleChainRoutes($stateProvider, NodeTemplatePathProvider label: '{"icon": "settings_ethernet", "label": "{{ vm.ruleChain.name }}", "translate": "false"}' } }); -} \ No newline at end of file +} diff --git a/ui/src/app/rulechain/rulechains.controller.js b/ui/src/app/rulechain/rulechains.controller.js index ff1d393618..eac031011a 100644 --- a/ui/src/app/rulechain/rulechains.controller.js +++ b/ui/src/app/rulechain/rulechains.controller.js @@ -94,6 +94,8 @@ export default function RuleChainsController(ruleChainService, userService, edge vm.isRootRuleChain = isRootRuleChain; vm.isNonRootRuleChain = isNonRootRuleChain; + vm.isDefaultEdgeRuleChain = isDefaultEdgeRuleChain; + var defaultEdgeRuleChainIds = []; vm.exportRuleChain = exportRuleChain; vm.setRootRuleChain = setRootRuleChain; @@ -234,9 +236,10 @@ export default function RuleChainsController(ruleChainService, userService, edge details: function() { return $translate.instant('rulechain.import') }, icon: "file_upload" }); + } else if (vm.ruleChainsScope === 'edge') { fetchRuleChainsFunction = function (pageLink) { - return ruleChainService.getEdgeRuleChains(edgeId, pageLink); + return fetchEdgeRuleChains(edgeId, pageLink); }; deleteRuleChainFunction = function (ruleChainId) { return ruleChainService.unassignRuleChainFromEdge(edgeId, ruleChainId); @@ -252,6 +255,26 @@ export default function RuleChainsController(ruleChainService, userService, edge isEnabled: isNonRootRuleChain }); + ruleChainActionsList.push({ + onAction: function ($event, item) { + setDefaultEdgeRuleChain($event, item); + }, + name: function() { return $translate.instant('rulechain.set-default-edge') }, + details: function() { return $translate.instant('rulechain.set-default-edge') }, + icon: "bookmark_outline", + isEnabled: isNonDefaultEdgeRuleChain + }); + + ruleChainActionsList.push({ + onAction: function ($event, item) { + removeDefaultEdgeRuleChain($event, item); + }, + name: function() { return $translate.instant('rulechain.remove-default-edge') }, + details: function() { return $translate.instant('rulechain.remove-default-edge') }, + icon: "bookmark", + isEnabled: isDefaultEdgeRuleChain + }); + ruleChainActionsList.push( { onAction: function ($event, item) { @@ -315,29 +338,51 @@ export default function RuleChainsController(ruleChainService, userService, edge vm.grid = grid; } + function getDefaultEdges(ruleChains) { + var deferred = $q.defer(); + ruleChainService.getDefaultEdgeRuleChains(null).then( + function success(response) { + response.map(function (ruleChain) { + defaultEdgeRuleChainIds.push(ruleChain.id.id) + }); + + const data = ruleChains.data; + data.map(function (ruleChain) { + ruleChain.isDefault = defaultEdgeRuleChainIds.some( + id => ruleChain.id.id.includes(id)); + return ruleChain; + }); + ruleChains.data = data; + + deferred.resolve(ruleChains); + }, function fail() { + deferred.reject(); + } + ) + return deferred.promise; + } + function fetchRuleChains(pageLink, type) { - if (type === types.systemRuleChainType) { - return ruleChainService.getRuleChains(pageLink, null, type); - } else { - var deferred = $q.defer(); - ruleChainService.getRuleChains(pageLink, null, type).then( - // TODO: deaflynx - // function success(response) { - // ruleChainService.getDefaultEdgeRuleChains().then( - // function success(defaultEdgeRuleChains) { - // // response.data merge with defaultEdgeRuleChains - // deferred.resolve(data); - // }, - // function fail() { - // deferred.reject(); - // } - // ); - // }, - function fail() { + return ruleChainService.getRuleChains(pageLink, null, type); + } + + function fetchEdgeRuleChains(edgeId, pageLink) { + var deferred = $q.defer(); + ruleChainService.getRuleChains(pageLink, null, types.edgeRuleChainType).then( + function success(ruleChains) { + getDefaultEdges(ruleChains).then( + function success(response) { + deferred.resolve(response); + }, function fail() { + deferred.reject(); + } + ); + // deferred.resolve(response); + }, function fail() { deferred.reject(); - }); - return deferred.promise; - } + } + ); + return deferred.promise; } function saveRuleChain(ruleChain) { @@ -389,6 +434,14 @@ export default function RuleChainsController(ruleChainService, userService, edge } } + function isDefaultEdgeRuleChain(ruleChain) { + return angular.isDefined(ruleChain) && !ruleChain.root && defaultEdgeRuleChainIds.includes(ruleChain.id.id) === true; + } + + function isNonDefaultEdgeRuleChain(ruleChain) { + return angular.isDefined(ruleChain) && !ruleChain.root && defaultEdgeRuleChainIds.includes(ruleChain.id.id) === false; + } + function exportRuleChain($event, ruleChain) { $event.stopPropagation(); importExport.exportRuleChain(ruleChain.id.id); @@ -421,6 +474,42 @@ export default function RuleChainsController(ruleChainService, userService, edge }); } + function setDefaultEdgeRuleChain($event, ruleChain) { + $event.stopPropagation(); + var confirm = $mdDialog.confirm() + .targetEvent($event) + .title($translate.instant('rulechain.set-default-edge-title', {ruleChainName: ruleChain.name})) + .htmlContent($translate.instant('rulechain.set-default-edge-text')) + .ariaLabel($translate.instant('rulechain.set-default-edge')) + .cancel($translate.instant('action.no')) + .ok($translate.instant('action.yes')); + $mdDialog.show(confirm).then(function () { + ruleChainService.addDefaultEdgeRuleChain(ruleChain.id.id).then( + () => { + vm.grid.refreshList(); + } + ); + }); + } + + function removeDefaultEdgeRuleChain($event, ruleChain) { + $event.stopPropagation(); + var confirm = $mdDialog.confirm() + .targetEvent($event) + .title($translate.instant('rulechain.remove-default-edge-title', {ruleChainName: ruleChain.name})) + .htmlContent($translate.instant('rulechain.remove-default-edge-text')) + .ariaLabel($translate.instant('rulechain.remove-default-edge')) + .cancel($translate.instant('action.no')) + .ok($translate.instant('action.yes')); + $mdDialog.show(confirm).then(function () { + ruleChainService.removeDefaultEdgeRuleChain(ruleChain.id.id).then( + () => { + vm.grid.refreshList(); + } + ); + }); + } + function setDefaultRootEdgeRuleChain($event, ruleChain) { $event.stopPropagation(); var confirm = $mdDialog.confirm() From cc7bd63b62de36886955ebf2aee52dd56de424a1 Mon Sep 17 00:00:00 2001 From: deaflynx Date: Sat, 30 May 2020 18:39:14 +0300 Subject: [PATCH 19/52] Fixed edge dashboards breadcrumb --- ui/src/app/dashboard/dashboard.routes.js | 24 ++++++++++++++++++- ui/src/app/dashboard/dashboards.controller.js | 5 ++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/ui/src/app/dashboard/dashboard.routes.js b/ui/src/app/dashboard/dashboard.routes.js index 2ddfd89dc2..e78d211b03 100644 --- a/ui/src/app/dashboard/dashboard.routes.js +++ b/ui/src/app/dashboard/dashboard.routes.js @@ -142,7 +142,29 @@ export default function DashboardRoutes($stateProvider) { pageTitle: 'edge.dashboards' }, ncyBreadcrumb: { - label: '{"icon": "dashboard", "label": "{{ vm.edgeDashboardsTitle }}", "translate": "false"}' + label: '{"icon": "dashboard", "label": "edge.dashboards"}' + } + }) + .state('home.edges.dashboards.dashboard', { + url: '/:dashboardId?state', + reloadOnSearch: false, + module: 'private', + auth: ['TENANT_ADMIN', 'CUSTOMER_USER'], + views: { + "content@home": { + templateUrl: dashboardTemplate, + controller: 'DashboardController', + controllerAs: 'vm' + } + }, + data: { + widgetEditMode: false, + searchEnabled: false, + pageTitle: 'dashboard.dashboard', + dashboardsType: 'edge', + }, + ncyBreadcrumb: { + label: '{"icon": "dashboard", "label": "{{ vm.dashboard.title }}", "translate": "false"}' } }) } diff --git a/ui/src/app/dashboard/dashboards.controller.js b/ui/src/app/dashboard/dashboards.controller.js index 51eed98d4c..6d61029b84 100644 --- a/ui/src/app/dashboard/dashboards.controller.js +++ b/ui/src/app/dashboard/dashboards.controller.js @@ -674,6 +674,11 @@ export function DashboardsController(userService, dashboardService, customerServ customerId: customerId, dashboardId: dashboard.id.id }); + } else if (vm.dashboardsScope === 'edge') { + $state.go('home.edges.dashboards.dashboard', { + edgeId: edgeId, + dashboardId: dashboard.id.id + }); } else { $state.go('home.dashboards.dashboard', {dashboardId: dashboard.id.id}); } From 8b4c7cea1f178bb0656a9e10948a56e3b9238068 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Mon, 1 Jun 2020 12:54:23 +0300 Subject: [PATCH 20/52] Fixed remove default edge. Fixed edge tests --- .../server/dao/rule/BaseRuleChainService.java | 14 ++- .../dao/service/BaseDashboardServiceTest.java | 2 + .../dao/service/EdgeServiceImplTest.java | 104 ++++++------------ 3 files changed, 42 insertions(+), 78 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 a86d10326e..05c455cf25 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 @@ -482,12 +482,14 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC public boolean setDefaultRootEdgeRuleChain(TenantId tenantId, RuleChainId ruleChainId) { RuleChain ruleChain = ruleChainDao.findById(tenantId, ruleChainId.getId()); RuleChain previousDefaultRootEdgeRuleChain = getDefaultRootEdgeRuleChain(ruleChain.getTenantId()); - if (!previousDefaultRootEdgeRuleChain.getId().equals(ruleChain.getId())) { + if (previousDefaultRootEdgeRuleChain == null || !previousDefaultRootEdgeRuleChain.getId().equals(ruleChain.getId())) { try { - deleteRelation(tenantId, new EntityRelation(previousDefaultRootEdgeRuleChain.getTenantId(), previousDefaultRootEdgeRuleChain.getId(), - EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN)); - previousDefaultRootEdgeRuleChain.setRoot(false); - ruleChainDao.save(tenantId, previousDefaultRootEdgeRuleChain); + if (previousDefaultRootEdgeRuleChain != null) { + deleteRelation(tenantId, new EntityRelation(previousDefaultRootEdgeRuleChain.getTenantId(), previousDefaultRootEdgeRuleChain.getId(), + EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN)); + previousDefaultRootEdgeRuleChain.setRoot(false); + ruleChainDao.save(tenantId, previousDefaultRootEdgeRuleChain); + } createRelation(tenantId, new EntityRelation(ruleChain.getTenantId(), ruleChain.getId(), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN)); ruleChain.setRoot(true); @@ -517,7 +519,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC public boolean removeDefaultEdgeRuleChain(TenantId tenantId, RuleChainId ruleChainId) { try { deleteRelation(tenantId, new EntityRelation(tenantId, ruleChainId, - EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN)); + EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE)); return true; } catch (ExecutionException | InterruptedException e) { log.warn("Failed to remove default edge rule chain, ruleChainId: [{}]", ruleChainId, e); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDashboardServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDashboardServiceTest.java index d1e022139e..0854ceaae2 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDashboardServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDashboardServiceTest.java @@ -356,6 +356,8 @@ public abstract class BaseDashboardServiceTest extends AbstractServiceTest { edge.setTenantId(tenant.getId()); edge.setName("Test different edge"); edge.setType("default"); + edge.setSecret(RandomStringUtils.randomAlphanumeric(20)); + edge.setRoutingKey(RandomStringUtils.randomAlphanumeric(20)); edge = edgeService.saveEdge(edge); try { dashboardService.assignDashboardToEdge(tenantId, dashboard.getId(), edge.getId()); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/EdgeServiceImplTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/EdgeServiceImplTest.java index efa7db33d7..7dbedce770 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/EdgeServiceImplTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/EdgeServiceImplTest.java @@ -59,10 +59,7 @@ public abstract class EdgeServiceImplTest extends AbstractServiceTest { @Test public void testSaveEdge() { - Edge edge = new Edge(); - edge.setTenantId(tenantId); - edge.setName("My edge"); - edge.setType("default"); + Edge edge = constructEdge("My edge", "default"); Edge savedEdge = edgeService.saveEdge(edge); Assert.assertNotNull(savedEdge); @@ -109,10 +106,7 @@ public abstract class EdgeServiceImplTest extends AbstractServiceTest { @Test(expected = DataValidationException.class) public void testAssignEdgeToNonExistentCustomer() { - Edge edge = new Edge(); - edge.setName("My edge"); - edge.setType("default"); - edge.setTenantId(tenantId); + Edge edge = constructEdge("My edge", "default"); edge = edgeService.saveEdge(edge); try { edgeService.assignEdgeToCustomer(tenantId, edge.getId(), new CustomerId(UUIDs.timeBased())); @@ -123,10 +117,7 @@ public abstract class EdgeServiceImplTest extends AbstractServiceTest { @Test(expected = DataValidationException.class) public void testAssignEdgeToCustomerFromDifferentTenant() { - Edge edge = new Edge(); - edge.setName("My edge"); - edge.setType("default"); - edge.setTenantId(tenantId); + Edge edge = constructEdge("My edge", "default"); edge = edgeService.saveEdge(edge); Tenant tenant = new Tenant(); tenant.setTitle("Test different tenant"); @@ -145,10 +136,7 @@ public abstract class EdgeServiceImplTest extends AbstractServiceTest { @Test public void testFindEdgeById() { - Edge edge = new Edge(); - edge.setTenantId(tenantId); - edge.setName("My edge"); - edge.setType("default"); + Edge edge = constructEdge("My edge", "default"); Edge savedEdge = edgeService.saveEdge(edge); Edge foundEdge = edgeService.findEdgeById(tenantId, savedEdge.getId()); Assert.assertNotNull(foundEdge); @@ -161,24 +149,15 @@ public abstract class EdgeServiceImplTest extends AbstractServiceTest { List edges = new ArrayList<>(); try { for (int i = 0; i < 3; i++) { - Edge edge = new Edge(); - edge.setTenantId(tenantId); - edge.setName("My edge B" + i); - edge.setType("typeB"); + Edge edge = constructEdge("My edge B" + i, "typeB"); edges.add(edgeService.saveEdge(edge)); } for (int i = 0; i < 7; i++) { - Edge edge = new Edge(); - edge.setTenantId(tenantId); - edge.setName("My edge C" + i); - edge.setType("typeC"); + Edge edge = constructEdge("My edge C" + i, "typeC"); edges.add(edgeService.saveEdge(edge)); } for (int i = 0; i < 9; i++) { - Edge edge = new Edge(); - edge.setTenantId(tenantId); - edge.setName("My edge A" + i); - edge.setType("typeA"); + Edge edge = constructEdge("My edge A" + i, "typeA"); edges.add(edgeService.saveEdge(edge)); } List edgeTypes = edgeService.findEdgeTypesByTenantId(tenantId).get(); @@ -196,10 +175,7 @@ public abstract class EdgeServiceImplTest extends AbstractServiceTest { @Test public void testDeleteEdge() { - Edge edge = new Edge(); - edge.setTenantId(tenantId); - edge.setName("My edge"); - edge.setType("default"); + Edge edge = constructEdge("My edge", "default"); Edge savedEdge = edgeService.saveEdge(edge); Edge foundEdge = edgeService.findEdgeById(tenantId, savedEdge.getId()); Assert.assertNotNull(foundEdge); @@ -218,10 +194,7 @@ public abstract class EdgeServiceImplTest extends AbstractServiceTest { List edges = new ArrayList<>(); for (int i = 0; i < 178; i++) { - Edge edge = new Edge(); - edge.setTenantId(tenantId); - edge.setName("Edge" + i); - edge.setType("default"); + Edge edge = constructEdge(tenantId, "Edge " + i, "default"); edges.add(edgeService.saveEdge(edge)); } @@ -256,25 +229,19 @@ public abstract class EdgeServiceImplTest extends AbstractServiceTest { String title1 = "Edge title 1"; List edgesTitle1 = new ArrayList<>(); for (int i = 0; i < 143; i++) { - Edge edge = new Edge(); - edge.setTenantId(tenantId); String suffix = RandomStringUtils.randomAlphanumeric(15); String name = title1 + suffix; name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); - edge.setName(name); - edge.setType("default"); + Edge edge = constructEdge(name, "default"); edgesTitle1.add(edgeService.saveEdge(edge)); } String title2 = "Edge title 2"; List edgesTitle2 = new ArrayList<>(); for (int i = 0; i < 175; i++) { - Edge edge = new Edge(); - edge.setTenantId(tenantId); String suffix = RandomStringUtils.randomAlphanumeric(15); String name = title2 + suffix; name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); - edge.setName(name); - edge.setType("default"); + Edge edge = constructEdge(name, "default"); edgesTitle2.add(edgeService.saveEdge(edge)); } @@ -334,26 +301,20 @@ public abstract class EdgeServiceImplTest extends AbstractServiceTest { String type1 = "typeA"; List edgesType1 = new ArrayList<>(); for (int i = 0; i < 143; i++) { - Edge edge = new Edge(); - edge.setTenantId(tenantId); String suffix = RandomStringUtils.randomAlphanumeric(15); String name = title1 + suffix; name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); - edge.setName(name); - edge.setType(type1); + Edge edge = constructEdge(name, type1); edgesType1.add(edgeService.saveEdge(edge)); } String title2 = "Edge title 2"; String type2 = "typeB"; List edgesType2 = new ArrayList<>(); for (int i = 0; i < 175; i++) { - Edge edge = new Edge(); - edge.setTenantId(tenantId); String suffix = RandomStringUtils.randomAlphanumeric(15); String name = title2 + suffix; name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); - edge.setName(name); - edge.setType(type2); + Edge edge = constructEdge(name, type2); edgesType2.add(edgeService.saveEdge(edge)); } @@ -423,10 +384,7 @@ public abstract class EdgeServiceImplTest extends AbstractServiceTest { List edges = new ArrayList<>(); for (int i = 0; i < 278; i++) { - Edge edge = new Edge(); - edge.setTenantId(tenantId); - edge.setName("Edge" + i); - edge.setType("default"); + Edge edge = constructEdge(tenantId, "Edge" + i, "default"); edge = edgeService.saveEdge(edge); edges.add(edgeService.assignEdgeToCustomer(tenantId, edge.getId(), customerId)); } @@ -469,26 +427,20 @@ public abstract class EdgeServiceImplTest extends AbstractServiceTest { String title1 = "Edge title 1"; List edgesTitle1 = new ArrayList<>(); for (int i = 0; i < 175; i++) { - Edge edge = new Edge(); - edge.setTenantId(tenantId); String suffix = RandomStringUtils.randomAlphanumeric(15); String name = title1 + suffix; name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); - edge.setName(name); - edge.setType("default"); + Edge edge = constructEdge(name, "default"); edge = edgeService.saveEdge(edge); edgesTitle1.add(edgeService.assignEdgeToCustomer(tenantId, edge.getId(), customerId)); } String title2 = "Edge title 2"; List edgesTitle2 = new ArrayList<>(); for (int i = 0; i < 143; i++) { - Edge edge = new Edge(); - edge.setTenantId(tenantId); String suffix = RandomStringUtils.randomAlphanumeric(15); String name = title2 + suffix; name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); - edge.setName(name); - edge.setType("default"); + Edge edge = constructEdge(name, "default"); edge = edgeService.saveEdge(edge); edgesTitle2.add(edgeService.assignEdgeToCustomer(tenantId, edge.getId(), customerId)); } @@ -557,13 +509,10 @@ public abstract class EdgeServiceImplTest extends AbstractServiceTest { String type1 = "typeC"; List edgesType1 = new ArrayList<>(); for (int i = 0; i < 175; i++) { - Edge edge = new Edge(); - edge.setTenantId(tenantId); String suffix = RandomStringUtils.randomAlphanumeric(15); String name = title1 + suffix; name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); - edge.setName(name); - edge.setType(type1); + Edge edge = constructEdge(name, type1); edge = edgeService.saveEdge(edge); edgesType1.add(edgeService.assignEdgeToCustomer(tenantId, edge.getId(), customerId)); } @@ -571,13 +520,10 @@ public abstract class EdgeServiceImplTest extends AbstractServiceTest { String type2 = "typeD"; List edgesType2 = new ArrayList<>(); for (int i = 0; i < 143; i++) { - Edge edge = new Edge(); - edge.setTenantId(tenantId); String suffix = RandomStringUtils.randomAlphanumeric(15); String name = title2 + suffix; name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); - edge.setName(name); - edge.setType(type2); + Edge edge = constructEdge(name, type2); edge = edgeService.saveEdge(edge); edgesType2.add(edgeService.assignEdgeToCustomer(tenantId, edge.getId(), customerId)); } @@ -633,4 +579,18 @@ public abstract class EdgeServiceImplTest extends AbstractServiceTest { customerService.deleteCustomer(tenantId, customerId); } + private Edge constructEdge(String name, String type) { + return constructEdge(tenantId, name, type); + } + + private Edge constructEdge(TenantId tenantId, String name, String type) { + Edge edge = new Edge(); + edge.setTenantId(tenantId); + edge.setName(name); + edge.setType(type); + edge.setSecret(RandomStringUtils.randomAlphanumeric(20)); + edge.setRoutingKey(RandomStringUtils.randomAlphanumeric(20)); + return edge; + } + } From 0f24bd291228aadaa07d5230a99710f2074f2eb0 Mon Sep 17 00:00:00 2001 From: deaflynx Date: Mon, 1 Jun 2020 14:30:44 +0300 Subject: [PATCH 21/52] Edge code cleaning. Not stable Default edges --- ui/src/app/asset/asset.routes.js | 23 ---- ui/src/app/dashboard/dashboard.routes.js | 43 ------- ui/src/app/device/device.controller.js | 14 --- ui/src/app/device/device.routes.js | 23 ---- ui/src/app/edge/edge.controller.js | 2 - ui/src/app/edge/edge.routes.js | 110 ++++++++++++++++-- .../app/entity-view/entity-view.controller.js | 34 ++++++ ui/src/app/entity-view/entity-view.routes.js | 24 ---- ui/src/app/rulechain/rulechain-card.scss | 25 ---- ui/src/app/rulechain/rulechain-card.tpl.html | 9 +- ui/src/app/rulechain/rulechains.controller.js | 6 +- 11 files changed, 141 insertions(+), 172 deletions(-) delete mode 100644 ui/src/app/rulechain/rulechain-card.scss diff --git a/ui/src/app/asset/asset.routes.js b/ui/src/app/asset/asset.routes.js index 79870398f7..cfc4e77315 100644 --- a/ui/src/app/asset/asset.routes.js +++ b/ui/src/app/asset/asset.routes.js @@ -67,29 +67,6 @@ export default function AssetRoutes($stateProvider, types) { ncyBreadcrumb: { label: '{"icon": "domain", "label": "{{ vm.customerAssetsTitle }}", "translate": "false"}' } - }) - .state('home.edges.assets', { - url: '/:edgeId/assets', - params: {'topIndex': 0}, - module: 'private', - auth: ['TENANT_ADMIN'], - views: { - "content@home": { - templateUrl: assetsTemplate, - controllerAs: 'vm', - controller: 'AssetController' - } - }, - data: { - assetsType: 'edge', - searchEnabled: true, - searchByEntitySubtype: true, - searchEntityType: types.entityType.asset, - pageTitle: 'edge.assets' - }, - ncyBreadcrumb: { - label: '{"icon": "domain", "label": "edge.assets"}' - } }); } diff --git a/ui/src/app/dashboard/dashboard.routes.js b/ui/src/app/dashboard/dashboard.routes.js index e78d211b03..43ec230c6d 100644 --- a/ui/src/app/dashboard/dashboard.routes.js +++ b/ui/src/app/dashboard/dashboard.routes.js @@ -124,47 +124,4 @@ export default function DashboardRoutes($stateProvider) { label: '{"icon": "dashboard", "label": "customer.dashboard"}' } }) - .state('home.edges.dashboards', { - url: '/:edgeId/dashboards', - params: {'topIndex': 0}, - module: 'private', - auth: ['TENANT_ADMIN'], - views: { - "content@home": { - templateUrl: dashboardsTemplate, - controllerAs: 'vm', - controller: 'DashboardsController' - } - }, - data: { - dashboardsType: 'edge', - searchEnabled: true, - pageTitle: 'edge.dashboards' - }, - ncyBreadcrumb: { - label: '{"icon": "dashboard", "label": "edge.dashboards"}' - } - }) - .state('home.edges.dashboards.dashboard', { - url: '/:dashboardId?state', - reloadOnSearch: false, - module: 'private', - auth: ['TENANT_ADMIN', 'CUSTOMER_USER'], - views: { - "content@home": { - templateUrl: dashboardTemplate, - controller: 'DashboardController', - controllerAs: 'vm' - } - }, - data: { - widgetEditMode: false, - searchEnabled: false, - pageTitle: 'dashboard.dashboard', - dashboardsType: 'edge', - }, - ncyBreadcrumb: { - label: '{"icon": "dashboard", "label": "{{ vm.dashboard.title }}", "translate": "false"}' - } - }) } diff --git a/ui/src/app/device/device.controller.js b/ui/src/app/device/device.controller.js index a0f9c1b8c0..93f8dad1c4 100644 --- a/ui/src/app/device/device.controller.js +++ b/ui/src/app/device/device.controller.js @@ -219,20 +219,6 @@ export function DeviceController($rootScope, userService, deviceService, custome } }); - deviceActionsList.push( - { - onAction: function ($event, item) { - unassignFromEdge($event, item, false); - }, - name: function() { return $translate.instant('action.unassign') }, - details: function() { return $translate.instant('device.unassign-from-edge') }, - icon: "portable_wifi_off", - isEnabled: function(device) { - return device && device.edgeId && device.edgeId.id !== types.id.nullUid; - } - } - ); - deviceActionsList.push( { onAction: function ($event, item) { diff --git a/ui/src/app/device/device.routes.js b/ui/src/app/device/device.routes.js index 1836aa1326..da7dbb89dc 100644 --- a/ui/src/app/device/device.routes.js +++ b/ui/src/app/device/device.routes.js @@ -67,29 +67,6 @@ export default function DeviceRoutes($stateProvider, types) { ncyBreadcrumb: { label: '{"icon": "devices_other", "label": "{{ vm.customerDevicesTitle }}", "translate": "false"}' } - }) - .state('home.edges.devices', { - url: '/:edgeId/devices', - params: {'topIndex': 0}, - module: 'private', - auth: ['TENANT_ADMIN'], - views: { - "content@home": { - templateUrl: devicesTemplate, - controllerAs: 'vm', - controller: 'DeviceController' - } - }, - data: { - devicesType: 'edge', - searchEnabled: true, - searchByEntitySubtype: true, - searchEntityType: types.entityType.device, - pageTitle: 'edge.devices' - }, - ncyBreadcrumb: { - label: '{"icon": "devices_other", "label": "edge.devices"}' - } }); } diff --git a/ui/src/app/edge/edge.controller.js b/ui/src/app/edge/edge.controller.js index 3afa5323ef..51269ea753 100644 --- a/ui/src/app/edge/edge.controller.js +++ b/ui/src/app/edge/edge.controller.js @@ -577,7 +577,6 @@ export function EdgeController($rootScope, userService, edgeService, customerSer }); } - function assignEdgesToCustomer($event, items) { var edgeIds = []; for (var id in items.selections) { @@ -667,7 +666,6 @@ export function EdgeController($rootScope, userService, edgeService, customerSer $state.go('home.edges.ruleChains', {edgeId: edge.id.id}); } - function openEdgeAssets($event, edge) { if ($event) { $event.stopPropagation(); diff --git a/ui/src/app/edge/edge.routes.js b/ui/src/app/edge/edge.routes.js index c7b7e45752..1afd319609 100644 --- a/ui/src/app/edge/edge.routes.js +++ b/ui/src/app/edge/edge.routes.js @@ -16,6 +16,11 @@ /* eslint-disable import/no-unresolved, import/default */ import edgesTemplate from './edges.tpl.html'; +import entityViewsTemplate from "../entity-view/entity-views.tpl.html"; +import devicesTemplate from "../device/devices.tpl.html"; +import assetsTemplate from "../asset/assets.tpl.html"; +import dashboardsTemplate from "../dashboard/dashboards.tpl.html"; +import dashboardTemplate from "../dashboard/dashboard.tpl.html"; /* eslint-enable import/no-unresolved, import/default */ @@ -45,27 +50,116 @@ export default function EdgeRoutes($stateProvider, types) { label: '{"icon": "transform", "label": "edge.edges"}' } }) - .state('home.customers.edges', { - url: '/:customerId/edges', + .state('home.edges.entityViews', { + url: '/:edgeId/entityViews', params: {'topIndex': 0}, module: 'private', auth: ['TENANT_ADMIN'], views: { "content@home": { - templateUrl: edgesTemplate, + templateUrl: entityViewsTemplate, controllerAs: 'vm', - controller: 'EdgeController' + controller: 'EntityViewController' } }, data: { - edgesType: 'customer', + entityViewsType: 'edge', searchEnabled: true, searchByEntitySubtype: true, - searchEntityType: types.entityType.edge, - pageTitle: 'customer.edges' + searchEntityType: types.entityType.entityView, + pageTitle: 'edge.entity-views' + }, + ncyBreadcrumb: { + label: '{"icon": "view_quilt", "label": "edge.entity-views"}' + } + }) + .state('home.edges.devices', { + url: '/:edgeId/devices', + params: {'topIndex': 0}, + module: 'private', + auth: ['TENANT_ADMIN'], + views: { + "content@home": { + templateUrl: devicesTemplate, + controllerAs: 'vm', + controller: 'DeviceController' + } + }, + data: { + devicesType: 'edge', + searchEnabled: true, + searchByEntitySubtype: true, + searchEntityType: types.entityType.device, + pageTitle: 'edge.devices' + }, + ncyBreadcrumb: { + label: '{"icon": "devices_other", "label": "edge.devices"}' + } + }) + .state('home.edges.assets', { + url: '/:edgeId/assets', + params: {'topIndex': 0}, + module: 'private', + auth: ['TENANT_ADMIN'], + views: { + "content@home": { + templateUrl: assetsTemplate, + controllerAs: 'vm', + controller: 'AssetController' + } + }, + data: { + assetsType: 'edge', + searchEnabled: true, + searchByEntitySubtype: true, + searchEntityType: types.entityType.asset, + pageTitle: 'edge.assets' + }, + ncyBreadcrumb: { + label: '{"icon": "domain", "label": "edge.assets"}' + } + }) + .state('home.edges.dashboards', { + url: '/:edgeId/dashboards', + params: {'topIndex': 0}, + module: 'private', + auth: ['TENANT_ADMIN'], + views: { + "content@home": { + templateUrl: dashboardsTemplate, + controllerAs: 'vm', + controller: 'DashboardsController' + } + }, + data: { + dashboardsType: 'edge', + searchEnabled: true, + pageTitle: 'edge.dashboards' + }, + ncyBreadcrumb: { + label: '{"icon": "dashboard", "label": "edge.dashboards"}' + } + }) + .state('home.edges.dashboards.dashboard', { + url: '/:dashboardId?state', + reloadOnSearch: false, + module: 'private', + auth: ['TENANT_ADMIN', 'CUSTOMER_USER'], + views: { + "content@home": { + templateUrl: dashboardTemplate, + controller: 'DashboardController', + controllerAs: 'vm' + } + }, + data: { + widgetEditMode: false, + searchEnabled: false, + pageTitle: 'dashboard.dashboard', + dashboardsType: 'edge', }, ncyBreadcrumb: { - label: '{"icon": "transform", "label": "{{ vm.customerEdgesTitle }}", "translate": "false"}' + label: '{"icon": "dashboard", "label": "{{ vm.dashboard.title }}", "translate": "false"}' } }); } diff --git a/ui/src/app/entity-view/entity-view.controller.js b/ui/src/app/entity-view/entity-view.controller.js index 4420a3b82d..9c5e7907f5 100644 --- a/ui/src/app/entity-view/entity-view.controller.js +++ b/ui/src/app/entity-view/entity-view.controller.js @@ -294,6 +294,19 @@ export function EntityViewController($rootScope, userService, entityViewService, return {"edgeId": edgeId, "topIndex": vm.topIndex}; }; + entityViewActionsList.push({ + onAction: function ($event, item) { + unassignFromEdge($event, item, false); + }, + name: function() { return $translate.instant('action.unassign') }, + details: function() { return $translate.instant('entity-view.unassign-from-edge') }, + icon: "assignment_return", + isEnabled: function(entityView) { + return entityView && entityView.edgeId && entityView.edgeId.id !== types.id.nullUid; + } + } + ); + entityViewGroupActionsList.push( { onAction: function ($event, items) { @@ -583,4 +596,25 @@ export function EntityViewController($rootScope, userService, entityViewService, }); }); } + + function unassignFromEdge($event, entityView) { + if ($event) { + $event.stopPropagation(); + } + var title = $translate.instant('entity-view.unassign-entity-view-from-edge-title', {entityViewName: entityView.name}); + var content = $translate.instant('entity-view.unassign-entity-view-from-edge-text'); + var label = $translate.instant('entity-view.unassign-entity-view'); + var confirm = $mdDialog.confirm() + .targetEvent($event) + .title(title) + .htmlContent(content) + .ariaLabel(label) + .cancel($translate.instant('action.no')) + .ok($translate.instant('action.yes')); + $mdDialog.show(confirm).then(function () { + entityViewService.unassignEntityViewFromEdge(entityView.id.id).then(function success() { + vm.grid.refreshList(); + }); + }); + } } diff --git a/ui/src/app/entity-view/entity-view.routes.js b/ui/src/app/entity-view/entity-view.routes.js index ad845e32af..4da8c80500 100644 --- a/ui/src/app/entity-view/entity-view.routes.js +++ b/ui/src/app/entity-view/entity-view.routes.js @@ -67,29 +67,5 @@ export default function EntityViewRoutes($stateProvider, types) { ncyBreadcrumb: { label: '{"icon": "view_quilt", "label": "{{ vm.customerEntityViewsTitle }}", "translate": "false"}' } - }) - .state('home.edges.entityViews', { - url: '/:edgeId/entityViews', - params: {'topIndex': 0}, - module: 'private', - auth: ['TENANT_ADMIN'], - views: { - "content@home": { - templateUrl: entityViewsTemplate, - controllerAs: 'vm', - controller: 'EntityViewController' - } - }, - data: { - entityViewsType: 'edge', - searchEnabled: true, - searchByEntitySubtype: true, - searchEntityType: types.entityType.entityView, - pageTitle: 'edge.entity-views' - }, - ncyBreadcrumb: { - label: '{"icon": "view_quilt", "label": "edge.entity-views"}' - } }); - } diff --git a/ui/src/app/rulechain/rulechain-card.scss b/ui/src/app/rulechain/rulechain-card.scss deleted file mode 100644 index 86e86680bd..0000000000 --- a/ui/src/app/rulechain/rulechain-card.scss +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright © 2016-2020 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-rule-chain-assigned-edges { - display: block; - display: -webkit-box; /* stylelint-disable-line value-no-vendor-prefix */ - height: 34px; - margin-bottom: 4px; - overflow: hidden; - text-overflow: ellipsis; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; /* stylelint-disable-line property-no-vendor-prefix */ -} diff --git a/ui/src/app/rulechain/rulechain-card.tpl.html b/ui/src/app/rulechain/rulechain-card.tpl.html index 4e28413327..bab05c0a41 100644 --- a/ui/src/app/rulechain/rulechain-card.tpl.html +++ b/ui/src/app/rulechain/rulechain-card.tpl.html @@ -17,9 +17,6 @@ -->
rulechain.root -
-
rulechain.default -
+ (vm.parentCtl.ruleChainsScope === 'edges' && vm.parentCtl.isRootRuleChain(item))" translate>rulechain.root + +
rulechain.default
diff --git a/ui/src/app/rulechain/rulechains.controller.js b/ui/src/app/rulechain/rulechains.controller.js index eac031011a..58da44ac47 100644 --- a/ui/src/app/rulechain/rulechains.controller.js +++ b/ui/src/app/rulechain/rulechains.controller.js @@ -21,8 +21,6 @@ import addRuleChainsToEdgeTemplate from "./add-rulechains-to-edge.tpl.html"; /* eslint-enable import/no-unresolved, import/default */ -import './rulechain-card.scss'; - /*@ngInject*/ export default function RuleChainsController(ruleChainService, userService, edgeService, importExport, $state, $stateParams, $filter, $translate, $mdDialog, $document, $q, types) { @@ -342,6 +340,7 @@ export default function RuleChainsController(ruleChainService, userService, edge var deferred = $q.defer(); ruleChainService.getDefaultEdgeRuleChains(null).then( function success(response) { + defaultEdgeRuleChainIds = []; response.map(function (ruleChain) { defaultEdgeRuleChainIds.push(ruleChain.id.id) }); @@ -368,7 +367,7 @@ export default function RuleChainsController(ruleChainService, userService, edge function fetchEdgeRuleChains(edgeId, pageLink) { var deferred = $q.defer(); - ruleChainService.getRuleChains(pageLink, null, types.edgeRuleChainType).then( + ruleChainService.getEdgeRuleChains(edgeId, pageLink, null).then( function success(ruleChains) { getDefaultEdges(ruleChains).then( function success(response) { @@ -377,7 +376,6 @@ export default function RuleChainsController(ruleChainService, userService, edge deferred.reject(); } ); - // deferred.resolve(response); }, function fail() { deferred.reject(); } From 0af0422a92c7eff8f0c7f2f698fa860d06a4a639 Mon Sep 17 00:00:00 2001 From: deaflynx Date: Mon, 1 Jun 2020 15:10:24 +0300 Subject: [PATCH 22/52] Default edge rule chains code optimization --- ui/src/app/rulechain/rulechains.controller.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/ui/src/app/rulechain/rulechains.controller.js b/ui/src/app/rulechain/rulechains.controller.js index 58da44ac47..0a1e16033e 100644 --- a/ui/src/app/rulechain/rulechains.controller.js +++ b/ui/src/app/rulechain/rulechains.controller.js @@ -93,7 +93,6 @@ export default function RuleChainsController(ruleChainService, userService, edge vm.isRootRuleChain = isRootRuleChain; vm.isNonRootRuleChain = isNonRootRuleChain; vm.isDefaultEdgeRuleChain = isDefaultEdgeRuleChain; - var defaultEdgeRuleChainIds = []; vm.exportRuleChain = exportRuleChain; vm.setRootRuleChain = setRootRuleChain; @@ -340,15 +339,13 @@ export default function RuleChainsController(ruleChainService, userService, edge var deferred = $q.defer(); ruleChainService.getDefaultEdgeRuleChains(null).then( function success(response) { - defaultEdgeRuleChainIds = []; + let defaultEdgeRuleChainIds = []; response.map(function (ruleChain) { defaultEdgeRuleChainIds.push(ruleChain.id.id) }); - const data = ruleChains.data; data.map(function (ruleChain) { - ruleChain.isDefault = defaultEdgeRuleChainIds.some( - id => ruleChain.id.id.includes(id)); + ruleChain.isDefault = defaultEdgeRuleChainIds.some(id => ruleChain.id.id.includes(id)); return ruleChain; }); ruleChains.data = data; @@ -433,11 +430,11 @@ export default function RuleChainsController(ruleChainService, userService, edge } function isDefaultEdgeRuleChain(ruleChain) { - return angular.isDefined(ruleChain) && !ruleChain.root && defaultEdgeRuleChainIds.includes(ruleChain.id.id) === true; + return angular.isDefined(ruleChain) && ruleChain.isDefault; } function isNonDefaultEdgeRuleChain(ruleChain) { - return angular.isDefined(ruleChain) && !ruleChain.root && defaultEdgeRuleChainIds.includes(ruleChain.id.id) === false; + return angular.isDefined(ruleChain) && !ruleChain.isDefault; } function exportRuleChain($event, ruleChain) { From 9b16d93c6e08436b887b300270252d71f8a743e7 Mon Sep 17 00:00:00 2001 From: deaflynx Date: Mon, 1 Jun 2020 18:11:39 +0300 Subject: [PATCH 23/52] Fixed default edge rule chain scope --- ui/src/app/rulechain/rulechain-card.tpl.html | 2 +- ui/src/app/rulechain/rulechains.controller.js | 84 +++++++++---------- 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/ui/src/app/rulechain/rulechain-card.tpl.html b/ui/src/app/rulechain/rulechain-card.tpl.html index bab05c0a41..521d49d48c 100644 --- a/ui/src/app/rulechain/rulechain-card.tpl.html +++ b/ui/src/app/rulechain/rulechain-card.tpl.html @@ -19,4 +19,4 @@ (vm.parentCtl.ruleChainsScope === 'edge' && vm.parentCtl.isRootRuleChain(item)) || (vm.parentCtl.ruleChainsScope === 'edges' && vm.parentCtl.isRootRuleChain(item))" translate>rulechain.root -
rulechain.default
+
rulechain.default
diff --git a/ui/src/app/rulechain/rulechains.controller.js b/ui/src/app/rulechain/rulechains.controller.js index 0a1e16033e..0261af3301 100644 --- a/ui/src/app/rulechain/rulechains.controller.js +++ b/ui/src/app/rulechain/rulechains.controller.js @@ -181,6 +181,26 @@ export default function RuleChainsController(ruleChainService, userService, edge return deleteRuleChain(ruleChainId); }; + ruleChainActionsList.push({ + onAction: function ($event, item) { + setDefaultEdgeRuleChain($event, item); + }, + name: function() { return $translate.instant('rulechain.set-default-edge') }, + details: function() { return $translate.instant('rulechain.set-default-edge') }, + icon: "bookmark_outline", + isEnabled: isNonDefaultEdgeRuleChain + }); + + ruleChainActionsList.push({ + onAction: function ($event, item) { + removeDefaultEdgeRuleChain($event, item); + }, + name: function() { return $translate.instant('rulechain.remove-default-edge') }, + details: function() { return $translate.instant('rulechain.remove-default-edge') }, + icon: "bookmark", + isEnabled: isDefaultEdgeRuleChain + }); + ruleChainActionsList.push({ onAction: function ($event, item) { vm.grid.deleteItem($event, item); @@ -236,7 +256,7 @@ export default function RuleChainsController(ruleChainService, userService, edge } else if (vm.ruleChainsScope === 'edge') { fetchRuleChainsFunction = function (pageLink) { - return fetchEdgeRuleChains(edgeId, pageLink); + return ruleChainService.getEdgeRuleChains(edgeId, pageLink); }; deleteRuleChainFunction = function (ruleChainId) { return ruleChainService.unassignRuleChainFromEdge(edgeId, ruleChainId); @@ -252,26 +272,6 @@ export default function RuleChainsController(ruleChainService, userService, edge isEnabled: isNonRootRuleChain }); - ruleChainActionsList.push({ - onAction: function ($event, item) { - setDefaultEdgeRuleChain($event, item); - }, - name: function() { return $translate.instant('rulechain.set-default-edge') }, - details: function() { return $translate.instant('rulechain.set-default-edge') }, - icon: "bookmark_outline", - isEnabled: isNonDefaultEdgeRuleChain - }); - - ruleChainActionsList.push({ - onAction: function ($event, item) { - removeDefaultEdgeRuleChain($event, item); - }, - name: function() { return $translate.instant('rulechain.remove-default-edge') }, - details: function() { return $translate.instant('rulechain.remove-default-edge') }, - icon: "bookmark", - isEnabled: isDefaultEdgeRuleChain - }); - ruleChainActionsList.push( { onAction: function ($event, item) { @@ -359,25 +359,25 @@ export default function RuleChainsController(ruleChainService, userService, edge } function fetchRuleChains(pageLink, type) { - return ruleChainService.getRuleChains(pageLink, null, type); - } - - function fetchEdgeRuleChains(edgeId, pageLink) { - var deferred = $q.defer(); - ruleChainService.getEdgeRuleChains(edgeId, pageLink, null).then( - function success(ruleChains) { - getDefaultEdges(ruleChains).then( - function success(response) { - deferred.resolve(response); - }, function fail() { - deferred.reject(); - } - ); - }, function fail() { - deferred.reject(); - } - ); - return deferred.promise; + if (vm.ruleChainsScope === 'tenant') { + return ruleChainService.getRuleChains(pageLink, null, type); + } else if (vm.ruleChainsScope === 'edges') { + var deferred = $q.defer(); + ruleChainService.getRuleChains(pageLink, null, type).then( + function success(ruleChains) { + getDefaultEdges(ruleChains).then( + function success(response) { + deferred.resolve(response); + }, function fail() { + deferred.reject(); + } + ); + }, function fail() { + deferred.reject(); + } + ); + return deferred.promise; + } } function saveRuleChain(ruleChain) { @@ -430,11 +430,11 @@ export default function RuleChainsController(ruleChainService, userService, edge } function isDefaultEdgeRuleChain(ruleChain) { - return angular.isDefined(ruleChain) && ruleChain.isDefault; + return angular.isDefined(ruleChain) && !ruleChain.root && ruleChain.isDefault; } function isNonDefaultEdgeRuleChain(ruleChain) { - return angular.isDefined(ruleChain) && !ruleChain.isDefault; + return angular.isDefined(ruleChain) && !ruleChain.root && !ruleChain.isDefault; } function exportRuleChain($event, ruleChain) { From 8416c206ff1237b611d2773ab59342e8649a2150 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Mon, 1 Jun 2020 22:08:30 +0300 Subject: [PATCH 24/52] Fixed rule chain relation. Fixed default edge removal --- .../server/actors/tenant/TenantActor.java | 2 +- .../server/controller/EdgeController.java | 12 +++++++----- .../server/controller/RuleChainController.java | 18 ++++++++++++------ .../server/dao/edge/EdgeService.java | 2 ++ .../data/relation/RelationTypeGroup.java | 3 ++- .../server/dao/edge/CassandraEdgeDao.java | 4 ++-- .../server/dao/edge/EdgeServiceImpl.java | 14 ++++++++++++++ .../server/dao/rule/BaseRuleChainService.java | 4 ++-- .../server/dao/rule/CassandraRuleChainDao.java | 2 +- .../server/dao/sql/edge/JpaEdgeDao.java | 4 ++-- .../server/dao/sql/rule/JpaRuleChainDao.java | 2 +- 11 files changed, 46 insertions(+), 21 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 1b0e4c3837..900a3a88c3 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 @@ -206,7 +206,7 @@ public class TenantActor extends RuleChainManagerActor { if (msg.getEntityId().getEntityType() == EntityType.RULE_CHAIN) { RuleChain ruleChain = systemContext.getRuleChainService(). findRuleChainById(tenantId, new RuleChainId(msg.getEntityId().getId())); - if (ruleChain.getType().equals(RuleChainType.SYSTEM)) { + if (ruleChain != null && ruleChain.getType().equals(RuleChainType.SYSTEM)) { visit(ruleChain, target); } } diff --git a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java b/application/src/main/java/org/thingsboard/server/controller/EdgeController.java index bb9106719d..d263497527 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EdgeController.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.controller; +import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; @@ -92,15 +93,16 @@ public class EdgeController extends BaseController { accessControlService.checkPermission(getCurrentUser(), Resource.EDGE, operation, edge.getId(), edge); - Edge result = checkNotNull(edgeService.saveEdge(edge)); + Edge savedEdge = checkNotNull(edgeService.saveEdge(edge)); if (created) { - ruleChainService.assignRuleChainToEdge(tenantId, defaultRootEdgeRuleChain.getId(), result.getId()); - edgeService.setEdgeRootRuleChain(tenantId, result, defaultRootEdgeRuleChain.getId()); + ruleChainService.assignRuleChainToEdge(tenantId, defaultRootEdgeRuleChain.getId(), savedEdge.getId()); + edgeService.setEdgeRootRuleChain(tenantId, savedEdge, defaultRootEdgeRuleChain.getId()); + edgeService.assignDefaultRuleChainsToEdge(tenantId, savedEdge.getId()); } - logEntityAction(result.getId(), result, null, created ? ActionType.ADDED : ActionType.UPDATED, null); - return result; + logEntityAction(savedEdge.getId(), savedEdge, null, created ? ActionType.ADDED : ActionType.UPDATED, null); + return savedEdge; } catch (Exception e) { logEntityAction(emptyId(EntityType.EDGE), edge, null, edge.getId() == null ? ActionType.ADDED : ActionType.UPDATED, e); diff --git a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java index 3d6b1f3349..06e7256421 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java +++ b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java @@ -137,8 +137,10 @@ public class RuleChainController extends BaseController { RuleChain savedRuleChain = checkNotNull(ruleChainService.saveRuleChain(ruleChain)); - tbClusterService.onEntityStateChange(ruleChain.getTenantId(), savedRuleChain.getId(), - created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); + if (RuleChainType.SYSTEM.equals(savedRuleChain.getType())) { + tbClusterService.onEntityStateChange(ruleChain.getTenantId(), savedRuleChain.getId(), + created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); + } logEntityAction(savedRuleChain.getId(), savedRuleChain, null, @@ -210,7 +212,9 @@ public class RuleChainController extends BaseController { RuleChain ruleChain = checkRuleChain(ruleChainMetaData.getRuleChainId(), Operation.WRITE); RuleChainMetaData savedRuleChainMetaData = checkNotNull(ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData)); - tbClusterService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.UPDATED); + if (RuleChainType.SYSTEM.equals(ruleChain.getType())) { + tbClusterService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.UPDATED); + } logEntityAction(ruleChain.getId(), ruleChain, null, @@ -266,10 +270,12 @@ public class RuleChainController extends BaseController { referencingRuleChainIds.remove(ruleChain.getId()); - referencingRuleChainIds.forEach(referencingRuleChainId -> - tbClusterService.onEntityStateChange(ruleChain.getTenantId(), referencingRuleChainId, ComponentLifecycleEvent.UPDATED)); + if (RuleChainType.SYSTEM.equals(ruleChain.getType())) { + referencingRuleChainIds.forEach(referencingRuleChainId -> + tbClusterService.onEntityStateChange(ruleChain.getTenantId(), referencingRuleChainId, ComponentLifecycleEvent.UPDATED)); - tbClusterService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.DELETED); + tbClusterService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.DELETED); + } logEntityAction(ruleChainId, ruleChain, null, diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java index d3913cf691..48f02c28fb 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java @@ -82,6 +82,8 @@ public interface EdgeService { Edge setEdgeRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) throws IOException; + void assignDefaultRuleChainsToEdge(TenantId tenantId, EdgeId edgeId); + ListenableFuture> findEdgesByTenantIdAndRuleChainId(TenantId tenantId, RuleChainId ruleChainId, TimePageLink pageLink); ListenableFuture> findEdgesByTenantIdAndDashboardId(TenantId tenantId, DashboardId dashboardId, TimePageLink pageLink); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/relation/RelationTypeGroup.java b/common/data/src/main/java/org/thingsboard/server/common/data/relation/RelationTypeGroup.java index 611c4777a3..07315428e3 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/relation/RelationTypeGroup.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/relation/RelationTypeGroup.java @@ -22,6 +22,7 @@ public enum RelationTypeGroup { DASHBOARD, RULE_CHAIN, RULE_NODE, - EDGE + EDGE, + EDGE_DEFAULT_RULE_CHAIN } diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/CassandraEdgeDao.java b/dao/src/main/java/org/thingsboard/server/dao/edge/CassandraEdgeDao.java index 835917b153..75fabb33bf 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/CassandraEdgeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/CassandraEdgeDao.java @@ -116,7 +116,7 @@ public class CassandraEdgeDao extends CassandraAbstractSearchTextDao { List> edgeFutures = new ArrayList<>(input.size()); for (EntityRelation relation : input) { - edgeFutures.add(findByIdAsync(new TenantId(tenantId), relation.getTo().getId())); + edgeFutures.add(findByIdAsync(new TenantId(tenantId), relation.getFrom().getId())); } return Futures.successfulAsList(edgeFutures); }, MoreExecutors.directExecutor()); @@ -129,7 +129,7 @@ public class CassandraEdgeDao extends CassandraAbstractSearchTextDao { List> edgeFutures = new ArrayList<>(input.size()); for (EntityRelation relation : input) { - edgeFutures.add(findByIdAsync(new TenantId(tenantId), relation.getTo().getId())); + edgeFutures.add(findByIdAsync(new TenantId(tenantId), relation.getFrom().getId())); } return Futures.successfulAsList(edgeFutures); }, MoreExecutors.directExecutor()); diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java index 31cbe1c765..f974cabc62 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java @@ -665,6 +665,20 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic return savedEdge; } + @Override + public void assignDefaultRuleChainsToEdge(TenantId tenantId, EdgeId edgeId) { + log.trace("Executing assignDefaultRuleChainsToEdge, tenantId [{}], edgeId [{}]", tenantId, edgeId); + ListenableFuture> future = ruleChainService.findDefaultEdgeRuleChainsByTenantId(tenantId); + Futures.transform(future, ruleChains -> { + if (ruleChains != null && !ruleChains.isEmpty()) { + for (RuleChain ruleChain : ruleChains) { + ruleChainService.assignRuleChainToEdge(tenantId, ruleChain.getId(), edgeId); + } + } + return null; + }, MoreExecutors.directExecutor()); + } + @Override public ListenableFuture> findEdgesByTenantIdAndRuleChainId(TenantId tenantId, RuleChainId ruleChainId, TimePageLink pageLink) { log.trace("Executing findEdgesByTenantIdAndRuleChainId, tenantId [{}], ruleChainId [{}], pageLink [{}]", tenantId, ruleChainId, pageLink); 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 05c455cf25..a667092231 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 @@ -507,7 +507,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC public boolean addDefaultEdgeRuleChain(TenantId tenantId, RuleChainId ruleChainId) { try { createRelation(tenantId, new EntityRelation(tenantId, ruleChainId, - EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE)); + EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE_DEFAULT_RULE_CHAIN)); return true; } catch (ExecutionException | InterruptedException e) { log.warn("Failed to add default edge rule chain, ruleChainId: [{}]", ruleChainId, e); @@ -519,7 +519,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC public boolean removeDefaultEdgeRuleChain(TenantId tenantId, RuleChainId ruleChainId) { try { deleteRelation(tenantId, new EntityRelation(tenantId, ruleChainId, - EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE)); + EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE_DEFAULT_RULE_CHAIN)); return true; } catch (ExecutionException | InterruptedException e) { log.warn("Failed to remove default edge rule chain, ruleChainId: [{}]", ruleChainId, e); diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/CassandraRuleChainDao.java b/dao/src/main/java/org/thingsboard/server/dao/rule/CassandraRuleChainDao.java index 31eca488b3..f3916e8cb5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/CassandraRuleChainDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/CassandraRuleChainDao.java @@ -109,7 +109,7 @@ public class CassandraRuleChainDao extends CassandraAbstractSearchTextDao> findDefaultEdgeRuleChainsByTenantId(UUID tenantId) { log.debug("Try to find default edge rule chains by tenantId [{}]", tenantId); - ListenableFuture> relations = relationDao.findAllByFromAndType(new TenantId(tenantId), new TenantId(tenantId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE); + ListenableFuture> relations = relationDao.findAllByFromAndType(new TenantId(tenantId), new TenantId(tenantId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE_DEFAULT_RULE_CHAIN); return Futures.transformAsync(relations, input -> { List> ruleChainFutures = new ArrayList<>(input.size()); for (EntityRelation relation : input) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java index e21a24ee0d..d89e085355 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java @@ -154,7 +154,7 @@ public class JpaEdgeDao extends JpaAbstractSearchTextDao imple return Futures.transformAsync(relations, input -> { List> edgeFutures = new ArrayList<>(input.size()); for (EntityRelation relation : input) { - edgeFutures.add(findByIdAsync(new TenantId(tenantId), relation.getTo().getId())); + edgeFutures.add(findByIdAsync(new TenantId(tenantId), relation.getFrom().getId())); } return Futures.successfulAsList(edgeFutures); }, MoreExecutors.directExecutor()); @@ -167,7 +167,7 @@ public class JpaEdgeDao extends JpaAbstractSearchTextDao imple return Futures.transformAsync(relations, input -> { List> edgeFutures = new ArrayList<>(input.size()); for (EntityRelation relation : input) { - edgeFutures.add(findByIdAsync(new TenantId(tenantId), relation.getTo().getId())); + edgeFutures.add(findByIdAsync(new TenantId(tenantId), relation.getFrom().getId())); } return Futures.successfulAsList(edgeFutures); }, MoreExecutors.directExecutor()); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java index 8afbfabb62..b6f2ac2421 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java @@ -109,7 +109,7 @@ public class JpaRuleChainDao extends JpaAbstractSearchTextDao> findDefaultEdgeRuleChainsByTenantId(UUID tenantId) { log.debug("Try to find default edge rule chains by tenantId [{}]", tenantId); - ListenableFuture> relations = relationDao.findAllByFromAndType(new TenantId(tenantId), new TenantId(tenantId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE); + ListenableFuture> relations = relationDao.findAllByFromAndType(new TenantId(tenantId), new TenantId(tenantId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE_DEFAULT_RULE_CHAIN); return Futures.transformAsync(relations, input -> { List> ruleChainsFutures = new ArrayList<>(input.size()); for (EntityRelation relation : input) { From 134148c49f0cabfc269e66ce72c931405dd81e0d Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Tue, 2 Jun 2020 12:46:57 +0300 Subject: [PATCH 25/52] Removed edgeId. Code clean up and refactoring --- .../server/controller/AssetController.java | 46 +++++------ .../server/controller/DeviceController.java | 40 +++++----- .../controller/EntityViewController.java | 40 +++++----- .../service/edge/rpc/EdgeGrpcSession.java | 2 +- .../edge/rpc/init/DefaultInitEdgeService.java | 20 ++--- .../CassandraDatabaseUpgradeService.java | 12 --- .../install/SqlDatabaseUpgradeService.java | 9 --- .../server/dao/asset/AssetService.java | 8 +- .../server/dao/device/DeviceService.java | 11 ++- .../dao/entityview/EntityViewService.java | 10 +-- .../server/common/data/Device.java | 13 ---- .../server/common/data/EntityView.java | 2 - .../server/common/data/asset/Asset.java | 15 ---- .../server/dao/alarm/BaseAlarmService.java | 16 +--- .../server/dao/asset/AssetDao.java | 16 +--- .../server/dao/asset/BaseAssetService.java | 65 +++++++++++----- .../server/dao/asset/CassandraAssetDao.java | 44 +++++------ .../dao/dashboard/DashboardServiceImpl.java | 10 --- .../server/dao/device/CassandraDeviceDao.java | 42 +++++----- .../server/dao/device/DeviceDao.java | 14 +--- .../server/dao/device/DeviceServiceImpl.java | 64 ++++++++++----- .../server/dao/edge/EdgeServiceImpl.java | 78 ++++++++++--------- .../dao/entity/AbstractEntityService.java | 12 +++ .../entityview/CassandraEntityViewDao.java | 44 +++++------ .../server/dao/entityview/EntityViewDao.java | 23 ++---- .../dao/entityview/EntityViewServiceImpl.java | 75 +++++++++++------- .../server/dao/model/ModelConstants.java | 3 - .../server/dao/model/nosql/AssetEntity.java | 22 +----- .../server/dao/model/nosql/DeviceEntity.java | 29 +++---- .../dao/model/nosql/EntityViewEntity.java | 12 --- .../server/dao/model/sql/AssetEntity.java | 13 +--- .../server/dao/model/sql/DeviceEntity.java | 10 --- .../dao/model/sql/EntityViewEntity.java | 10 --- .../server/dao/rule/BaseRuleChainService.java | 13 +--- .../dao/rule/CassandraRuleChainDao.java | 2 +- .../server/dao/sql/asset/AssetRepository.java | 22 ------ .../server/dao/sql/asset/JpaAssetDao.java | 44 ++++++----- .../dao/sql/device/DeviceRepository.java | 22 ------ .../server/dao/sql/device/JpaDeviceDao.java | 45 +++++------ .../sql/entityview/EntityViewRepository.java | 22 ------ .../dao/sql/entityview/JpaEntityViewDao.java | 48 ++++++------ .../resources/sql/schema-entities-hsql.sql | 3 - .../main/resources/sql/schema-entities.sql | 3 - ui/src/app/api/asset.service.js | 24 +++--- ui/src/app/api/device.service.js | 25 +++--- ui/src/app/api/entity-view.service.js | 24 +++--- ui/src/app/asset/asset.controller.js | 12 +-- ui/src/app/device/device.controller.js | 12 +-- .../app/entity-view/entity-view.controller.js | 19 ++--- ui/src/app/locale/locale.constant-en_US.json | 17 +++- 50 files changed, 499 insertions(+), 688 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/AssetController.java b/application/src/main/java/org/thingsboard/server/controller/AssetController.java index d50c25af74..1a0d600d7b 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AssetController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AssetController.java @@ -40,6 +40,8 @@ import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.common.data.page.TimePageData; +import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.queue.util.TbCoreComponent; @@ -51,6 +53,8 @@ import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; +import static org.thingsboard.server.controller.EdgeController.EDGE_ID; + @RestController @TbCoreComponent @RequestMapping("/api") @@ -336,9 +340,9 @@ public class AssetController extends BaseController { @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/edge/{edgeId}/asset/{assetId}", method = RequestMethod.POST) @ResponseBody - public Asset assignAssetToEdge(@PathVariable("edgeId") String strEdgeId, + public Asset assignAssetToEdge(@PathVariable(EDGE_ID) String strEdgeId, @PathVariable(ASSET_ID) String strAssetId) throws ThingsboardException { - checkParameter("edgeId", strEdgeId); + checkParameter(EDGE_ID, strEdgeId); checkParameter(ASSET_ID, strAssetId); try { EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); @@ -365,20 +369,20 @@ public class AssetController extends BaseController { } @PreAuthorize("hasAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/edge/asset/{assetId}", method = RequestMethod.DELETE) + @RequestMapping(value = "/edge/{edgeId}/asset/{assetId}", method = RequestMethod.DELETE) @ResponseBody - public Asset unassignAssetFromEdge(@PathVariable(ASSET_ID) String strAssetId) throws ThingsboardException { + public Asset unassignAssetFromEdge(@PathVariable(EDGE_ID) String strEdgeId, + @PathVariable(ASSET_ID) String strAssetId) throws ThingsboardException { + checkParameter(EDGE_ID, strEdgeId); checkParameter(ASSET_ID, strAssetId); try { + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); + Edge edge = checkEdgeId(edgeId, Operation.READ); + AssetId assetId = new AssetId(toUUID(strAssetId)); Asset asset = checkAssetId(assetId, Operation.UNASSIGN_FROM_EDGE); - if (asset.getEdgeId() == null || asset.getEdgeId().getId().equals(ModelConstants.NULL_UUID)) { - throw new IncorrectParameterException("Asset isn't assigned to any edge!"); - } - Edge edge = checkEdgeId(asset.getEdgeId(), Operation.READ); - - Asset savedAsset = checkNotNull(assetService.unassignAssetFromEdge(getTenantId(), assetId)); + Asset savedAsset = checkNotNull(assetService.unassignAssetFromEdge(getTenantId(), assetId, edgeId)); logEntityAction(assetId, asset, asset.getCustomerId(), @@ -398,24 +402,20 @@ public class AssetController extends BaseController { @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @RequestMapping(value = "/edge/{edgeId}/assets", params = {"limit"}, method = RequestMethod.GET) @ResponseBody - public TextPageData getEdgeAssets( - @PathVariable("edgeId") String strEdgeId, + public TimePageData getEdgeAssets( + @PathVariable(EDGE_ID) String strEdgeId, @RequestParam int limit, - @RequestParam(required = false) String type, - @RequestParam(required = false) String textSearch, - @RequestParam(required = false) String idOffset, - @RequestParam(required = false) String textOffset) throws ThingsboardException { - checkParameter("edgeId", strEdgeId); + @RequestParam(required = false) Long startTime, + @RequestParam(required = false) Long endTime, + @RequestParam(required = false, defaultValue = "false") boolean ascOrder, + @RequestParam(required = false) String offset) throws ThingsboardException { + checkParameter(EDGE_ID, strEdgeId); try { TenantId tenantId = getCurrentUser().getTenantId(); EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); checkEdgeId(edgeId, Operation.READ); - TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); - if (type != null && type.trim().length()>0) { - return checkNotNull(assetService.findAssetsByTenantIdAndEdgeIdAndType(tenantId, edgeId, type, pageLink)); - } else { - return checkNotNull(assetService.findAssetsByTenantIdAndEdgeId(tenantId, edgeId, pageLink)); - } + TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset); + return checkNotNull(assetService.findAssetsByTenantIdAndEdgeId(tenantId, edgeId, pageLink).get()); } catch (Exception e) { throw handleException(e); } diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java index fc38153425..8fc4202b76 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java @@ -49,6 +49,9 @@ import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.common.data.page.TimePageData; +import org.thingsboard.server.common.data.page.TimePageLink; +import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.dao.device.claim.ClaimResponse; import org.thingsboard.server.dao.device.claim.ClaimResult; @@ -519,19 +522,20 @@ public class DeviceController extends BaseController { } @PreAuthorize("hasAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/edge/device/{deviceId}", method = RequestMethod.DELETE) + @RequestMapping(value = "/edge/{edgeId}/device/{deviceId}", method = RequestMethod.DELETE) @ResponseBody - public Device unassignDeviceFromEdge(@PathVariable(DEVICE_ID) String strDeviceId) throws ThingsboardException { + public Device unassignDeviceFromEdge(@PathVariable(EDGE_ID) String strEdgeId, + @PathVariable(DEVICE_ID) String strDeviceId) throws ThingsboardException { + checkParameter(EDGE_ID, strEdgeId); checkParameter(DEVICE_ID, strDeviceId); try { + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); + Edge edge = checkEdgeId(edgeId, Operation.READ); + DeviceId deviceId = new DeviceId(toUUID(strDeviceId)); Device device = checkDeviceId(deviceId, Operation.UNASSIGN_FROM_EDGE); - if (device.getEdgeId() == null || device.getEdgeId().getId().equals(ModelConstants.NULL_UUID)) { - throw new IncorrectParameterException("Device isn't assigned to any edge!"); - } - Edge edge = checkEdgeId(device.getEdgeId(), Operation.READ); - Device savedDevice = checkNotNull(deviceService.unassignDeviceFromEdge(getCurrentUser().getTenantId(), deviceId)); + Device savedDevice = checkNotNull(deviceService.unassignDeviceFromEdge(getCurrentUser().getTenantId(), deviceId, edgeId)); logEntityAction(deviceId, device, device.getCustomerId(), @@ -549,24 +553,20 @@ public class DeviceController extends BaseController { @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @RequestMapping(value = "/edge/{edgeId}/devices", params = {"limit"}, method = RequestMethod.GET) @ResponseBody - public TextPageData getEdgeDevices( - @PathVariable("edgeId") String strEdgeId, + public TimePageData getEdgeDevices( + @PathVariable(EDGE_ID) String strEdgeId, @RequestParam int limit, - @RequestParam(required = false) String type, - @RequestParam(required = false) String textSearch, - @RequestParam(required = false) String idOffset, - @RequestParam(required = false) String textOffset) throws ThingsboardException { - checkParameter("edgeId", strEdgeId); + @RequestParam(required = false) Long startTime, + @RequestParam(required = false) Long endTime, + @RequestParam(required = false, defaultValue = "false") boolean ascOrder, + @RequestParam(required = false) String offset) throws ThingsboardException { + checkParameter(EDGE_ID, strEdgeId); try { TenantId tenantId = getCurrentUser().getTenantId(); EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); checkEdgeId(edgeId, Operation.READ); - TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); - if (type != null && type.trim().length()>0) { - return checkNotNull(deviceService.findDevicesByTenantIdAndEdgeIdAndType(tenantId, edgeId, type, pageLink)); - } else { - return checkNotNull(deviceService.findDevicesByTenantIdAndEdgeId(tenantId, edgeId, pageLink)); - } + TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset); + return checkNotNull(deviceService.findDevicesByTenantIdAndEdgeId(tenantId, edgeId, pageLink).get()); } catch (Exception e) { throw handleException(e); } diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java index 27dfb64189..db4e8f86e7 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java @@ -48,6 +48,8 @@ import org.thingsboard.server.common.data.id.UUIDBased; import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.common.data.page.TimePageData; +import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.queue.util.TbCoreComponent; @@ -400,18 +402,20 @@ public class EntityViewController extends BaseController { } @PreAuthorize("hasAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/edge/entityView/{entityViewId}", method = RequestMethod.DELETE) + @RequestMapping(value = "/edge/{edgeId}/entityView/{entityViewId}", method = RequestMethod.DELETE) @ResponseBody - public EntityView unassignEntityViewFromEdge(@PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException { + public EntityView unassignEntityViewFromEdge(@PathVariable(EDGE_ID) String strEdgeId, + @PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException { + checkParameter(EDGE_ID, strEdgeId); checkParameter(ENTITY_VIEW_ID, strEntityViewId); try { + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); + Edge edge = checkEdgeId(edgeId, Operation.READ); + EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId)); EntityView entityView = checkEntityViewId(entityViewId, Operation.UNASSIGN_FROM_EDGE); - if (entityView.getEdgeId() == null || entityView.getEdgeId().getId().equals(ModelConstants.NULL_UUID)) { - throw new IncorrectParameterException("Entity View isn't assigned to any edge!"); - } - Edge edge = checkEdgeId(entityView.getEdgeId(), Operation.READ); - EntityView savedEntityView = checkNotNull(entityViewService.unassignEntityViewFromEdge(getTenantId(), entityViewId)); + + EntityView savedEntityView = checkNotNull(entityViewService.unassignEntityViewFromEdge(getTenantId(), entityViewId, edgeId)); logEntityAction(entityViewId, entityView, entityView.getCustomerId(), ActionType.UNASSIGNED_FROM_EDGE, null, strEntityViewId, edge.getId().toString(), edge.getName()); @@ -428,24 +432,20 @@ public class EntityViewController extends BaseController { @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @RequestMapping(value = "/edge/{edgeId}/entityViews", params = {"limit"}, method = RequestMethod.GET) @ResponseBody - public TextPageData getEdgeEntityViews( - @PathVariable("edgeId") String strEdgeId, + public TimePageData getEdgeEntityViews( + @PathVariable(EDGE_ID) String strEdgeId, @RequestParam int limit, - @RequestParam(required = false) String type, - @RequestParam(required = false) String textSearch, - @RequestParam(required = false) String idOffset, - @RequestParam(required = false) String textOffset) throws ThingsboardException { - checkParameter("edgeId", strEdgeId); + @RequestParam(required = false) Long startTime, + @RequestParam(required = false) Long endTime, + @RequestParam(required = false, defaultValue = "false") boolean ascOrder, + @RequestParam(required = false) String offset) throws ThingsboardException { + checkParameter(EDGE_ID, strEdgeId); try { TenantId tenantId = getCurrentUser().getTenantId(); EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); checkEdgeId(edgeId, Operation.READ); - TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); - if (type != null && type.trim().length()>0) { - return checkNotNull(entityViewService.findEntityViewsByTenantIdAndEdgeIdAndType(tenantId, edgeId, type, pageLink)); - } else { - return checkNotNull(entityViewService.findEntityViewsByTenantIdAndEdgeId(tenantId, edgeId, pageLink)); - } + TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset); + return checkNotNull(entityViewService.findEntityViewsByTenantIdAndEdgeId(tenantId, edgeId, pageLink).get()); } catch (Exception e) { throw handleException(e); } 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 f701b6506b..c1a94d99d6 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 @@ -554,7 +554,7 @@ public final class EdgeGrpcSession implements Closeable { case ENTITY_DELETED_RPC_MESSAGE: Device device = ctx.getDeviceService().findDeviceByTenantIdAndName(edge.getTenantId(), deviceName); if (device != null) { - ctx.getDeviceService().unassignDeviceFromEdge(edge.getTenantId(), device.getId()); + ctx.getDeviceService().unassignDeviceFromEdge(edge.getTenantId(), device.getId(), edge.getId()); } break; } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/DefaultInitEdgeService.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/DefaultInitEdgeService.java index 848b67be84..192fb8395b 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/DefaultInitEdgeService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/DefaultInitEdgeService.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.service.edge.rpc.init; +import com.google.common.util.concurrent.Futures; import io.grpc.stub.StreamObserver; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -54,6 +55,7 @@ import org.thingsboard.server.service.edge.rpc.constructor.EntityViewUpdateMsgCo import org.thingsboard.server.service.edge.rpc.constructor.RuleChainUpdateMsgConstructor; import java.util.UUID; +import java.util.concurrent.Future; @Service @Slf4j @@ -100,10 +102,10 @@ public class DefaultInitEdgeService implements InitEdgeService { private void initDevices(Edge edge, StreamObserver outputStream) { try { - TextPageLink pageLink = new TextPageLink(100); - TextPageData pageData; + TimePageLink pageLink = new TimePageLink(100); + TimePageData pageData; do { - pageData = deviceService.findDevicesByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), pageLink); + pageData = deviceService.findDevicesByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), pageLink).get(); if (!pageData.getData().isEmpty()) { log.trace("[{}] [{}] device(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size()); for (Device device : pageData.getData()) { @@ -130,10 +132,10 @@ public class DefaultInitEdgeService implements InitEdgeService { private void initAssets(Edge edge, StreamObserver outputStream) { try { - TextPageLink pageLink = new TextPageLink(100); - TextPageData pageData; + TimePageLink pageLink = new TimePageLink(100); + TimePageData pageData; do { - pageData = assetService.findAssetsByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), pageLink); + pageData = assetService.findAssetsByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), pageLink).get(); if (!pageData.getData().isEmpty()) { log.trace("[{}] [{}] asset(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size()); for (Asset asset : pageData.getData()) { @@ -160,10 +162,10 @@ public class DefaultInitEdgeService implements InitEdgeService { private void initEntityViews(Edge edge, StreamObserver outputStream) { try { - TextPageLink pageLink = new TextPageLink(100); - TextPageData pageData; + TimePageLink pageLink = new TimePageLink(100); + TimePageData pageData; do { - pageData = entityViewService.findEntityViewsByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), pageLink); + pageData = entityViewService.findEntityViewsByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), pageLink).get(); if (!pageData.getData().isEmpty()) { log.trace("[{}] [{}] entity view(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size()); for (EntityView entityView : pageData.getData()) { diff --git a/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java index 5227efcfa3..29d698d0a4 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java @@ -311,18 +311,6 @@ public class CassandraDatabaseUpgradeService extends AbstractCassandraDatabaseUp schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.6.0", SCHEMA_UPDATE_CQL); loadCql(schemaUpdateFile); - try { - cluster.getSession().execute("alter table asset add edge_id text"); - Thread.sleep(2500); - } catch (InvalidQueryException e) {} - try { - cluster.getSession().execute("alter table device add edge_id text"); - Thread.sleep(2500); - } catch (InvalidQueryException e) {} - try { - cluster.getSession().execute("alter table entity_view add edge_id text"); - Thread.sleep(2500); - } catch (InvalidQueryException e) {} try { cluster.getSession().execute("alter table rule_chain add type text"); Thread.sleep(2500); 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 c9966d0cbb..f9c5e53fd5 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 @@ -238,15 +238,6 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService log.info("Updating schema ..."); schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.6.0", SCHEMA_UPDATE_SQL); loadSql(schemaUpdateFile, conn); - try { - conn.createStatement().execute("ALTER TABLE asset ADD edge_id varchar(31)"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script - } catch (Exception e) {} - try { - conn.createStatement().execute("ALTER TABLE device ADD edge_id varchar(31)"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script - } catch (Exception e) {} - try { - conn.createStatement().execute("ALTER TABLE entity_view ADD edge_id varchar(31)"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script - } catch (Exception e) {} try { conn.createStatement().execute("ALTER TABLE rule_chain ADD type varchar(255) DEFAULT 'SYSTEM'"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script } catch (Exception e) {} diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/asset/AssetService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/asset/AssetService.java index 13a3799cf2..e0db26b718 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/asset/AssetService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/asset/AssetService.java @@ -25,6 +25,8 @@ import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.common.data.page.TimePageData; +import org.thingsboard.server.common.data.page.TimePageLink; import java.util.List; import java.util.Optional; @@ -67,9 +69,7 @@ public interface AssetService { Asset assignAssetToEdge(TenantId tenantId, AssetId assetId, EdgeId edgeId); - Asset unassignAssetFromEdge(TenantId tenantId, AssetId assetId); + Asset unassignAssetFromEdge(TenantId tenantId, AssetId assetId, EdgeId edgeId); - TextPageData findAssetsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TextPageLink pageLink); - - TextPageData findAssetsByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, TextPageLink pageLink); + ListenableFuture> findAssetsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink); } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java index 3bd47fbd68..4e3099e25b 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java @@ -25,11 +25,13 @@ import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.common.data.page.TimePageData; +import org.thingsboard.server.common.data.page.TimePageLink; import java.util.List; public interface DeviceService { - + Device findDeviceById(TenantId tenantId, DeviceId deviceId); ListenableFuture findDeviceByIdAsync(TenantId tenantId, DeviceId deviceId); @@ -68,10 +70,7 @@ public interface DeviceService { Device assignDeviceToEdge(TenantId tenantId, DeviceId deviceId, EdgeId edgeId); - Device unassignDeviceFromEdge(TenantId tenantId, DeviceId deviceId); - - TextPageData findDevicesByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TextPageLink pageLink); - - TextPageData findDevicesByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, TextPageLink pageLink); + Device unassignDeviceFromEdge(TenantId tenantId, DeviceId deviceId, EdgeId edgeId); + ListenableFuture> findDevicesByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink); } 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 04b096f4b2..6061a448e6 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 @@ -26,6 +26,8 @@ import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.common.data.page.TimePageData; +import org.thingsboard.server.common.data.page.TimePageLink; import java.util.List; @@ -68,11 +70,7 @@ public interface EntityViewService { EntityView assignEntityViewToEdge(TenantId tenantId, EntityViewId entityViewId, EdgeId edgeId); - EntityView unassignEntityViewFromEdge(TenantId tenantId, EntityViewId entityViewId); - - TextPageData findEntityViewsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TextPageLink pageLink); - - TextPageData findEntityViewsByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, TextPageLink pageLink); - + EntityView unassignEntityViewFromEdge(TenantId tenantId, EntityViewId entityViewId, EdgeId edgeId); + ListenableFuture> findEntityViewsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Device.java b/common/data/src/main/java/org/thingsboard/server/common/data/Device.java index 201bbc8b07..cd617ec345 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Device.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Device.java @@ -18,7 +18,6 @@ package org.thingsboard.server.common.data; import lombok.EqualsAndHashCode; 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; @EqualsAndHashCode(callSuper = true) @@ -28,7 +27,6 @@ public class Device extends SearchTextBasedWithAdditionalInfo implemen private TenantId tenantId; private CustomerId customerId; - private EdgeId edgeId; private String name; private String type; private String label; @@ -48,7 +46,6 @@ public class Device extends SearchTextBasedWithAdditionalInfo implemen this.name = device.getName(); this.type = device.getType(); this.label = device.getLabel(); - this.edgeId = device.getEdgeId(); } public TenantId getTenantId() { @@ -67,14 +64,6 @@ public class Device extends SearchTextBasedWithAdditionalInfo implemen this.customerId = customerId; } - public EdgeId getEdgeId() { - return edgeId; - } - - public void setEdgeId(EdgeId edgeId) { - this.edgeId = edgeId; - } - @Override public String getName() { return name; @@ -112,8 +101,6 @@ public class Device extends SearchTextBasedWithAdditionalInfo implemen builder.append(tenantId); builder.append(", customerId="); builder.append(customerId); - builder.append(", edgeId="); - builder.append(edgeId); builder.append(", name="); builder.append(name); builder.append(", type="); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java b/common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java index 157a5c884e..4c7e69d79e 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java @@ -19,7 +19,6 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.id.CustomerId; -import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.TenantId; @@ -40,7 +39,6 @@ public class EntityView extends SearchTextBasedWithAdditionalInfo private EntityId entityId; private TenantId tenantId; private CustomerId customerId; - private EdgeId edgeId; private String name; private String type; private TelemetryEntityView keys; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java b/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java index e0dc5dc90c..e0f587dda4 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java @@ -15,13 +15,10 @@ */ package org.thingsboard.server.common.data.asset; -import com.fasterxml.jackson.databind.JsonNode; import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.*; 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.EdgeId; import org.thingsboard.server.common.data.id.TenantId; @EqualsAndHashCode(callSuper = true) @@ -34,7 +31,6 @@ public class Asset extends SearchTextBasedWithAdditionalInfo implements private String name; private String type; private String label; - private EdgeId edgeId; public Asset() { super(); @@ -51,7 +47,6 @@ public class Asset extends SearchTextBasedWithAdditionalInfo implements this.name = asset.getName(); this.type = asset.getType(); this.label = asset.getLabel(); - this.edgeId = asset.getEdgeId(); } public TenantId getTenantId() { @@ -70,14 +65,6 @@ public class Asset extends SearchTextBasedWithAdditionalInfo implements this.customerId = customerId; } - public EdgeId getEdgeId() { - return edgeId; - } - - public void setEdgeId(EdgeId edgeId) { - this.edgeId = edgeId; - } - @Override public String getName() { return name; @@ -115,8 +102,6 @@ public class Asset extends SearchTextBasedWithAdditionalInfo implements builder.append(tenantId); builder.append(", customerId="); builder.append(customerId); - builder.append(", edgeId="); - builder.append(edgeId); builder.append(", name="); builder.append(name); builder.append(", type="); diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java index 41f8a2b630..118b8d47fd 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java @@ -341,16 +341,6 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ } } - private void deleteRelation(TenantId tenantId, EntityRelation alarmRelation) { - log.debug("Deleting Alarm relation: {}", alarmRelation); - relationService.deleteRelation(tenantId, alarmRelation); - } - - private void createRelation(TenantId tenantId, EntityRelation alarmRelation) { - log.debug("Creating Alarm relation: {}", alarmRelation); - relationService.saveRelation(tenantId, alarmRelation); - } - private Alarm merge(Alarm existing, Alarm alarm) { if (alarm.getStartTs() > existing.getEndTs()) { existing.setEndTs(alarm.getStartTs()); @@ -395,7 +385,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ } } - private void createAlarmRelation(TenantId tenantId, EntityId entityId, EntityId alarmId, AlarmStatus status, boolean createAnyRelation) { + private void createAlarmRelation(TenantId tenantId, EntityId entityId, EntityId alarmId, AlarmStatus status, boolean createAnyRelation) throws ExecutionException, InterruptedException { if (createAnyRelation) { createRelation(tenantId, new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + AlarmSearchStatus.ANY.name(), RelationTypeGroup.ALARM)); } @@ -404,13 +394,13 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ createRelation(tenantId, new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.getAckSearchStatus().name(), RelationTypeGroup.ALARM)); } - private void deleteAlarmRelation(TenantId tenantId, EntityId entityId, EntityId alarmId, AlarmStatus status) { + private void deleteAlarmRelation(TenantId tenantId, EntityId entityId, EntityId alarmId, AlarmStatus status) throws ExecutionException, InterruptedException { deleteRelation(tenantId, new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.name(), RelationTypeGroup.ALARM)); deleteRelation(tenantId, new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.getClearSearchStatus().name(), RelationTypeGroup.ALARM)); deleteRelation(tenantId, new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.getAckSearchStatus().name(), RelationTypeGroup.ALARM)); } - private void updateAlarmRelation(TenantId tenantId, EntityId entityId, EntityId alarmId, AlarmStatus oldStatus, AlarmStatus newStatus) { + private void updateAlarmRelation(TenantId tenantId, EntityId entityId, EntityId alarmId, AlarmStatus oldStatus, AlarmStatus newStatus) throws ExecutionException, InterruptedException { deleteAlarmRelation(tenantId, entityId, alarmId, oldStatus); createAlarmRelation(tenantId, entityId, alarmId, newStatus, false); } 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 958dcf05b3..0566cb895a 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 @@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.dao.Dao; import java.util.List; @@ -116,23 +117,12 @@ public interface AssetDao extends Dao { ListenableFuture> findTenantAssetTypesAsync(UUID tenantId); /** - * Find assets by tenantId, customerId and page link. + * Find assets by tenantId, edgeId and page link. * * @param tenantId the tenantId * @param edgeId the edgeId * @param pageLink the page link * @return the list of asset objects */ - List findAssetsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TextPageLink pageLink); - - /** - * Find assets by tenantId, customerId, type and page link. - * - * @param tenantId the tenantId - * @param edgeId the edgeId - * @param type the type - * @param pageLink the page link - * @return the list of asset objects - */ - List findAssetsByTenantIdAndEdgeIdAndType(UUID tenantId, UUID edgeId, String type, TextPageLink pageLink); + ListenableFuture> findAssetsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java index 907b0399fa..8c0f4cf644 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java @@ -16,6 +16,7 @@ package org.thingsboard.server.dao.asset; +import com.google.common.base.Function; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; @@ -29,12 +30,14 @@ import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.asset.AssetSearchQuery; +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.EdgeId; @@ -42,9 +45,13 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.common.data.page.TimePageData; +import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntitySearchDirection; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.dao.customer.CustomerDao; +import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.exception.DataValidationException; @@ -52,6 +59,7 @@ import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.tenant.TenantDao; +import javax.annotation.Nullable; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -89,6 +97,9 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ @Autowired private EntityViewService entityViewService; + @Autowired + private EdgeService edgeService; + @Autowired private CacheManager cacheManager; @@ -285,36 +296,52 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ @Override public Asset assignAssetToEdge(TenantId tenantId, AssetId assetId, EdgeId edgeId) { Asset asset = findAssetById(tenantId, assetId); - asset.setEdgeId(edgeId); - return saveAsset(asset); + Edge edge = edgeService.findEdgeById(tenantId, edgeId); + if (edge == null) { + throw new DataValidationException("Can't assign asset to non-existent edge!"); + } + if (!edge.getTenantId().getId().equals(asset.getTenantId().getId())) { + throw new DataValidationException("Can't assign asset to edge from different tenant!"); + } + try { + createRelation(tenantId, new EntityRelation(edgeId, assetId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE)); + } catch (ExecutionException | InterruptedException e) { + log.warn("[{}] Failed to create asset relation. Edge Id: [{}]", assetId, edgeId); + throw new RuntimeException(e); + } + return asset; } @Override - public Asset unassignAssetFromEdge(TenantId tenantId, AssetId assetId) { + public Asset unassignAssetFromEdge(TenantId tenantId, AssetId assetId, EdgeId edgeId) { Asset asset = findAssetById(tenantId, assetId); - asset.setEdgeId(null); - return saveAsset(asset); + Edge edge = edgeService.findEdgeById(tenantId, edgeId); + if (edge == null) { + throw new DataValidationException("Can't unassign asset from non-existent edge!"); + } + try { + deleteRelation(tenantId, new EntityRelation(edgeId, assetId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE)); + } catch (ExecutionException | InterruptedException e) { + log.warn("[{}] Failed to delete asset relation. Edge Id: [{}]", assetId, edgeId); + throw new RuntimeException(e); + } + return asset; } @Override - public TextPageData findAssetsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TextPageLink pageLink) { + public ListenableFuture> findAssetsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink) { log.trace("Executing findAssetsByTenantIdAndEdgeId, tenantId [{}], edgeId [{}], pageLink [{}]", tenantId, edgeId, pageLink); validateId(tenantId, INCORRECT_TENANT_ID + tenantId); validateId(edgeId, INCORRECT_EDGE_ID + edgeId); validatePageLink(pageLink, INCORRECT_PAGE_LINK + pageLink); - List assets = assetDao.findAssetsByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink); - return new TextPageData<>(assets, pageLink); - } - - @Override - public TextPageData findAssetsByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, TextPageLink pageLink) { - log.trace("Executing findAssetsByTenantIdAndEdgeIdAndType, tenantId [{}], edgeId [{}], type [{}], pageLink [{}]", tenantId, edgeId, type, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(edgeId, INCORRECT_EDGE_ID + edgeId); - validateString(type, "Incorrect type " + type); - validatePageLink(pageLink, INCORRECT_PAGE_LINK + pageLink); - List assets = assetDao.findAssetsByTenantIdAndEdgeIdAndType(tenantId.getId(), edgeId.getId(), type, pageLink); - return new TextPageData<>(assets, pageLink); + ListenableFuture> assets = assetDao.findAssetsByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink); + return Futures.transform(assets, new Function, TimePageData>() { + @Nullable + @Override + public TimePageData apply(@Nullable List assets) { + return new TimePageData<>(assets, pageLink); + } + }, MoreExecutors.directExecutor()); } private DataValidator assetValidator = diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/CassandraAssetDao.java b/dao/src/main/java/org/thingsboard/server/dao/asset/CassandraAssetDao.java index 5f8632516f..87ccef86f1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/CassandraAssetDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/CassandraAssetDao.java @@ -25,16 +25,23 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.common.data.page.TimePageLink; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.model.EntitySubtypeEntity; import org.thingsboard.server.dao.model.nosql.AssetEntity; import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTextDao; +import org.thingsboard.server.dao.relation.RelationDao; import org.thingsboard.server.dao.util.NoSqlDao; import javax.annotation.Nullable; @@ -68,6 +75,9 @@ import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY; @NoSqlDao public class CassandraAssetDao extends CassandraAbstractSearchTextDao implements AssetDao { + @Autowired + private RelationDao relationDao; + @Override protected Class getColumnFamilyClass() { return AssetEntity.class; @@ -190,30 +200,16 @@ public class CassandraAssetDao extends CassandraAbstractSearchTextDao findAssetsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TextPageLink pageLink) { -// log.debug("Try to find assets by tenantId [{}], customerId[{}] and pageLink [{}]", tenantId, customerId, pageLink); -// List assetEntities = findPageWithTextSearch(new TenantId(tenantId), ASSET_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME, -// Arrays.asList(eq(ASSET_CUSTOMER_ID_PROPERTY, customerId), -// eq(ASSET_TENANT_ID_PROPERTY, tenantId)), -// pageLink); -// -// log.trace("Found assets [{}] by tenantId [{}], customerId [{}] and pageLink [{}]", assetEntities, tenantId, customerId, pageLink); -// return DaoUtil.convertDataList(assetEntities); - throw new UnsupportedOperationException("Cassandra is not supported yet"); - } - - @Override - public List findAssetsByTenantIdAndEdgeIdAndType(UUID tenantId, UUID edgeId, String type, TextPageLink pageLink) { -// log.debug("Try to find assets by tenantId [{}], customerId [{}], type [{}] and pageLink [{}]", tenantId, customerId, type, pageLink); -// List assetEntities = findPageWithTextSearch(new TenantId(tenantId), ASSET_BY_CUSTOMER_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME, -// Arrays.asList(eq(ASSET_TYPE_PROPERTY, type), -// eq(ASSET_CUSTOMER_ID_PROPERTY, customerId), -// eq(ASSET_TENANT_ID_PROPERTY, tenantId)), -// pageLink); -// -// log.trace("Found assets [{}] by tenantId [{}], customerId [{}], type [{}] and pageLink [{}]", assetEntities, tenantId, customerId, type, pageLink); -// return DaoUtil.convertDataList(assetEntities); - throw new UnsupportedOperationException("Cassandra is not supported yet"); + public ListenableFuture> findAssetsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink) { + log.debug("Try to find assets by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink); + ListenableFuture> relations = relationDao.findRelations(new TenantId(tenantId), new EdgeId(edgeId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE, EntityType.ASSET, pageLink); + return Futures.transformAsync(relations, input -> { + List> assetFutures = new ArrayList<>(input.size()); + for (EntityRelation relation : input) { + assetFutures.add(findByIdAsync(new TenantId(tenantId), relation.getTo().getId())); + } + return Futures.successfulAsList(assetFutures); + }, MoreExecutors.directExecutor()); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java index 3658dc22e8..ae3f787e5a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java @@ -162,16 +162,6 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb } } - private void deleteRelation(TenantId tenantId, EntityRelation dashboardRelation) throws ExecutionException, InterruptedException { - log.debug("Deleting Dashboard relation: {}", dashboardRelation); - relationService.deleteRelationAsync(tenantId, dashboardRelation).get(); - } - - private void createRelation(TenantId tenantId, EntityRelation dashboardRelation) throws ExecutionException, InterruptedException { - log.debug("Creating Dashboard relation: {}", dashboardRelation); - relationService.saveRelationAsync(tenantId, dashboardRelation).get(); - } - @Override public void deleteDashboard(TenantId tenantId, DashboardId dashboardId) { log.trace("Executing deleteDashboard [{}]", dashboardId); diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/CassandraDeviceDao.java b/dao/src/main/java/org/thingsboard/server/dao/device/CassandraDeviceDao.java index 513b313aa0..41d0ff8d2c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/CassandraDeviceDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/CassandraDeviceDao.java @@ -25,16 +25,22 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.common.data.page.TimePageLink; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.model.EntitySubtypeEntity; import org.thingsboard.server.dao.model.nosql.DeviceEntity; import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTextDao; +import org.thingsboard.server.dao.relation.RelationDao; import org.thingsboard.server.dao.util.NoSqlDao; import javax.annotation.Nullable; @@ -68,6 +74,9 @@ import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY; @NoSqlDao public class CassandraDeviceDao extends CassandraAbstractSearchTextDao implements DeviceDao { + @Autowired + private RelationDao relationDao; + @Override protected Class getColumnFamilyClass() { return DeviceEntity.class; @@ -190,30 +199,17 @@ public class CassandraDeviceDao extends CassandraAbstractSearchTextDao findDevicesByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TextPageLink pageLink) { -// log.debug("Try to find devices by tenantId [{}], customerId[{}] and pageLink [{}]", tenantId, customerId, pageLink); -// List deviceEntities = findPageWithTextSearch(new TenantId(tenantId), DEVICE_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME, -// Arrays.asList(eq(DEVICE_CUSTOMER_ID_PROPERTY, customerId), -// eq(DEVICE_TENANT_ID_PROPERTY, tenantId)), -// pageLink); -// -// log.trace("Found devices [{}] by tenantId [{}], customerId [{}] and pageLink [{}]", deviceEntities, tenantId, customerId, pageLink); -// return DaoUtil.convertDataList(deviceEntities); - throw new UnsupportedOperationException("Cassandra is not supported yet"); + public ListenableFuture> findDevicesByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink) { + log.debug("Try to find devices by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink); + ListenableFuture> relations = relationDao.findRelations(new TenantId(tenantId), new EdgeId(edgeId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE, EntityType.DEVICE, pageLink); + return Futures.transformAsync(relations, input -> { + List> deviceFutures = new ArrayList<>(input.size()); + for (EntityRelation relation : input) { + deviceFutures.add(findByIdAsync(new TenantId(tenantId), relation.getTo().getId())); + } + return Futures.successfulAsList(deviceFutures); + }, MoreExecutors.directExecutor()); } - @Override - public List findDevicesByTenantIdAndEdgeIdAndType(UUID tenantId, UUID edgeId, String type, TextPageLink pageLink) { -// log.debug("Try to find devices by tenantId [{}], customerId [{}], type [{}] and pageLink [{}]", tenantId, customerId, type, pageLink); -// List deviceEntities = findPageWithTextSearch(new TenantId(tenantId), DEVICE_BY_CUSTOMER_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME, -// Arrays.asList(eq(DEVICE_TYPE_PROPERTY, type), -// eq(DEVICE_CUSTOMER_ID_PROPERTY, customerId), -// eq(DEVICE_TENANT_ID_PROPERTY, tenantId)), -// pageLink); -// -// log.trace("Found devices [{}] by tenantId [{}], customerId [{}], type [{}] and pageLink [{}]", deviceEntities, tenantId, customerId, type, pageLink); -// return DaoUtil.convertDataList(deviceEntities); - throw new UnsupportedOperationException("Cassandra is not supported yet"); - } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java index 856243a4a5..d6687b7278 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java @@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.dao.Dao; import java.util.List; @@ -124,16 +125,5 @@ public interface DeviceDao extends Dao { * @param pageLink the page link * @return the list of device objects */ - List findDevicesByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TextPageLink pageLink); - - /** - * Find devices by tenantId, edgeId, type and page link. - * - * @param tenantId the tenantId - * @param edgeId the edgeId - * @param type the type - * @param pageLink the page link - * @return the list of device objects - */ - List findDevicesByTenantIdAndEdgeIdAndType(UUID tenantId, UUID edgeId, String type, TextPageLink pageLink); + ListenableFuture> findDevicesByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java index cb151bcd97..c8f24262a1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java @@ -41,19 +41,26 @@ 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.EntityId; +import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.common.data.page.TimePageData; +import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntitySearchDirection; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; +import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentialsType; import org.thingsboard.server.dao.customer.CustomerDao; +import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.entityview.EntityViewService; 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.tenant.TenantDao; import javax.annotation.Nullable; @@ -98,6 +105,9 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe @Autowired private EntityViewService entityViewService; + @Autowired + private EdgeService edgeService; + @Autowired private CacheManager cacheManager; @@ -324,36 +334,52 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe @Override public Device assignDeviceToEdge(TenantId tenantId, DeviceId deviceId, EdgeId edgeId) { Device device = findDeviceById(tenantId, deviceId); - device.setEdgeId(edgeId); - return saveDevice(device); + Edge edge = edgeService.findEdgeById(tenantId, edgeId); + if (edge == null) { + throw new DataValidationException("Can't assign device to non-existent edge!"); + } + if (!edge.getTenantId().getId().equals(device.getTenantId().getId())) { + throw new DataValidationException("Can't assign device to edge from different tenant!"); + } + try { + createRelation(tenantId, new EntityRelation(edgeId, deviceId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE)); + } catch (ExecutionException | InterruptedException e) { + log.warn("[{}] Failed to create device relation. Edge Id: [{}]", deviceId, edgeId); + throw new RuntimeException(e); + } + return device; } @Override - public Device unassignDeviceFromEdge(TenantId tenantId, DeviceId deviceId) { + public Device unassignDeviceFromEdge(TenantId tenantId, DeviceId deviceId, EdgeId edgeId) { Device device = findDeviceById(tenantId, deviceId); - device.setEdgeId(null); - return saveDevice(device); + Edge edge = edgeService.findEdgeById(tenantId, edgeId); + if (edge == null) { + throw new DataValidationException("Can't unassign device from non-existent edge!"); + } + try { + deleteRelation(tenantId, new EntityRelation(edgeId, deviceId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE)); + } catch (ExecutionException | InterruptedException e) { + log.warn("[{}] Failed to delete device relation. Edge Id: [{}]", deviceId, edgeId); + throw new RuntimeException(e); + } + return device; } @Override - public TextPageData findDevicesByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TextPageLink pageLink) { + public ListenableFuture> findDevicesByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink) { log.trace("Executing findDevicesByTenantIdAndEdgeId, tenantId [{}], edgeId [{}], pageLink [{}]", tenantId, edgeId, pageLink); validateId(tenantId, INCORRECT_TENANT_ID + tenantId); validateId(edgeId, INCORRECT_EDGE_ID + edgeId); validatePageLink(pageLink, INCORRECT_PAGE_LINK + pageLink); - List devices = deviceDao.findDevicesByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink); - return new TextPageData<>(devices, pageLink); - } - - @Override - public TextPageData findDevicesByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, TextPageLink pageLink) { - log.trace("Executing findDevicesByTenantIdAndEdgeIdAndType, tenantId [{}], edgeId [{}], type [{}], pageLink [{}]", tenantId, edgeId, type, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(edgeId, INCORRECT_EDGE_ID + edgeId); - validateString(type, "Incorrect type " + type); - validatePageLink(pageLink, INCORRECT_PAGE_LINK + pageLink); - List devices = deviceDao.findDevicesByTenantIdAndEdgeIdAndType(tenantId.getId(), edgeId.getId(), type, pageLink); - return new TextPageData<>(devices, pageLink); + ListenableFuture> devices = deviceDao.findDevicesByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink); + return Futures.transform(devices, new Function, TimePageData>() { + @Nullable + @Override + public TimePageData apply(@Nullable List devices) { + return new TimePageData<>(devices, pageLink); + } + }, MoreExecutors.directExecutor()); } private DataValidator deviceValidator = diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java index f974cabc62..25c83ed031 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java @@ -62,6 +62,7 @@ import org.thingsboard.server.common.data.page.TimePageData; import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntitySearchDirection; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleChainType; @@ -75,6 +76,7 @@ import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.event.EventService; import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; @@ -144,6 +146,9 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic @Autowired private EntityViewService entityViewService; + @Autowired + private RelationService relationService; + private ExecutorService tsCallBackExecutor; @PostConstruct @@ -219,7 +224,7 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic dashboardService.unassignEdgeDashboards(tenantId, edgeId); // TODO: validate that rule chains are removed by deleteEntityRelations(tenantId, edgeId); call - ruleChainService.unassignEdgeRuleChains(tenantId, edgeId); + ruleChainService.unassignEdgeRuleChains(tenantId, edgeId); List list = new ArrayList<>(); list.add(edge.getTenantId()); @@ -385,15 +390,18 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic } private void processCustomTbMsg(TenantId tenantId, TbMsg tbMsg, FutureCallback callback) { - EdgeId edgeId = getEdgeIdByOriginatorId(tenantId, tbMsg.getOriginator()); - EdgeQueueEntityType edgeQueueEntityType = getEdgeQueueTypeByEntityType(tbMsg.getOriginator().getEntityType()); - if (edgeId != null && edgeQueueEntityType != null) { - try { - saveEventToEdgeQueue(tenantId, edgeId, edgeQueueEntityType, tbMsg.getType(), Base64.encodeBase64String(TbMsg.toByteArray(tbMsg)), callback); - } catch (IOException e) { - log.error("Error while saving custom tbMsg into Edge Queue", e); + ListenableFuture edgeIdFuture = getEdgeIdByOriginatorId(tenantId, tbMsg.getOriginator()); + Futures.transform(edgeIdFuture, edgeId -> { + EdgeQueueEntityType edgeQueueEntityType = getEdgeQueueTypeByEntityType(tbMsg.getOriginator().getEntityType()); + if (edgeId != null && edgeQueueEntityType != null) { + try { + saveEventToEdgeQueue(tenantId, edgeId, edgeQueueEntityType, tbMsg.getType(), Base64.encodeBase64String(TbMsg.toByteArray(tbMsg)), callback); + } catch (IOException e) { + log.error("Error while saving custom tbMsg into Edge Queue", e); + } } - } + return null; + }, MoreExecutors.directExecutor()); } private EdgeQueueEntityType getEdgeQueueTypeByEntityType(EntityType entityType) { @@ -410,23 +418,30 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic } } - private EdgeId getEdgeIdByOriginatorId(TenantId tenantId, EntityId originatorId) { - switch (originatorId.getEntityType()) { - case DEVICE: - Device device = deviceService.findDeviceById(tenantId, new DeviceId(originatorId.getId())); - return device.getEdgeId(); - case ASSET: - Asset asset = assetService.findAssetById(tenantId, new AssetId(originatorId.getId())); - return asset.getEdgeId(); - case ENTITY_VIEW: - EntityView entityView = entityViewService.findEntityViewById(tenantId, new EntityViewId(originatorId.getId())); - return entityView.getEdgeId(); - default: - log.info("Unsupported entity type: [{}]", originatorId.getEntityType()); - return null; + private ListenableFuture getEdgeIdByOriginatorId(TenantId tenantId, EntityId originatorId) { + List originatorEdgeRelations = relationService.findByToAndType(tenantId, originatorId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE); + if (originatorEdgeRelations != null && originatorEdgeRelations.size() > 0) { + return Futures.immediateFuture(new EdgeId(originatorEdgeRelations.get(0).getFrom().getId())); + } else { + return Futures.immediateFuture(null); } } + private void pushEventToEdge(TenantId tenantId, EntityId originatorId, EdgeQueueEntityType edgeQueueEntityType, TbMsg tbMsg, FutureCallback callback) { + ListenableFuture edgeIdFuture = getEdgeIdByOriginatorId(tenantId, originatorId); + Futures.transform(edgeIdFuture, edgeId -> { + if (edgeId != null) { + try { + pushEventToEdge(tenantId, edgeId, edgeQueueEntityType, tbMsg, callback); + } catch (Exception e) { + log.error("Failed to push event to edge, edgeId [{}], tbMsg [{}]", edgeId, tbMsg, e); + } + } + return null; + }, + MoreExecutors.directExecutor()); + } + private void processDevice(TenantId tenantId, TbMsg tbMsg, FutureCallback callback) throws IOException { switch (tbMsg.getType()) { case DataConstants.ENTITY_ASSIGNED_TO_EDGE: @@ -437,9 +452,7 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic case DataConstants.ENTITY_CREATED: case DataConstants.ENTITY_UPDATED: Device device = mapper.readValue(tbMsg.getData(), Device.class); - if (device.getEdgeId() != null) { - pushEventToEdge(tenantId, device.getEdgeId(), EdgeQueueEntityType.DEVICE, tbMsg, callback); - } + pushEventToEdge(tenantId, device.getId(), EdgeQueueEntityType.DEVICE, tbMsg, callback); break; default: log.warn("Unsupported msgType [{}], tbMsg [{}]", tbMsg.getType(), tbMsg); @@ -471,9 +484,7 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic case DataConstants.ENTITY_CREATED: case DataConstants.ENTITY_UPDATED: Asset asset = mapper.readValue(tbMsg.getData(), Asset.class); - if (asset.getEdgeId() != null) { - pushEventToEdge(tenantId, asset.getEdgeId(), EdgeQueueEntityType.ASSET, tbMsg, callback); - } + pushEventToEdge(tenantId, asset.getId(), EdgeQueueEntityType.ASSET, tbMsg, callback); break; default: log.warn("Unsupported msgType [{}], tbMsg [{}]", tbMsg.getType(), tbMsg); @@ -490,9 +501,7 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic case DataConstants.ENTITY_CREATED: case DataConstants.ENTITY_UPDATED: EntityView entityView = mapper.readValue(tbMsg.getData(), EntityView.class); - if (entityView.getEdgeId() != null) { - pushEventToEdge(tenantId, entityView.getEdgeId(), EdgeQueueEntityType.ENTITY_VIEW, tbMsg, callback); - } + pushEventToEdge(tenantId, entityView.getId(), EdgeQueueEntityType.ENTITY_VIEW, tbMsg, callback); break; default: log.warn("Unsupported msgType [{}], tbMsg [{}]", tbMsg.getType(), tbMsg); @@ -507,10 +516,9 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic case DataConstants.ALARM_ACK: case DataConstants.ALARM_CLEAR: Alarm alarm = mapper.readValue(tbMsg.getData(), Alarm.class); - EdgeId edgeId = getEdgeIdByOriginatorId(tenantId, alarm.getOriginator()); EdgeQueueEntityType edgeQueueEntityType = getEdgeQueueTypeByEntityType(alarm.getOriginator().getEntityType()); - if (edgeId != null && edgeQueueEntityType != null) { - pushEventToEdge(tenantId, edgeId, EdgeQueueEntityType.ALARM, tbMsg, callback); + if (edgeQueueEntityType != null) { + pushEventToEdge(tenantId, alarm.getOriginator(), EdgeQueueEntityType.ALARM, tbMsg, callback); } break; default: diff --git a/dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java b/dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java index 5c7f6c88e2..17fa7bcb3e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java @@ -21,10 +21,12 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.dao.relation.RelationService; import javax.annotation.PostConstruct; import java.util.Optional; +import java.util.concurrent.ExecutionException; @Slf4j public abstract class AbstractEntityService { @@ -42,6 +44,16 @@ public abstract class AbstractEntityService { sqlDatabaseUsed = "sql".equalsIgnoreCase(databaseType); } + protected void createRelation(TenantId tenantId, EntityRelation relation) throws ExecutionException, InterruptedException { + log.debug("Creating relation: {}", relation); + relationService.saveRelation(tenantId, relation); + } + + protected void deleteRelation(TenantId tenantId, EntityRelation relation) throws ExecutionException, InterruptedException { + log.debug("Deleting relation: {}", relation); + relationService.deleteRelation(tenantId, relation); + } + protected void deleteEntityRelations(TenantId tenantId, EntityId entityId) { log.trace("Executing deleteEntityRelations [{}]", entityId); relationService.deleteEntityRelations(tenantId, entityId); diff --git a/dao/src/main/java/org/thingsboard/server/dao/entityview/CassandraEntityViewDao.java b/dao/src/main/java/org/thingsboard/server/dao/entityview/CassandraEntityViewDao.java index ae08cddb05..c65e7d70f2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entityview/CassandraEntityViewDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entityview/CassandraEntityViewDao.java @@ -25,16 +25,23 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityView; +import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.common.data.page.TimePageLink; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.model.EntitySubtypeEntity; import org.thingsboard.server.dao.model.nosql.EntityViewEntity; import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTextDao; +import org.thingsboard.server.dao.relation.RelationDao; import org.thingsboard.server.dao.util.NoSqlDao; import javax.annotation.Nullable; @@ -73,6 +80,9 @@ import static org.thingsboard.server.dao.model.ModelConstants.TENANT_ID_PROPERTY @NoSqlDao public class CassandraEntityViewDao extends CassandraAbstractSearchTextDao implements EntityViewDao { + @Autowired + private RelationDao relationDao; + @Override protected Class getColumnFamilyClass() { return EntityViewEntity.class; @@ -186,30 +196,16 @@ public class CassandraEntityViewDao extends CassandraAbstractSearchTextDao findEntityViewsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TextPageLink pageLink) { -// log.debug("Try to find entity views by tenantId [{}], customerId[{}] and pageLink [{}]", -// tenantId, customerId, pageLink); -// List entityViewEntities = findPageWithTextSearch(new TenantId(tenantId), -// ENTITY_VIEW_BY_TENANT_AND_CUSTOMER_CF, -// Arrays.asList(eq(CUSTOMER_ID_PROPERTY, customerId), eq(TENANT_ID_PROPERTY, tenantId)), -// pageLink); -// log.trace("Found find entity views [{}] by tenantId [{}], customerId [{}] and pageLink [{}]", -// entityViewEntities, tenantId, customerId, pageLink); -// return DaoUtil.convertDataList(entityViewEntities); - throw new UnsupportedOperationException("Cassandra is not supported yet"); + public ListenableFuture> findEntityViewsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink) { + log.debug("Try to find entity views by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink); + ListenableFuture> relations = relationDao.findRelations(new TenantId(tenantId), new EdgeId(edgeId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE, EntityType.ENTITY_VIEW, pageLink); + return Futures.transformAsync(relations, input -> { + List> entityViewFutures = new ArrayList<>(input.size()); + for (EntityRelation relation : input) { + entityViewFutures.add(findByIdAsync(new TenantId(tenantId), relation.getTo().getId())); + } + return Futures.successfulAsList(entityViewFutures); + }, MoreExecutors.directExecutor()); } - @Override - public List findEntityViewsByTenantIdAndEdgeIdAndType(UUID tenantId, UUID edgeId, String type, TextPageLink pageLink) { -// log.debug("Try to find entity views by tenantId [{}], customerId[{}], type [{}] and pageLink [{}]", -// tenantId, customerId, type, pageLink); -// List entityViewEntities = findPageWithTextSearch(new TenantId(tenantId), -// ENTITY_VIEW_BY_TENANT_AND_CUSTOMER_AND_TYPE_CF, -// Arrays.asList(eq(DEVICE_TYPE_PROPERTY, type), eq(CUSTOMER_ID_PROPERTY, customerId), eq(TENANT_ID_PROPERTY, tenantId)), -// pageLink); -// log.trace("Found find entity views [{}] by tenantId [{}], customerId [{}], type [{}] and pageLink [{}]", -// entityViewEntities, tenantId, customerId, type, pageLink); -// return DaoUtil.convertDataList(entityViewEntities); - throw new UnsupportedOperationException("Cassandra is not supported yet"); - } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.java b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.java index cefa28a916..cd9501a70b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.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, @@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.dao.Dao; import java.util.List; @@ -110,22 +111,8 @@ public interface EntityViewDao extends Dao { * @param pageLink the page link * @return the list of entity view objects */ - List findEntityViewsByTenantIdAndEdgeId(UUID tenantId, - UUID edgeId, - TextPageLink pageLink); - - /** - * Find entity views by tenantId, edgeId, type and page link. - * - * @param tenantId the tenantId - * @param edgeId the edgeId - * @param type the type - * @param pageLink the page link - * @return the list of entity view objects - */ - List findEntityViewsByTenantIdAndEdgeIdAndType(UUID tenantId, - UUID edgeId, - String type, - TextPageLink pageLink); + ListenableFuture> findEntityViewsByTenantIdAndEdgeId(UUID tenantId, + UUID edgeId, + TimePageLink pageLink); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java index 70be8c4486..907072e549 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java @@ -30,10 +30,12 @@ import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Caching; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EdgeId; @@ -42,9 +44,13 @@ import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.common.data.page.TimePageData; +import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntitySearchDirection; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.dao.customer.CustomerDao; +import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; @@ -58,6 +64,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Optional; +import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; import static org.thingsboard.server.common.data.CacheConstants.ENTITY_VIEW_CACHE; @@ -88,6 +95,9 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti @Autowired private CustomerDao customerDao; + @Autowired + private EdgeService edgeService; + @Autowired private CacheManager cacheManager; @@ -285,47 +295,56 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti }, MoreExecutors.directExecutor()); } - - @CacheEvict(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityViewId}") @Override public EntityView assignEntityViewToEdge(TenantId tenantId, EntityViewId entityViewId, EdgeId edgeId) { EntityView entityView = findEntityViewById(tenantId, entityViewId); - entityView.setEdgeId(edgeId); - return saveEntityView(entityView); + Edge edge = edgeService.findEdgeById(tenantId, edgeId); + if (edge == null) { + throw new DataValidationException("Can't assign entityView to non-existent edge!"); + } + if (!edge.getTenantId().getId().equals(entityView.getTenantId().getId())) { + throw new DataValidationException("Can't assign entityView to edge from different tenant!"); + } + try { + createRelation(tenantId, new EntityRelation(edgeId, entityViewId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE)); + } catch (ExecutionException | InterruptedException e) { + log.warn("[{}] Failed to create entityView relation. Edge Id: [{}]", entityViewId, edgeId); + throw new RuntimeException(e); + } + return entityView; } - @CacheEvict(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityViewId}") @Override - public EntityView unassignEntityViewFromEdge(TenantId tenantId, EntityViewId entityViewId) { + public EntityView unassignEntityViewFromEdge(TenantId tenantId, EntityViewId entityViewId, EdgeId edgeId) { EntityView entityView = findEntityViewById(tenantId, entityViewId); - entityView.setEdgeId(null); - return saveEntityView(entityView); - } - - @Override - public TextPageData findEntityViewsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, - TextPageLink pageLink) { - log.trace("Executing findEntityViewsByTenantIdAndEdgeId, tenantId [{}], edgeId [{}]," + - " pageLink [{}]", tenantId, edgeId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(edgeId, INCORRECT_EDGE_ID + edgeId); - validatePageLink(pageLink, INCORRECT_PAGE_LINK + pageLink); - List entityViews = entityViewDao.findEntityViewsByTenantIdAndEdgeId(tenantId.getId(), - edgeId.getId(), pageLink); - return new TextPageData<>(entityViews, pageLink); + Edge edge = edgeService.findEdgeById(tenantId, edgeId); + if (edge == null) { + throw new DataValidationException("Can't unassign entityView from non-existent edge!"); + } + try { + deleteRelation(tenantId, new EntityRelation(edgeId, entityViewId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE)); + } catch (ExecutionException | InterruptedException e) { + log.warn("[{}] Failed to delete entityView relation. Edge Id: [{}]", entityViewId, edgeId); + throw new RuntimeException(e); + } + return entityView; } @Override - public TextPageData findEntityViewsByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, TextPageLink pageLink) { - log.trace("Executing findEntityViewsByTenantIdAndEdgeIdAndType, tenantId [{}], edgeId [{}]," + - " pageLink [{}], type [{}]", tenantId, edgeId, pageLink, type); + public ListenableFuture> findEntityViewsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, + TimePageLink pageLink) { + log.trace("Executing findEntityViewsByTenantIdAndEdgeId, tenantId [{}], edgeId [{}], pageLink [{}]", tenantId, edgeId, pageLink); validateId(tenantId, INCORRECT_TENANT_ID + tenantId); validateId(edgeId, INCORRECT_EDGE_ID + edgeId); validatePageLink(pageLink, INCORRECT_PAGE_LINK + pageLink); - validateString(type, "Incorrect type " + type); - List entityViews = entityViewDao.findEntityViewsByTenantIdAndEdgeIdAndType(tenantId.getId(), - edgeId.getId(), type, pageLink); - return new TextPageData<>(entityViews, pageLink); + ListenableFuture> entityViews = entityViewDao.findEntityViewsByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink); + return Futures.transform(entityViews, new Function, TimePageData>() { + @Nullable + @Override + public TimePageData apply(@Nullable List entityViews) { + return new TimePageData<>(entityViews, pageLink); + } + }, MoreExecutors.directExecutor()); } private DataValidator entityViewValidator = 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 7c089f8975..dcdf9460bf 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 @@ -142,7 +142,6 @@ public class ModelConstants { public static final String DEVICE_TYPE_PROPERTY = "type"; public static final String DEVICE_LABEL_PROPERTY = "label"; public static final String DEVICE_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY; - public static final String DEVICE_EDGE_ID_PROPERTY = "edge_id"; public static final String DEVICE_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_and_search_text"; public static final String DEVICE_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_by_type_and_search_text"; public static final String DEVICE_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_customer_and_search_text"; @@ -158,7 +157,6 @@ public class ModelConstants { public static final String ENTITY_VIEW_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; public static final String ENTITY_VIEW_CUSTOMER_ID_PROPERTY = CUSTOMER_ID_PROPERTY; public static final String ENTITY_VIEW_NAME_PROPERTY = DEVICE_NAME_PROPERTY; - public static final String ENTITY_VIEW_EDGE_ID_PROPERTY = "edge_id"; public static final String ENTITY_VIEW_BY_TENANT_AND_CUSTOMER_CF = "entity_view_by_tenant_and_customer"; public static final String ENTITY_VIEW_BY_TENANT_AND_CUSTOMER_AND_TYPE_CF = "entity_view_by_tenant_and_customer_and_type"; public static final String ENTITY_VIEW_BY_TENANT_AND_ENTITY_ID_CF = "entity_view_by_tenant_and_entity_id"; @@ -206,7 +204,6 @@ public class ModelConstants { public static final String ASSET_TYPE_PROPERTY = "type"; public static final String ASSET_LABEL_PROPERTY = "label"; public static final String ASSET_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY; - public static final String ASSET_EDGE_ID_PROPERTY = "edge_id"; public static final String ASSET_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "asset_by_tenant_and_search_text"; public static final String ASSET_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "asset_by_tenant_by_type_and_search_text"; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/AssetEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/AssetEntity.java index 56849bca5e..aaa60f0b64 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/AssetEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/AssetEntity.java @@ -25,7 +25,6 @@ import lombok.ToString; import org.thingsboard.server.common.data.asset.Asset; 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.dao.model.SearchTextEntity; import org.thingsboard.server.dao.model.type.JsonCodec; @@ -35,11 +34,10 @@ import java.util.UUID; import static org.thingsboard.server.dao.model.ModelConstants.ASSET_ADDITIONAL_INFO_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ASSET_COLUMN_FAMILY_NAME; import static org.thingsboard.server.dao.model.ModelConstants.ASSET_CUSTOMER_ID_PROPERTY; -import static org.thingsboard.server.dao.model.ModelConstants.ASSET_EDGE_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ASSET_LABEL_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ASSET_NAME_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ASSET_TENANT_ID_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ASSET_TYPE_PROPERTY; -import static org.thingsboard.server.dao.model.ModelConstants.ASSET_LABEL_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; @@ -64,10 +62,6 @@ public final class AssetEntity implements SearchTextEntity { @Column(name = ASSET_TYPE_PROPERTY) private String type; - @PartitionKey(value = 4) - @Column(name = ASSET_EDGE_ID_PROPERTY) - private UUID edgeId; - @Column(name = ASSET_NAME_PROPERTY) private String name; @@ -94,9 +88,6 @@ public final class AssetEntity implements SearchTextEntity { if (asset.getCustomerId() != null) { this.customerId = asset.getCustomerId().getId(); } - if (asset.getEdgeId() != null) { - this.edgeId = asset.getEdgeId().getId(); - } this.name = asset.getName(); this.type = asset.getType(); this.label = asset.getLabel(); @@ -127,14 +118,6 @@ public final class AssetEntity implements SearchTextEntity { this.customerId = customerId; } - public UUID getEdgeId() { - return edgeId; - } - - public void setEdgeId(UUID edgeId) { - this.edgeId = edgeId; - } - public String getName() { return name; } @@ -183,9 +166,6 @@ public final class AssetEntity implements SearchTextEntity { if (customerId != null) { asset.setCustomerId(new CustomerId(customerId)); } - if (edgeId != null) { - asset.setEdgeId(new EdgeId(edgeId)); - } asset.setName(name); asset.setType(type); asset.setLabel(label); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/DeviceEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/DeviceEntity.java index 284ba4f164..f99b03c220 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/DeviceEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/DeviceEntity.java @@ -25,14 +25,21 @@ import lombok.ToString; import org.thingsboard.server.common.data.Device; 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; import org.thingsboard.server.dao.model.SearchTextEntity; import org.thingsboard.server.dao.model.type.JsonCodec; import java.util.UUID; -import static org.thingsboard.server.dao.model.ModelConstants.*; +import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_ADDITIONAL_INFO_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_CUSTOMER_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_LABEL_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_NAME_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_TENANT_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_TYPE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; @Table(name = DEVICE_COLUMN_FAMILY_NAME) @EqualsAndHashCode @@ -55,10 +62,6 @@ public final class DeviceEntity implements SearchTextEntity { @Column(name = DEVICE_TYPE_PROPERTY) private String type; - @PartitionKey(value = 4) - @Column(name = DEVICE_EDGE_ID_PROPERTY) - private UUID edgeId; - @Column(name = DEVICE_NAME_PROPERTY) private String name; @@ -85,9 +88,6 @@ public final class DeviceEntity implements SearchTextEntity { if (device.getCustomerId() != null) { this.customerId = device.getCustomerId().getId(); } - if (device.getEdgeId() != null) { - this.edgeId = device.getEdgeId().getId(); - } this.name = device.getName(); this.type = device.getType(); this.label = device.getLabel(); @@ -118,14 +118,6 @@ public final class DeviceEntity implements SearchTextEntity { this.customerId = customerId; } - public UUID getEdgeId() { - return edgeId; - } - - public void setEdgeId(UUID edgeId) { - this.edgeId = edgeId; - } - public String getName() { return name; } @@ -174,9 +166,6 @@ public final class DeviceEntity implements SearchTextEntity { if (customerId != null) { device.setCustomerId(new CustomerId(customerId)); } - if (edgeId != null) { - device.setEdgeId(new EdgeId(edgeId)); - } device.setName(name); device.setType(type); device.setLabel(label); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/EntityViewEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/EntityViewEntity.java index 3aca4ff739..cc5fb9d844 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/EntityViewEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/EntityViewEntity.java @@ -29,7 +29,6 @@ import org.hibernate.annotations.Type; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.id.CustomerId; -import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.TenantId; @@ -42,7 +41,6 @@ import javax.persistence.Enumerated; import java.io.IOException; import java.util.UUID; -import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_EDGE_ID_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_TYPE_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_TYPE_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_TABLE_FAMILY_NAME; @@ -71,10 +69,6 @@ public class EntityViewEntity implements SearchTextEntity { @Column(name = DEVICE_TYPE_PROPERTY) private String type; - @PartitionKey(value = 4) - @Column(name = DEVICE_EDGE_ID_PROPERTY) - private UUID edgeId; - @Enumerated(EnumType.STRING) @Column(name = ENTITY_TYPE_PROPERTY) private EntityType entityType; @@ -121,9 +115,6 @@ public class EntityViewEntity implements SearchTextEntity { if (entityView.getCustomerId() != null) { this.customerId = entityView.getCustomerId().getId(); } - if (entityView.getEdgeId() != null) { - this.edgeId = entityView.getEdgeId().getId(); - } this.type = entityView.getType(); this.name = entityView.getName(); try { @@ -155,9 +146,6 @@ public class EntityViewEntity implements SearchTextEntity { if (customerId != null) { entityView.setCustomerId(new CustomerId(customerId)); } - if (edgeId != null) { - entityView.setEdgeId(new EdgeId(edgeId)); - } entityView.setType(type); entityView.setName(name); try { diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AssetEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AssetEntity.java index f11b13e87b..746c6df33f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AssetEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AssetEntity.java @@ -25,7 +25,6 @@ import org.thingsboard.server.common.data.UUIDConverter; import org.thingsboard.server.common.data.asset.Asset; 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.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.ModelConstants; @@ -38,11 +37,10 @@ import javax.persistence.Table; import static org.thingsboard.server.dao.model.ModelConstants.ASSET_COLUMN_FAMILY_NAME; import static org.thingsboard.server.dao.model.ModelConstants.ASSET_CUSTOMER_ID_PROPERTY; -import static org.thingsboard.server.dao.model.ModelConstants.ASSET_EDGE_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ASSET_LABEL_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ASSET_NAME_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ASSET_TENANT_ID_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ASSET_TYPE_PROPERTY; -import static org.thingsboard.server.dao.model.ModelConstants.ASSET_LABEL_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; @Data @@ -58,9 +56,6 @@ public final class AssetEntity extends BaseSqlEntity implements SearchTex @Column(name = ASSET_CUSTOMER_ID_PROPERTY) private String customerId; - @Column(name = ASSET_EDGE_ID_PROPERTY) - private String edgeId; - @Column(name = ASSET_NAME_PROPERTY) private String name; @@ -91,9 +86,6 @@ public final class AssetEntity extends BaseSqlEntity implements SearchTex if (asset.getCustomerId() != null) { this.customerId = UUIDConverter.fromTimeUUID(asset.getCustomerId().getId()); } - if (asset.getEdgeId() != null) { - this.edgeId = UUIDConverter.fromTimeUUID(asset.getEdgeId().getId()); - } this.name = asset.getName(); this.type = asset.getType(); this.label = asset.getLabel(); @@ -124,9 +116,6 @@ public final class AssetEntity extends BaseSqlEntity implements SearchTex if (customerId != null) { asset.setCustomerId(new CustomerId(UUIDConverter.fromString(customerId))); } - if (edgeId != null) { - asset.setEdgeId(new EdgeId(UUIDConverter.fromString(edgeId))); - } asset.setName(name); asset.setType(type); asset.setLabel(label); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceEntity.java index 06c496a1f4..0d10adcf2b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceEntity.java @@ -24,7 +24,6 @@ import org.hibernate.annotations.TypeDef; import org.thingsboard.server.common.data.Device; 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; import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.ModelConstants; @@ -48,9 +47,6 @@ public final class DeviceEntity extends BaseSqlEntity implements SearchT @Column(name = ModelConstants.DEVICE_CUSTOMER_ID_PROPERTY) private String customerId; - @Column(name = ModelConstants.DEVICE_EDGE_ID_PROPERTY) - private String edgeId; - @Column(name = ModelConstants.DEVICE_TYPE_PROPERTY) private String type; @@ -81,9 +77,6 @@ public final class DeviceEntity extends BaseSqlEntity implements SearchT if (device.getCustomerId() != null) { this.customerId = toString(device.getCustomerId().getId()); } - if (device.getEdgeId() != null) { - this.edgeId = toString(device.getEdgeId().getId()); - } this.name = device.getName(); this.type = device.getType(); this.label = device.getLabel(); @@ -110,9 +103,6 @@ public final class DeviceEntity extends BaseSqlEntity implements SearchT if (customerId != null) { device.setCustomerId(new CustomerId(toUUID(customerId))); } - if (edgeId != null) { - device.setEdgeId(new EdgeId(toUUID(edgeId))); - } device.setName(name); device.setType(type); device.setLabel(label); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/EntityViewEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/EntityViewEntity.java index 9ea75eb0c2..62a786f459 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/EntityViewEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/EntityViewEntity.java @@ -26,7 +26,6 @@ import org.hibernate.annotations.TypeDef; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.id.CustomerId; -import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.TenantId; @@ -66,9 +65,6 @@ public class EntityViewEntity extends BaseSqlEntity implements Searc @Column(name = ModelConstants.ENTITY_VIEW_CUSTOMER_ID_PROPERTY) private String customerId; - @Column(name = ModelConstants.ENTITY_VIEW_EDGE_ID_PROPERTY) - private String edgeId; - @Column(name = ModelConstants.DEVICE_TYPE_PROPERTY) private String type; @@ -111,9 +107,6 @@ public class EntityViewEntity extends BaseSqlEntity implements Searc if (entityView.getCustomerId() != null) { this.customerId = toString(entityView.getCustomerId().getId()); } - if (entityView.getEdgeId() != null) { - this.edgeId = toString(entityView.getEdgeId().getId()); - } this.type = entityView.getType(); this.name = entityView.getName(); try { @@ -151,9 +144,6 @@ public class EntityViewEntity extends BaseSqlEntity implements Searc if (customerId != null) { entityView.setCustomerId(new CustomerId(toUUID(customerId))); } - if (edgeId != null) { - entityView.setEdgeId(new EdgeId(toUUID(edgeId))); - } entityView.setType(type); entityView.setName(name); try { 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 a667092231..d0c37903a8 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 @@ -467,8 +467,8 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC return Futures.transform(ruleChains, new Function, TimePageData>() { @Nullable @Override - public TimePageData apply(@Nullable List ruleChain) { - return new TimePageData<>(ruleChain, pageLink); + public TimePageData apply(@Nullable List ruleChains) { + return new TimePageData<>(ruleChains, pageLink); } }, MoreExecutors.directExecutor()); } @@ -557,15 +557,6 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC ruleNodeDao.removeById(tenantId, entityId.getId()); } - private void createRelation(TenantId tenantId, EntityRelation relation) throws ExecutionException, InterruptedException { - log.debug("Creating relation: {}", relation); - relationService.saveRelation(tenantId, relation); - } - - private void deleteRelation(TenantId tenantId, EntityRelation relation) throws ExecutionException, InterruptedException { - log.debug("Deleting relation: {}", relation); - relationService.deleteRelation(tenantId, relation); - } private DataValidator ruleChainValidator = new DataValidator() { diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/CassandraRuleChainDao.java b/dao/src/main/java/org/thingsboard/server/dao/rule/CassandraRuleChainDao.java index f3916e8cb5..d636281c2a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/CassandraRuleChainDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/CassandraRuleChainDao.java @@ -96,7 +96,7 @@ public class CassandraRuleChainDao extends CassandraAbstractSearchTextDao> findRuleChainsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink) { log.debug("Try to find rule chains by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink); - ListenableFuture> relations = relationDao.findRelations(new TenantId(tenantId), new EdgeId(edgeId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE, EntityType.DASHBOARD, pageLink); + ListenableFuture> relations = relationDao.findRelations(new TenantId(tenantId), new EdgeId(edgeId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE, EntityType.RULE_CHAIN, pageLink); return Futures.transformAsync(relations, input -> { List> ruleChainFutures = new ArrayList<>(input.size()); for (EntityRelation relation : input) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetRepository.java index 0f00e8c969..7b30d070bb 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetRepository.java @@ -77,26 +77,4 @@ public interface AssetRepository extends CrudRepository { @Query("SELECT DISTINCT a.type FROM AssetEntity a WHERE a.tenantId = :tenantId") List findTenantAssetTypes(@Param("tenantId") String tenantId); - - @Query("SELECT a FROM AssetEntity a WHERE a.tenantId = :tenantId " + - "AND a.edgeId = :edgeId " + - "AND LOWER(a.searchText) LIKE LOWER(CONCAT(:textSearch, '%')) " + - "AND a.id > :idOffset ORDER BY a.id") - List findByTenantIdAndEdgeId(@Param("tenantId") String tenantId, - @Param("edgeId") String edgeId, - @Param("textSearch") String textSearch, - @Param("idOffset") String idOffset, - Pageable pageable); - - @Query("SELECT a FROM AssetEntity a WHERE a.tenantId = :tenantId " + - "AND a.edgeId = :edgeId AND a.type = :type " + - "AND LOWER(a.searchText) LIKE LOWER(CONCAT(:textSearch, '%')) " + - "AND a.id > :idOffset ORDER BY a.id") - List findByTenantIdAndEdgeIdAndType(@Param("tenantId") String tenantId, - @Param("edgeId") String edgeId, - @Param("type") String type, - @Param("textSearch") String textSearch, - @Param("idOffset") String idOffset, - Pageable pageable); - } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java index 88247e0f76..4d96bb936a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java @@ -15,19 +15,28 @@ */ package org.thingsboard.server.dao.sql.asset; +import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.MoreExecutors; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.PageRequest; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.common.data.page.TimePageLink; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.asset.AssetDao; import org.thingsboard.server.dao.model.sql.AssetEntity; +import org.thingsboard.server.dao.relation.RelationDao; import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; import org.thingsboard.server.dao.util.SqlDao; @@ -47,11 +56,15 @@ import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID_STR; */ @Component @SqlDao +@Slf4j public class JpaAssetDao extends JpaAbstractSearchTextDao implements AssetDao { @Autowired private AssetRepository assetRepository; + @Autowired + private RelationDao relationDao; + @Override protected Class getEntityClass() { return AssetEntity.class; @@ -141,27 +154,16 @@ public class JpaAssetDao extends JpaAbstractSearchTextDao im } @Override - public List findAssetsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TextPageLink pageLink) { - return DaoUtil.convertDataList(assetRepository - .findByTenantIdAndEdgeId( - fromTimeUUID(tenantId), - fromTimeUUID(edgeId), - Objects.toString(pageLink.getTextSearch(), ""), - pageLink.getIdOffset() == null ? NULL_UUID_STR : fromTimeUUID(pageLink.getIdOffset()), - PageRequest.of(0, pageLink.getLimit()))); - } - - - @Override - public List findAssetsByTenantIdAndEdgeIdAndType(UUID tenantId, UUID edgeId, String type, TextPageLink pageLink) { - return DaoUtil.convertDataList(assetRepository - .findByTenantIdAndEdgeIdAndType( - fromTimeUUID(tenantId), - fromTimeUUID(edgeId), - type, - Objects.toString(pageLink.getTextSearch(), ""), - pageLink.getIdOffset() == null ? NULL_UUID_STR : fromTimeUUID(pageLink.getIdOffset()), - PageRequest.of(0, pageLink.getLimit()))); + public ListenableFuture> findAssetsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink) { + log.debug("Try to find assets by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink); + ListenableFuture> relations = relationDao.findRelations(new TenantId(tenantId), new EdgeId(edgeId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE, EntityType.ASSET, pageLink); + return Futures.transformAsync(relations, input -> { + List> assetFutures = new ArrayList<>(input.size()); + for (EntityRelation relation : input) { + assetFutures.add(findByIdAsync(new TenantId(tenantId), relation.getTo().getId())); + } + return Futures.successfulAsList(assetFutures); + }, MoreExecutors.directExecutor()); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java index d8544ea4c4..16ff6f1b52 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java @@ -85,26 +85,4 @@ public interface DeviceRepository extends CrudRepository { List findDevicesByTenantIdAndCustomerIdAndIdIn(String tenantId, String customerId, List deviceIds); List findDevicesByTenantIdAndIdIn(String tenantId, List deviceIds); - - @Query("SELECT d FROM DeviceEntity d WHERE d.tenantId = :tenantId " + - "AND d.edgeId = :edgeId " + - "AND LOWER(d.searchText) LIKE LOWER(CONCAT(:searchText, '%')) " + - "AND d.id > :idOffset ORDER BY d.id") - List findByTenantIdAndEdgeId(@Param("tenantId") String tenantId, - @Param("edgeId") String edgeId, - @Param("searchText") String searchText, - @Param("idOffset") String idOffset, - Pageable pageable); - - @Query("SELECT d FROM DeviceEntity d WHERE d.tenantId = :tenantId " + - "AND d.edgeId = :edgeId " + - "AND d.type = :type " + - "AND LOWER(d.searchText) LIKE LOWER(CONCAT(:textSearch, '%')) " + - "AND d.id > :idOffset ORDER BY d.id") - List findByTenantIdAndEdgeIdAndType(@Param("tenantId") String tenantId, - @Param("edgeId") String edgeId, - @Param("type") String type, - @Param("textSearch") String textSearch, - @Param("idOffset") String idOffset, - Pageable pageable); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java index 046db07e65..9b68493532 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java @@ -15,7 +15,10 @@ */ package org.thingsboard.server.dao.sql.device; +import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.MoreExecutors; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.PageRequest; import org.springframework.data.repository.CrudRepository; @@ -25,11 +28,17 @@ import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.UUIDConverter; +import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.common.data.page.TimePageLink; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; +import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.device.DeviceDao; import org.thingsboard.server.dao.model.sql.DeviceEntity; +import org.thingsboard.server.dao.relation.RelationDao; import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; import org.thingsboard.server.dao.util.SqlDao; @@ -49,11 +58,15 @@ import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID_STR; */ @Component @SqlDao +@Slf4j public class JpaDeviceDao extends JpaAbstractSearchTextDao implements DeviceDao { @Autowired private DeviceRepository deviceRepository; + @Autowired + private RelationDao relationDao; + @Override protected Class getEntityClass() { return DeviceEntity.class; @@ -149,28 +162,16 @@ public class JpaDeviceDao extends JpaAbstractSearchTextDao return list; } - @Override - public List findDevicesByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TextPageLink pageLink) { - return DaoUtil.convertDataList( - deviceRepository.findByTenantIdAndEdgeId( - fromTimeUUID(tenantId), - fromTimeUUID(edgeId), - Objects.toString(pageLink.getTextSearch(), ""), - pageLink.getIdOffset() == null ? NULL_UUID_STR : fromTimeUUID(pageLink.getIdOffset()), - PageRequest.of(0, pageLink.getLimit()))); - } - - @Override - public List findDevicesByTenantIdAndEdgeIdAndType(UUID tenantId, UUID edgeId, String type, TextPageLink pageLink) { - return DaoUtil.convertDataList( - deviceRepository.findByTenantIdAndEdgeIdAndType( - fromTimeUUID(tenantId), - fromTimeUUID(edgeId), - type, - Objects.toString(pageLink.getTextSearch(), ""), - pageLink.getIdOffset() == null ? NULL_UUID_STR : fromTimeUUID(pageLink.getIdOffset()), - PageRequest.of(0, pageLink.getLimit()))); + public ListenableFuture> findDevicesByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink) { + log.debug("Try to find devices by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink); + ListenableFuture> relations = relationDao.findRelations(new TenantId(tenantId), new EdgeId(edgeId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE, EntityType.DEVICE, pageLink); + return Futures.transformAsync(relations, input -> { + List> deviceFutures = new ArrayList<>(input.size()); + for (EntityRelation relation : input) { + deviceFutures.add(findByIdAsync(new TenantId(tenantId), relation.getTo().getId())); + } + return Futures.successfulAsList(deviceFutures); + }, MoreExecutors.directExecutor()); } - } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/EntityViewRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/EntityViewRepository.java index d5b8293de6..bc246e7dcc 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/EntityViewRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/EntityViewRepository.java @@ -76,26 +76,4 @@ public interface EntityViewRepository extends CrudRepository findTenantEntityViewTypes(@Param("tenantId") String tenantId); - - @Query("SELECT e FROM EntityViewEntity e WHERE e.tenantId = :tenantId " + - "AND e.edgeId = :edgeId " + - "AND LOWER(e.searchText) LIKE LOWER(CONCAT(:searchText, '%')) " + - "AND e.id > :idOffset ORDER BY e.id") - List findByTenantIdAndEdgeId(@Param("tenantId") String tenantId, - @Param("edgeId") String edgeId, - @Param("searchText") String searchText, - @Param("idOffset") String idOffset, - Pageable pageable); - - @Query("SELECT e FROM EntityViewEntity e WHERE e.tenantId = :tenantId " + - "AND e.edgeId = :edgeId " + - "AND e.type = :type " + - "AND LOWER(e.searchText) LIKE LOWER(CONCAT(:searchText, '%')) " + - "AND e.id > :idOffset ORDER BY e.id") - List findByTenantIdAndEdgeIdAndType(@Param("tenantId") String tenantId, - @Param("edgeId") String edgeId, - @Param("type") String type, - @Param("searchText") String searchText, - @Param("idOffset") String idOffset, - Pageable pageable); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java index eed482fabd..a981fff90f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java @@ -15,20 +15,29 @@ */ package org.thingsboard.server.dao.sql.entityview; +import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.MoreExecutors; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.PageRequest; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.UUIDConverter; +import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.common.data.page.TimePageLink; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.entityview.EntityViewDao; import org.thingsboard.server.dao.model.sql.EntityViewEntity; +import org.thingsboard.server.dao.relation.RelationDao; import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; import org.thingsboard.server.dao.util.SqlDao; @@ -47,12 +56,16 @@ import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID_STR; */ @Component @SqlDao +@Slf4j public class JpaEntityViewDao extends JpaAbstractSearchTextDao implements EntityViewDao { @Autowired private EntityViewRepository entityViewRepository; + @Autowired + private RelationDao relationDao; + @Override protected Class getEntityClass() { return EntityViewEntity.class; @@ -140,30 +153,15 @@ public class JpaEntityViewDao extends JpaAbstractSearchTextDao findEntityViewsByTenantIdAndEdgeId(UUID tenantId, - UUID edgeId, - TextPageLink pageLink) { - return DaoUtil.convertDataList( - entityViewRepository.findByTenantIdAndEdgeId( - fromTimeUUID(tenantId), - fromTimeUUID(edgeId), - Objects.toString(pageLink.getTextSearch(), ""), - pageLink.getIdOffset() == null ? NULL_UUID_STR : fromTimeUUID(pageLink.getIdOffset()), - PageRequest.of(0, pageLink.getLimit()) - )); - } - - @Override - public List findEntityViewsByTenantIdAndEdgeIdAndType(UUID tenantId, UUID edgeId, String type, TextPageLink pageLink) { - return DaoUtil.convertDataList( - entityViewRepository.findByTenantIdAndEdgeIdAndType( - fromTimeUUID(tenantId), - fromTimeUUID(edgeId), - type, - Objects.toString(pageLink.getTextSearch(), ""), - pageLink.getIdOffset() == null ? NULL_UUID_STR : fromTimeUUID(pageLink.getIdOffset()), - PageRequest.of(0, pageLink.getLimit()) - )); + public ListenableFuture> findEntityViewsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink) { + log.debug("Try to find entity views by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink); + ListenableFuture> relations = relationDao.findRelations(new TenantId(tenantId), new EdgeId(edgeId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE, EntityType.ENTITY_VIEW, pageLink); + return Futures.transformAsync(relations, input -> { + List> entityViewFutures = new ArrayList<>(input.size()); + for (EntityRelation relation : input) { + entityViewFutures.add(findByIdAsync(new TenantId(tenantId), relation.getTo().getId())); + } + return Futures.successfulAsList(entityViewFutures); + }, MoreExecutors.directExecutor()); } - } diff --git a/dao/src/main/resources/sql/schema-entities-hsql.sql b/dao/src/main/resources/sql/schema-entities-hsql.sql index 3930f428f0..42f52a2184 100644 --- a/dao/src/main/resources/sql/schema-entities-hsql.sql +++ b/dao/src/main/resources/sql/schema-entities-hsql.sql @@ -42,7 +42,6 @@ CREATE TABLE IF NOT EXISTS asset ( id varchar(31) NOT NULL CONSTRAINT asset_pkey PRIMARY KEY, additional_info varchar, customer_id varchar(31), - edge_id varchar(31), name varchar(255), label varchar(255), search_text varchar(255), @@ -120,7 +119,6 @@ CREATE TABLE IF NOT EXISTS device ( id varchar(31) NOT NULL CONSTRAINT device_pkey PRIMARY KEY, additional_info varchar, customer_id varchar(31), - edge_id varchar(31), type varchar(255), name varchar(255), label varchar(255), @@ -247,7 +245,6 @@ CREATE TABLE IF NOT EXISTS entity_view ( entity_type varchar(255), tenant_id varchar(31), customer_id varchar(31), - edge_id varchar(31), type varchar(255), name varchar(255), keys varchar(10000000), diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 10746084ae..ffea8bdf39 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -42,7 +42,6 @@ CREATE TABLE IF NOT EXISTS asset ( id varchar(31) NOT NULL CONSTRAINT asset_pkey PRIMARY KEY, additional_info varchar, customer_id varchar(31), - edge_id varchar(31), name varchar(255), label varchar(255), search_text varchar(255), @@ -120,7 +119,6 @@ CREATE TABLE IF NOT EXISTS device ( id varchar(31) NOT NULL CONSTRAINT device_pkey PRIMARY KEY, additional_info varchar, customer_id varchar(31), - edge_id varchar(31), type varchar(255), name varchar(255), label varchar(255), @@ -247,7 +245,6 @@ CREATE TABLE IF NOT EXISTS entity_view ( entity_type varchar(255), tenant_id varchar(31), customer_id varchar(31), - edge_id varchar(31), type varchar(255), name varchar(255), keys varchar(10000000), diff --git a/ui/src/app/api/asset.service.js b/ui/src/app/api/asset.service.js index c9ba3b1775..86d06670c9 100644 --- a/ui/src/app/api/asset.service.js +++ b/ui/src/app/api/asset.service.js @@ -18,7 +18,7 @@ export default angular.module('thingsboard.api.asset', []) .name; /*@ngInject*/ -function AssetService($http, $q, customerService, userService) { +function AssetService($http, $q, $filter, customerService, userService) { var service = { getAsset: getAsset, @@ -307,9 +307,9 @@ function AssetService($http, $q, customerService, userService) { return deferred.promise; } - function unassignAssetFromEdge(assetId, ignoreErrors, config) { + function unassignAssetFromEdge(edgeId, assetId, ignoreErrors, config) { var deferred = $q.defer(); - var url = '/api/edge/asset/' + assetId; + var url = '/api/edge/' + edgeId + '/asset/' + assetId; if (!config) { config = {}; } @@ -325,24 +325,20 @@ function AssetService($http, $q, customerService, userService) { function getEdgeAssets(edgeId, pageLink, config, type) { var deferred = $q.defer(); var url = '/api/edge/' + edgeId + '/assets?limit=' + pageLink.limit; - if (angular.isDefined(pageLink.textSearch)) { - url += '&textSearch=' + pageLink.textSearch; - } if (angular.isDefined(pageLink.idOffset)) { - url += '&idOffset=' + pageLink.idOffset; - } - if (angular.isDefined(pageLink.textOffset)) { - url += '&textOffset=' + pageLink.textOffset; - } - if (angular.isDefined(type) && type.length) { - url += '&type=' + type; + url += '&offset=' + pageLink.idOffset; } $http.get(url, config).then(function success(response) { + if (pageLink.textSearch) { + response.data.data = $filter('filter')(response.data.data, {name: pageLink.textSearch}); + } + if (angular.isDefined(type) && type.length) { + response.data.data = $filter('filter')(response.data.data, {type: type}); + } deferred.resolve(response.data); }, function fail() { deferred.reject(); }); - return deferred.promise; } } diff --git a/ui/src/app/api/device.service.js b/ui/src/app/api/device.service.js index 292e507cd3..9cc4412e86 100644 --- a/ui/src/app/api/device.service.js +++ b/ui/src/app/api/device.service.js @@ -20,7 +20,7 @@ export default angular.module('thingsboard.api.device', [thingsboardTypes]) .name; /*@ngInject*/ -function DeviceService($http, $q, $window, userService, attributeService, customerService, types) { +function DeviceService($http, $q, $window, $filter, userService, attributeService, customerService, types) { var service = { assignDeviceToCustomer: assignDeviceToCustomer, @@ -373,9 +373,9 @@ function DeviceService($http, $q, $window, userService, attributeService, custom return deferred.promise; } - function unassignDeviceFromEdge(deviceId) { + function unassignDeviceFromEdge(edgeId, deviceId) { var deferred = $q.defer(); - var url = '/api/edge/device/' + deviceId; + var url = '/api/edge/' + edgeId + '/device/' + deviceId; $http.delete(url).then(function success(response) { deferred.resolve(response.data); }, function fail() { @@ -387,25 +387,20 @@ function DeviceService($http, $q, $window, userService, attributeService, custom function getEdgeDevices(edgeId, pageLink, config, type) { var deferred = $q.defer(); var url = '/api/edge/' + edgeId + '/devices?limit=' + pageLink.limit; - if (angular.isDefined(pageLink.textSearch)) { - url += '&textSearch=' + pageLink.textSearch; - } if (angular.isDefined(pageLink.idOffset)) { - url += '&idOffset=' + pageLink.idOffset; - } - if (angular.isDefined(pageLink.textOffset)) { - url += '&textOffset=' + pageLink.textOffset; - } - if (angular.isDefined(type) && type.length) { - url += '&type=' + type; + url += '&offset=' + pageLink.idOffset; } $http.get(url, config).then(function success(response) { + if (pageLink.textSearch) { + response.data.data = $filter('filter')(response.data.data, {name: pageLink.textSearch}); + } + if (angular.isDefined(type) && type.length) { + response.data.data = $filter('filter')(response.data.data, {type: type}); + } deferred.resolve(response.data); }, function fail() { deferred.reject(); }); - return deferred.promise; } - } diff --git a/ui/src/app/api/entity-view.service.js b/ui/src/app/api/entity-view.service.js index ed5abc5752..cef08001cd 100644 --- a/ui/src/app/api/entity-view.service.js +++ b/ui/src/app/api/entity-view.service.js @@ -20,7 +20,7 @@ export default angular.module('thingsboard.api.entityView', [thingsboardTypes]) .name; /*@ngInject*/ -function EntityViewService($http, $q, $window, userService, attributeService, customerService, types) { +function EntityViewService($http, $q, $window, $filter, userService, attributeService, customerService, types) { var service = { assignEntityViewToCustomer: assignEntityViewToCustomer, @@ -234,9 +234,9 @@ function EntityViewService($http, $q, $window, userService, attributeService, cu return deferred.promise; } - function unassignEntityViewFromEdge(entityViewId) { + function unassignEntityViewFromEdge(edgeId, entityViewId) { var deferred = $q.defer(); - var url = '/api/edge/entityView/' + entityViewId; + var url = '/api/edge/' + edgeId + '/entityView/' + entityViewId; $http.delete(url).then(function success(response) { deferred.resolve(response.data); }, function fail() { @@ -248,24 +248,20 @@ function EntityViewService($http, $q, $window, userService, attributeService, cu function getEdgeEntityViews(edgeId, pageLink, config, type) { var deferred = $q.defer(); var url = '/api/edge/' + edgeId + '/entityViews?limit=' + pageLink.limit; - if (angular.isDefined(pageLink.textSearch)) { - url += '&textSearch=' + pageLink.textSearch; - } if (angular.isDefined(pageLink.idOffset)) { - url += '&idOffset=' + pageLink.idOffset; - } - if (angular.isDefined(pageLink.textOffset)) { - url += '&textOffset=' + pageLink.textOffset; - } - if (angular.isDefined(type) && type.length) { - url += '&type=' + type; + url += '&offset=' + pageLink.idOffset; } $http.get(url, config).then(function success(response) { + if (pageLink.textSearch) { + response.data.data = $filter('filter')(response.data.data, {name: pageLink.textSearch}); + } + if (angular.isDefined(type) && type.length) { + response.data.data = $filter('filter')(response.data.data, {type: type}); + } deferred.resolve(response.data); }, function fail() { deferred.reject(); }); - return deferred.promise; } } diff --git a/ui/src/app/asset/asset.controller.js b/ui/src/app/asset/asset.controller.js index d863133d4f..27190f18eb 100644 --- a/ui/src/app/asset/asset.controller.js +++ b/ui/src/app/asset/asset.controller.js @@ -327,7 +327,7 @@ export function AssetController($rootScope, userService, assetService, customerS return assetService.getEdgeAssets(edgeId, pageLink, null, assetType); }; deleteAssetFunction = function (assetId) { - return assetService.unassignAssetFromEdge(assetId); + return assetService.unassignAssetFromEdge(edgeId, assetId); }; refreshAssetsParamsFunction = function () { return {"edgeId": edgeId, "topIndex": vm.topIndex}; @@ -630,7 +630,7 @@ export function AssetController($rootScope, userService, assetService, customerS .cancel($translate.instant('action.no')) .ok($translate.instant('action.yes')); $mdDialog.show(confirm).then(function () { - assetService.unassignAssetFromEdge(asset.id.id).then(function success() { + assetService.unassignAssetFromEdge(edgeId, asset.id.id).then(function success() { vm.grid.refreshList(); }); }); @@ -639,15 +639,15 @@ export function AssetController($rootScope, userService, assetService, customerS function unassignAssetsFromEdge($event, items) { var confirm = $mdDialog.confirm() .targetEvent($event) - .title($translate.instant('asset.unassign-assets-title', {count: items.selectedCount}, 'messageformat')) - .htmlContent($translate.instant('asset.unassign-assets-text')) - .ariaLabel($translate.instant('asset.unassign-asset')) + .title($translate.instant('asset.unassign-assets-from-edge-title', {count: items.selectedCount}, 'messageformat')) + .htmlContent($translate.instant('asset.unassign-assets-from-edge-text')) + .ariaLabel($translate.instant('asset.unassign-asset-from-edge')) .cancel($translate.instant('action.no')) .ok($translate.instant('action.yes')); $mdDialog.show(confirm).then(function () { var tasks = []; for (var id in items.selections) { - tasks.push(assetService.unassignAssetFromEdge(id)); + tasks.push(assetService.unassignAssetFromEdge(edgeId, id)); } $q.all(tasks).then(function () { vm.grid.refreshList(); diff --git a/ui/src/app/device/device.controller.js b/ui/src/app/device/device.controller.js index 93f8dad1c4..05f7b54226 100644 --- a/ui/src/app/device/device.controller.js +++ b/ui/src/app/device/device.controller.js @@ -360,7 +360,7 @@ export function DeviceController($rootScope, userService, deviceService, custome return deviceService.getEdgeDevices(edgeId, pageLink, null, deviceType); }; deleteDeviceFunction = function (deviceId) { - return deviceService.unassignDeviceFromEdge(deviceId); + return deviceService.unassignDeviceFromEdge(edgeId, deviceId); }; refreshDevicesParamsFunction = function () { return {"edgeId": edgeId, "topIndex": vm.topIndex}; @@ -679,7 +679,7 @@ export function DeviceController($rootScope, userService, deviceService, custome .cancel($translate.instant('action.no')) .ok($translate.instant('action.yes')); $mdDialog.show(confirm).then(function () { - deviceService.unassignDeviceFromEdge(device.id.id).then(function success() { + deviceService.unassignDeviceFromEdge(edgeId, device.id.id).then(function success() { vm.grid.refreshList(); }); }); @@ -688,15 +688,15 @@ export function DeviceController($rootScope, userService, deviceService, custome function unassignDevicesFromEdge($event, items) { var confirm = $mdDialog.confirm() .targetEvent($event) - .title($translate.instant('device.unassign-devices-title', {count: items.selectedCount}, 'messageformat')) - .htmlContent($translate.instant('device.unassign-devices-text')) - .ariaLabel($translate.instant('device.unassign-device')) + .title($translate.instant('device.unassign-devices-from-edge-title', {count: items.selectedCount}, 'messageformat')) + .htmlContent($translate.instant('device.unassign-devices-from-edge-text')) + .ariaLabel($translate.instant('device.unassign-device-from-edge')) .cancel($translate.instant('action.no')) .ok($translate.instant('action.yes')); $mdDialog.show(confirm).then(function () { var tasks = []; for (var id in items.selections) { - tasks.push(deviceService.unassignDeviceFromEdge(id)); + tasks.push(deviceService.unassignDeviceFromEdge(edgeId, id)); } $q.all(tasks).then(function () { vm.grid.refreshList(); diff --git a/ui/src/app/entity-view/entity-view.controller.js b/ui/src/app/entity-view/entity-view.controller.js index 9c5e7907f5..3d382346de 100644 --- a/ui/src/app/entity-view/entity-view.controller.js +++ b/ui/src/app/entity-view/entity-view.controller.js @@ -288,7 +288,7 @@ export function EntityViewController($rootScope, userService, entityViewService, return entityViewService.getEdgeEntityViews(edgeId, pageLink, null, entityViewType); }; deleteEntityViewFunction = function (entityViewId) { - return entityViewService.unassignEntityViewFromEdge(entityViewId); + return entityViewService.unassignEntityViewFromEdge(edgeId, entityViewId); }; refreshEntityViewsParamsFunction = function () { return {"edgeId": edgeId, "topIndex": vm.topIndex}; @@ -300,10 +300,7 @@ export function EntityViewController($rootScope, userService, entityViewService, }, name: function() { return $translate.instant('action.unassign') }, details: function() { return $translate.instant('entity-view.unassign-from-edge') }, - icon: "assignment_return", - isEnabled: function(entityView) { - return entityView && entityView.edgeId && entityView.edgeId.id !== types.id.nullUid; - } + icon: "assignment_return" } ); @@ -314,7 +311,7 @@ export function EntityViewController($rootScope, userService, entityViewService, }, name: function() { return $translate.instant('entity-view.unassign-entity-views') }, details: function(selectedCount) { - return $translate.instant('entity-view.unassign-entity-views-action-title', {count: selectedCount}, "messageformat"); + return $translate.instant('entity-view.unassign-entity-views-from-edge-action-title', {count: selectedCount}, "messageformat"); }, icon: "assignment_return" } @@ -581,15 +578,15 @@ export function EntityViewController($rootScope, userService, entityViewService, function unassignEntityViewsFromEdge($event, items) { var confirm = $mdDialog.confirm() .targetEvent($event) - .title($translate.instant('entity-view.unassign-entity-views-title', {count: items.selectedCount}, 'messageformat')) - .htmlContent($translate.instant('entity-view.unassign-entity-views-text')) - .ariaLabel($translate.instant('entity-view.unassign-entity-view')) + .title($translate.instant('entity-view.unassign-entity-views-from-edge-title', {count: items.selectedCount}, 'messageformat')) + .htmlContent($translate.instant('entity-view.unassign-entity-views-from-edge-text')) + .ariaLabel($translate.instant('entity-view.unassign-entity-view-from-edge')) .cancel($translate.instant('action.no')) .ok($translate.instant('action.yes')); $mdDialog.show(confirm).then(function () { var tasks = []; for (var id in items.selections) { - tasks.push(entityViewService.unassignEntityViewFromEdge(id)); + tasks.push(entityViewService.unassignEntityViewFromEdge(edgeId, id)); } $q.all(tasks).then(function () { vm.grid.refreshList(); @@ -612,7 +609,7 @@ export function EntityViewController($rootScope, userService, entityViewService, .cancel($translate.instant('action.no')) .ok($translate.instant('action.yes')); $mdDialog.show(confirm).then(function () { - entityViewService.unassignEntityViewFromEdge(entityView.id.id).then(function success() { + entityViewService.unassignEntityViewFromEdge(edgeId, entityView.id.id).then(function success() { vm.grid.refreshList(); }); }); diff --git a/ui/src/app/locale/locale.constant-en_US.json b/ui/src/app/locale/locale.constant-en_US.json index 1fbb81066e..1688b59692 100644 --- a/ui/src/app/locale/locale.constant-en_US.json +++ b/ui/src/app/locale/locale.constant-en_US.json @@ -291,9 +291,12 @@ "unassign-from-edge": "Unassign from edge", "assign-to-edge": "Assign to edge", "assign-to-edge-text": "Please select the edge to assign the asset(s)", + "unassign-asset-from-edge": "Unassign asset", "unassign-asset-from-edge-title": "Are you sure you want to unassign the asset '{{assetName}}'?", "unassign-asset-from-edge-text": "After the confirmation the asset will be unassigned and won't be accessible by the edge.", - "unassign-assets-from-edge-action-title": "Unassign { count, plural, 1 {1 asset} other {# assets} } from edge" + "unassign-assets-from-edge-action-title": "Unassign { count, plural, 1 {1 asset} other {# assets} } from edge", + "unassign-assets-from-edge-title": "Are you sure you want to unassign { count, plural, 1 {1 asset} other {# assets} }?", + "unassign-assets-from-edge-text": "After the confirmation all selected assets will be unassigned and won't be accessible by the edge." }, "attribute": { "attributes": "Attributes", @@ -748,7 +751,10 @@ "assign-to-edge-text": "Please select the edge to assign the device(s)", "unassign-device-from-edge-title": "Are you sure you want to unassign the device '{{deviceName}}'?", "unassign-device-from-edge-text": "After the confirmation the device will be unassigned and won't be accessible by the edge.", - "unassign-devices-from-edge-action-title": "Unassign { count, plural, 1 {1 device} other {# devices} } from edge" + "unassign-devices-from-edge-action-title": "Unassign { count, plural, 1 {1 device} other {# devices} } from edge", + "unassign-device-from-edge": "Unassign device", + "unassign-devices-from-edge-title": "Are you sure you want to unassign { count, plural, 1 {1 device} other {# devices} }?", + "unassign-devices-from-edge-text": "After the confirmation all selected devices will be unassigned and won't be accessible by the edge." }, "dialog": { "close": "Close dialog" @@ -1054,7 +1060,10 @@ "assign-to-edge-text": "Please select the edge to assign the entity view(s)", "unassign-entity-view-from-edge-title": "Are you sure you want to unassign the entity view '{{entityViewName}}'?", "unassign-entity-view-from-edge-text": "After the confirmation the entity view will be unassigned and won't be accessible by the edge.", - "unassign-entity-views-from-edge-action-title": "Unassign { count, plural, 1 {1 entity view} other {# entity views} } from edge" + "unassign-entity-views-from-edge-action-title": "Unassign { count, plural, 1 {1 entity view} other {# entity views} } from edge", + "unassign-entity-view-from-edge": "Unassign entity view", + "unassign-entity-views-from-edge-title": "Are you sure you want to unassign { count, plural, 1 {1 entity view} other {# entity views} }?", + "unassign-entity-views-from-edge-text": "After the confirmation all selected entity views will be unassigned and won't be accessible by the edge." }, "event": { "event-type": "Event type", @@ -1580,7 +1589,7 @@ "invalid-rulechain-type-error": "Unable to import rule chain: Invalid rule chain type. Expected type is {{expectedRuleChainType}}.", "set-default-edge": "Make edge rule chain default", "set-default-edge-title": "Are you sure you want to make the edge rule chain '{{ruleChainName}}' default?", - "set-default-edge-text": "After the confirmation the edge rule chain will be added to default list and handle all incoming transport messages.", + "set-default-edge-text": "After the confirmation the edge rule chain will be added to default list and assigned to newly created edge(s).", "remove-default-edge": "Remove edge rule chain from defaults", "remove-default-edge-title": "Are you sure you want to remove the edge rule chain '{{ruleChainName}}' from default list?", "remove-default-edge-text": "After the confirmation the edge rule chain will stop handling all incoming transport messages." From bf0b7306022de9c0b0d16da9fb59fca4de108973 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Tue, 2 Jun 2020 12:48:24 +0300 Subject: [PATCH 26/52] License fix --- .../org/thingsboard/server/dao/entityview/EntityViewDao.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.java b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.java index cd9501a70b..44dfd81125 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.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, From 2748a755c02f6e7e86186e4fc3764e815ffd23d7 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Tue, 2 Jun 2020 15:57:31 +0300 Subject: [PATCH 27/52] Code cleanup --- .../thingsboard/server/dao/edge/CassandraEdgeDao.java | 8 ++++---- .../java/org/thingsboard/server/dao/edge/EdgeDao.java | 10 ++++------ .../thingsboard/server/dao/edge/EdgeServiceImpl.java | 9 +++------ .../thingsboard/server/dao/sql/edge/JpaEdgeDao.java | 8 ++++---- 4 files changed, 15 insertions(+), 20 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/CassandraEdgeDao.java b/dao/src/main/java/org/thingsboard/server/dao/edge/CassandraEdgeDao.java index 75fabb33bf..fbe44c2267 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/CassandraEdgeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/CassandraEdgeDao.java @@ -110,8 +110,8 @@ public class CassandraEdgeDao extends CassandraAbstractSearchTextDao> findEdgesByTenantIdAndRuleChainId(UUID tenantId, UUID ruleChainId, TimePageLink pageLink) { - log.debug("Try to find edges by tenantId [{}], ruleChainId [{}] and pageLink [{}]", tenantId, ruleChainId, pageLink); + public ListenableFuture> findEdgesByTenantIdAndRuleChainId(UUID tenantId, UUID ruleChainId) { + log.debug("Try to find edges by tenantId [{}], ruleChainId [{}]", tenantId, ruleChainId); ListenableFuture> relations = relationDao.findAllByToAndType(new TenantId(tenantId), new RuleChainId(ruleChainId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE); return Futures.transformAsync(relations, input -> { List> edgeFutures = new ArrayList<>(input.size()); @@ -123,8 +123,8 @@ public class CassandraEdgeDao extends CassandraAbstractSearchTextDao> findEdgesByTenantIdAndDashboardId(UUID tenantId, UUID dashboardId, TimePageLink pageLink) { - log.debug("Try to find edges by tenantId [{}], dashboardId [{}] and pageLink [{}]", tenantId, dashboardId, pageLink); + public ListenableFuture> findEdgesByTenantIdAndDashboardId(UUID tenantId, UUID dashboardId) { + log.debug("Try to find edges by tenantId [{}], dashboardId [{}]", tenantId, dashboardId); ListenableFuture> relations = relationDao.findAllByToAndType(new TenantId(tenantId), new DashboardId(dashboardId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE); return Futures.transformAsync(relations, input -> { List> edgeFutures = new ArrayList<>(input.size()); diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java index adc844f14e..f9e561b0f4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java @@ -127,22 +127,20 @@ public interface EdgeDao extends Dao { Optional findByRoutingKey(UUID tenantId, String routingKey); /** - * Find edges by tenantId, ruleChainId and page link. + * Find edges by tenantId and ruleChainId. * * @param tenantId the tenantId * @param ruleChainId the ruleChainId - * @param pageLink the page link * @return the list of rule chain objects */ - ListenableFuture> findEdgesByTenantIdAndRuleChainId(UUID tenantId, UUID ruleChainId, TimePageLink pageLink); + ListenableFuture> findEdgesByTenantIdAndRuleChainId(UUID tenantId, UUID ruleChainId); /** - * Find edges by tenantId, dashboardId and page link. + * Find edges by tenantId and dashboardId. * * @param tenantId the tenantId * @param dashboardId the dashboardId - * @param pageLink the page link * @return the list of rule chain objects */ - ListenableFuture> findEdgesByTenantIdAndDashboardId(UUID tenantId, UUID dashboardId, TimePageLink pageLink); + ListenableFuture> findEdgesByTenantIdAndDashboardId(UUID tenantId, UUID dashboardId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java index 25c83ed031..c7b1d87ab9 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java @@ -464,10 +464,7 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic case DataConstants.ENTITY_DELETED: case DataConstants.ENTITY_CREATED: case DataConstants.ENTITY_UPDATED: - Edge edge = mapper.readValue(tbMsg.getData(), Edge.class); - if (edge != null) { - pushEventToEdge(tenantId, edge.getId(), EdgeQueueEntityType.EDGE, tbMsg, callback); - } + // TODO: voba - handle properly edge creation break; default: log.warn("Unsupported msgType [{}], tbMsg [{}]", tbMsg.getType(), tbMsg); @@ -693,7 +690,7 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic Validator.validateId(tenantId, "Incorrect tenantId " + tenantId); Validator.validateId(ruleChainId, "Incorrect ruleChainId " + ruleChainId); Validator.validatePageLink(pageLink, "Incorrect page link " + pageLink); - ListenableFuture> edges = edgeDao.findEdgesByTenantIdAndRuleChainId(tenantId.getId(), ruleChainId.getId(), pageLink); + ListenableFuture> edges = edgeDao.findEdgesByTenantIdAndRuleChainId(tenantId.getId(), ruleChainId.getId()); return Futures.transform(edges, new Function, TimePageData>() { @Nullable @@ -710,7 +707,7 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic Validator.validateId(tenantId, "Incorrect tenantId " + tenantId); Validator.validateId(dashboardId, "Incorrect dashboardId " + dashboardId); Validator.validatePageLink(pageLink, "Incorrect page link " + pageLink); - ListenableFuture> edges = edgeDao.findEdgesByTenantIdAndDashboardId(tenantId.getId(), dashboardId.getId(), pageLink); + ListenableFuture> edges = edgeDao.findEdgesByTenantIdAndDashboardId(tenantId.getId(), dashboardId.getId()); return Futures.transform(edges, new Function, TimePageData>() { @Nullable diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java index d89e085355..6a18a4e039 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java @@ -148,8 +148,8 @@ public class JpaEdgeDao extends JpaAbstractSearchTextDao imple } @Override - public ListenableFuture> findEdgesByTenantIdAndRuleChainId(UUID tenantId, UUID ruleChainId, TimePageLink pageLink) { - log.debug("Try to find edges by tenantId [{}], ruleChainId [{}] and pageLink [{}]", tenantId, ruleChainId, pageLink); + public ListenableFuture> findEdgesByTenantIdAndRuleChainId(UUID tenantId, UUID ruleChainId) { + log.debug("Try to find edges by tenantId [{}], ruleChainId [{}]", tenantId, ruleChainId); ListenableFuture> relations = relationDao.findAllByToAndType(new TenantId(tenantId), new RuleChainId(ruleChainId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE); return Futures.transformAsync(relations, input -> { List> edgeFutures = new ArrayList<>(input.size()); @@ -161,8 +161,8 @@ public class JpaEdgeDao extends JpaAbstractSearchTextDao imple } @Override - public ListenableFuture> findEdgesByTenantIdAndDashboardId(UUID tenantId, UUID dashboardId, TimePageLink pageLink) { - log.debug("Try to find edges by tenantId [{}], dashboardId [{}] and pageLink [{}]", tenantId, dashboardId, pageLink); + public ListenableFuture> findEdgesByTenantIdAndDashboardId(UUID tenantId, UUID dashboardId) { + log.debug("Try to find edges by tenantId [{}], dashboardId [{}]", tenantId, dashboardId); ListenableFuture> relations = relationDao.findAllByToAndType(new TenantId(tenantId), new DashboardId(dashboardId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE); return Futures.transformAsync(relations, input -> { List> edgeFutures = new ArrayList<>(input.size()); From 36997c5c3f422c8f200f008aba06c7d339a51831 Mon Sep 17 00:00:00 2001 From: deaflynx Date: Tue, 2 Jun 2020 19:09:34 +0300 Subject: [PATCH 28/52] Added german locale for edge --- ui/src/app/locale/locale.constant-de_DE.json | 170 ++++++++++++++++++- ui/src/app/locale/locale.constant-en_US.json | 2 +- 2 files changed, 167 insertions(+), 5 deletions(-) diff --git a/ui/src/app/locale/locale.constant-de_DE.json b/ui/src/app/locale/locale.constant-de_DE.json index 1f8b3b6fbe..90782ddf44 100644 --- a/ui/src/app/locale/locale.constant-de_DE.json +++ b/ui/src/app/locale/locale.constant-de_DE.json @@ -183,6 +183,8 @@ "filter-type-entity-view-type": "Entitätsansichtstyp", "filter-type-entity-view-type-description": "Entitätsansichten vom Typ '{{entityView}}'", "filter-type-entity-view-type-and-name-description": "Entitätsansichten vom Typ '{{entityView}}' und Name beginnend mit '{{prefix}}'", + "filter-type-edge-type": "Randtyp", + "filter-type-edge-type-description": "Rand vom Typ '{{edgeType}}'", "filter-type-relations-query": "Beziehungsabfrage", "filter-type-relations-query-description": "{{entities}} mit {{relationType}} Beziehung {{direction}} {{rootEntity}}", "filter-type-asset-search-query": "Objektabfrage", @@ -191,6 +193,8 @@ "filter-type-device-search-query-description": "Geräte vom Typ {{deviceTypes}} mit {{relationType}} Beziehung {{direction}} {{rootEntity}}", "filter-type-entity-view-search-query": "Entitätsansichtsabfrage", "filter-type-entity-view-search-query-description": "Entitätsansichten vom Typ {{entityViewTypes}} mit {{relationType}} Beziehung {{direction}} {{rootEntity}}", + "filter-type-edge-search-query": "Randabfrage", + "filter-type-edge-search-query-description": "Rand vom Typ {{edgeTypes}} mit {{relationType}} Beziehung {{direction}} {{rootEntity}}", "entity-filter": "Entitätsfilter", "resolve-multiple": "Als mehrere Entitäten auflösen", "filter-type": "Filtertyp", @@ -269,7 +273,14 @@ "no-assets-matching": "Es wurden keine zu '{{entity}}' passenden Objekte gefunden.", "asset-required": "Objekt ist erforderlich", "name-starts-with": "Name des Objekts beginnt mit", - "label": "Bezeichnung" + "label": "Bezeichnung", + "assign-asset-to-edge": "Objekte dem Rand zuordnen", + "assign-asset-to-edge-text":"Bitte wählen Sie die Objekte aus, die dem Rand zugeordnet werden sollen", + "unassign-from-edge": "Randzuordnung aufheben", + "assign-to-edge": "Einem Rand zuordnen", + "unassign-asset-from-edge-title": "Sind Sie sicher, dass Sie die Zuordnung für das Objekt '{{assetName}}' aufheben möchten?", + "unassign-asset-from-edge-text": "Nach Bestätigung wird die Zuordnung des Objekts aufgehoben und es ist für den Kunden nicht mehr zugänglich.", + "unassign-assets-from-edge-action-title": "Rand { count, plural, 1 {1 Objektzuordnung} other {# Objektzuordnungen} } aufheben" }, "attribute": { "attributes": "Eigenschaften", @@ -317,6 +328,8 @@ "type-credentials-updated": "Anmeldeinformationen wurden aktualisiert", "type-assigned-to-customer": "Kunden Zuordnung", "type-unassigned-from-customer": "Kunden Zuordnung aufgehoben", + "type-assigned-to-edge": "Rand Zuordnung", + "type-unassigned-from-edge": "Rand Zuordnung aufgehoben", "type-activated": "Aktiviert", "type-suspended": "Ausgesetzt", "type-credentials-read": "Anmeldeinformationen gelesen", @@ -380,6 +393,7 @@ "public-devices": "Öffentliche Geräte", "public-assets": "Öffentliche Objekte", "public-entity-views": "Öffentliche Entitätsansichten", + "public-edges": "Öffentliche Rand", "add": "Kunde hinzufügen", "delete": "Kunde löschen", "manage-customer-users": "Kundenbenutzer verwalten", @@ -388,7 +402,9 @@ "manage-public-devices": "Öffentliche Geräte verwalten", "manage-public-dashboards": "Öffentliche Dashboards verwalten", "manage-customer-assets": "Kundenobjekte verwalten", + "manage-customer-edges": "Randobjekte verwalten", "manage-public-assets": "Öffentliche Objekte verwalten", + "manage-public-edges": "Öffentliche Rand verwalten", "add-customer-text": "Neuen Kunden hinzufügen", "no-customers-text": "Keine Kunden gefunden", "customer-details": "Kundendetails", @@ -413,6 +429,7 @@ "customer-required": "Kunde ist erforderlich", "select-default-customer": "Wählen Sie den Standardkunden aus.", "default-customer": "Standardkunde", + "edges": "Kunden Rand", "default-customer-required": "Ein Standardkunde ist erforderlich, um das Dashboard auf Mandantenebene zu testen." }, "datetime": { @@ -560,7 +577,21 @@ "show-details": "Details anzeigen", "hide-details": "Details ausblenden", "select-state": "Soll-Zustand auswählen", - "state-controller": "Zustandssteuerung" + "state-controller": "Zustandssteuerung", + "manage-assigned-edges": "Zugeordnete Rand verwalten", + "unassign-dashboard-from-edge-text": "Nach der Bestätigung wird die Zuordnung des Dashboards aufgehoben und es ist für der Rand nicht mehr zugänglich.", + "assigned-edges": "Zugeordnete Rand", + "unassign-from-edge": "Zuordnung von Rand aufheben", + "unassign-dashboards-from-edge-action-title": "Zuordnung { count, plural, 1 {1 Dashboard} other {# Dashboards} } vom Rand aufheben", + "unassign-dashboards-from-edge-text": "Nach der Bestätigung wird die Zuordnung aller ausgewählten Dashboards aufgehoben und sie sind für den Rand nicht mehr zugänglich.", + "assign-dashboard-to-edge": "Dashboard(s) dem Rand zuordnen", + "assign-dashboard-to-edge-text": "Bitte wählen Sie die Dashboards aus, die Sie dem Rand zuordnen möchten", + "assign-dashboards-to-edge-text": "Zuordnen { count, plural, 1 {1 Dashboard} other {# Dashboards} } zum Rand", + "unassign-dashboards-from-edge-action-text": "Zuordnung { count, plural, 1 {1 Dashboard} other {# Dashboards} } vom Rand aufheben", + "assign-to-edges": "Dashboard(s) den Rand zuordnen", + "assign-to-edges-text": "Bitte wählen Sie den Rand aus, dem die Dashboards zugeordnet werden sollen", + "unassign-from-edges": "Zuordnung von Dashboard(s) zum Rand aufheben", + "unassign-from-edges-text": "Bitte wählen Sie die Rand aus, für die die Zuordnung von Dashboard(s) aufgehoben werden soll" }, "datakey": { "settings": "Einstellungen", @@ -691,11 +722,92 @@ "is-gateway": "Ist ein Gateway", "public": "Öffentlich", "device-public": "Gerät ist öffentlich", - "select-device": "Gerät auswählen" + "select-device": "Gerät auswählen", + "assign-device-to-edge": "Dashboard(s) dem Gerät zuordnen", + "assign-device-to-edge-text":"Bitte wählen Sie die Geräte aus, die Sie dem Rand zuordnen möchten", + "unassign-from-edge": "Zuordnung von Rand aufheben", + "assign-to-edge": "Einem Rand zuordnen", + "assign-to-edge-text": "Bitte wählen Sie die Geräte aus, die Sie dem Rand zuordnen möchten", + "unassign-device-from-edge-title": "Sind Sie sicher, dass Sie die Zuordnung zum Gerät '{{deviceName}}' wirklich aufheben möchten?", + "unassign-device-from-edge-text": "Nach der Bestätigung ist das Gerät nicht zugeordnet und für den Kunden nicht zugänglich.", + "unassign-devices-from-edge-action-title": "Zuordnung { count, plural, 1 {1 Gerät} other {# Geräte} } vom Rand aufheben", + "unassign-device-from-edge": "Nicht zugeordnete Geräte", + "unassign-devices-from-edge-title": "Sind Sie sicher, dass Sie { count, plural, 1 {1 Gerät} other {# Geräte} } nicht mehr zuordnen möchten?", + "unassign-devices-from-edge-text": "Nach der Bestätigung werden alle ausgewählten Geräte nicht zugewiesen und sind für den Rand nicht zugänglich." }, "dialog": { "close": "Dialog schließen" }, + "edge": { + "edge": "Rand", + "edges": "Rand", + "management": "Rand verwalten", + "no-edges-matching": "Keine passenden Rand '{{entity}}' gefunden.", + "add": "Rand hinzufügen", + "view": "Rand anzeigen", + "no-edges-text": "Kein Rand gefunden.", + "edge-details": "Details der Rand", + "add-edge-text": "Neue Rand hinzufügen", + "delete": "Rand löschen", + "delete-edges": "Rand löschen", + "delete-edge-title": "Möchten Sie des Rands wirklich löschen '{{edgeName}}'?", + "delete-edge-text": "Seien Sie vorsichtig, nach der Bestätigung werden der Rand und alle zugehörigen Daten nicht wiederhergestellt.", + "delete-edges-title": "Sind Sie sicher, dass Sie die Rand löschen möchten { count, plural, 1 {1 Rand} other {# Rand} }?", + "delete-edges-action-title": "Löschen { count, plural, 1 {1 Rand} other {# Rand} }", + "delete-edges-text": "Vorsicht, nach Bestätigung werden alle ausgewählten Rand entfernt und alle zugehörigen Daten werden nicht wiederhergestellt.", + "name": "Name", + "name-required": "Name ist erforderlich.", + "description": "Beschreibung", + "events": "Ereignisse", + "details": "Details", + "copy-id": "Regelketten-ID kopieren", + "id-copied-message": "Regelketten-ID wurde in die Zwischenablage kopiert", + "permissions": "Berechtigungen", + "edge-required": "Rand ist erforderlich.", + "edge-type": "Randtyp", + "edge-type-required": "Randtyp ist erforderlich.", + "select-edge-type": "Randtyp auswählen", + "assign-to-customer": "Einem Kunden zuordnen", + "assign-to-customer-text": "Bitte wählen Sie den Kunden aus, dem die Rand zugeordnet werden sollen", + "assign-edge-to-customer": "Rand dem Kunden zuordnen", + "assign-edge-to-customer-text": "Bitte wählen Sie die Rand aus, die dem Kunden zugeordnet werden sollen", + "assigned-to-customer": "Kunden Zuordnung", + "unassign-from-customer": "Kunden Zuordnung aufgehoben", + "assign-edges-text": "{ count, plural, 1 {1 Gerät} other {# Geräte} } dem Rand zuordnen", + "unassign-edge-title": "Sind Sie sicher, dass Sie die Zuordnung zum Rand '{{edgeName}}' wirklich aufheben möchten?", + "unassign-edge-text": "Nach der Bestätigung ist der Rand nicht zugeordnet und für den Kunden nicht zugänglich.", + "make-public": "Rand öffentlich machen", + "make-public-edge-title": "Sind Sie sicher, dass Sie der Rand '{{edgeName}}' öffentlich machen möchten?", + "make-public-edge-text": "Nach Bestätigung wird der Rabd und alle zugehörigen Daten anderen zugänglich gemacht.", + "make-private": "Rand privat machen", + "public": "Öffentlich", + "make-private-edge-title": "Sind Sie sicher, dass Sie der Rand '{{edgeName}}' privat machen möchten?", + "make-private-edge-text": "Nach der Bestätigung werden der Rand und dessen Daten privat und sind für andere nicht mehr zugänglich.", + "import": "Rand importieren", + "label": "Bezeichnung", + "assign-new-edge": "Neue Rand zuordnen", + "manage-edge-dashboards": "Rand-Dashboards verwalten", + "unassign-from-edge": "Zuordnung zum Rand aufheben", + "dashboards": "Rand Dashboards", + "manage-edge-rulechains": "Randregelkette verwalten", + "rulechains": "Rand Regelketten", + "rulechain": "Rand Regelkette", + "edge-key": "Rand Schlüssel", + "copy-edge-key": "Rand Schlüssel kopieren", + "edge-key-copied-message": "Rand Schlüssel wurde in die Zwischenablage kopiert", + "edge-secret": "Rand Geheimnis", + "copy-edge-secret": "Rand Geheimnis kopieren", + "edge-secret-copied-message": "Rand Geheimnis wurde in die Zwischenablage kopiert", + "manage-edge-assets": "Rand-Objekte verwalten", + "manage-edge-devices": "Rand-Geräte verwalten", + "manage-edge-entity-views": "Rand-Entitätsansichten verwalten", + "assets": "Rand Objekte", + "devices": "Objekte Geräte", + "entity-views": "Objekte Entitätsansichten", + "set-root-rule-chain-text": "Bitte wählen Sie die Regelkette zur Wurzel rule chain für die Rand", + "set-root-rule-chain-to-edges": "Regelkette zur Wurzel machen für die Rand", + "set-root-rule-chain-to-edges-text": "Die Regelkette zur Wurzel für { count, plural, 1 {1 Rand} other {# Rand} } machen" + }, "error": { "unable-to-connect": "Es konnte keine Verbindung zum Server hergestellt werden! Bitte überprüfen Sie Ihre Internetverbindung.", "unhandled-error-code": "Unbehandelter Fehlercode: {{errorCode}}", @@ -789,6 +901,10 @@ "type-rulenodes": "Regelknoten", "list-of-rulenodes": "{ count, plural, 1 {Ein Regelknoten} other {Liste von # Regelknoten} }", "rulenode-name-starts-with": "Regelknoten beginnend mit '{{prefix}}'", + "type-edge": "Randtyp", + "type-edges": "Randtyp", + "list-of-edges": "{ count, plural, 1 {1 Rand} other {# Rand} }", + "edge-name-starts-with": "Rand beginnend mit '{{prefix}}'", "type-current-customer": "Aktueller Kunde", "search": "Entitäten suchen", "selected-entities": "{ count, plural, 1 {Entität} other {# Entitäten} } ausgewählt", @@ -861,6 +977,17 @@ "entity-view-types": "Entitätsansichtstypen", "name": "Name", "name-required": "Name ist erforderlich.", + "assign-entity-view-to-edge": "Entitätsansicht dem Rand zuordnen", + "assign-entity-view-to-edge-text":"Bitte wählen Sie die Entitätsansicht aus, die dem Rand zugeordnet werden sollen", + "unassign-from-edge": "Randzuordnung aufheben", + "assign-to-edge": "Einem Rand zuordnen", + "assign-to-edge-text": "Bitte wählen Sie die Entitätsansichte aus, die dem Rand zugeordnet werden sollen", + "unassign-entity-view-from-edge-title": "Sind Sie sicher, dass Sie die Zuordnung für Entitätsansicht '{{entityViewName}}' aufheben möchten?", + "unassign-entity-view-from-edge-text": "Nach Bestätigung wird die Zuordnung des Entitätsansichts aufgehoben und es ist für den Kunden nicht mehr zugänglich.", + "unassign-entity-views-from-edge-action-title": "Rand { count, plural, 1 {1 Entitätsansicht} other {# Entitätsansichte} } aufheben", + "unassign-entity-view-from-edge": "Entitätsansichtzuordnung aufheben", + "unassign-entity-views-from-edge-title": "Sind Sie sicher, dass Sie { count, plural, 1 {1 Entitätsansicht} other {# Entitätsansichte} } nicht mehr zuordnen möchten?", + "unassign-entity-views-from-edge-text": "Nach der Bestätigung werden alle ausgewählten Entitätsansicht nicht zugewiesen und sind für den Rand nicht zugänglich.", "description": "Beschreibung", "events": "Ereignisse", "details": "Details", @@ -1224,6 +1351,8 @@ "rulechain": { "rulechain": "Regelkette", "rulechains": "Regelketten", + "system-rulechains": "Systemeregelketten", + "edge-rulechains": "Randregelketten", "root": "Wurzel", "delete": "Regelkette löschen", "name": "Name", @@ -1256,7 +1385,40 @@ "no-rulechains-matching": "Es wurden keine passenden Regelketten für '{{entity}}' gefunden.", "rulechain-required": "Regelkette ist erforderlich", "management": "Regelverwaltung", - "debug-mode": "Modus zur Fehlersuche" + "debug-mode": "Modus zur Fehlersuche", + "assign-rulechains": "Regelketten zuweisen", + "assign-new-rulechain": "Neues Regelkette zuweisen", + "delete-rulechains": "Regelketten löschen", + "default": "Standard", + "unassign-rulechain": "Nicht zugeordnete Regelkette", + "unassign-rulechains": "Nicht zugeordnete Regelketten", + "unassign-rulechain-title": "Möchten Sie die Zuordnung die Regelkette '{{ruleChainTitle}}' wirklich aufheben?", + "unassign-rulechains-title": "Sind Sie sicher, dass Sie die Zuordnung aufheben möchten { count, plural, 1 {1 Regelkette} other {# Regelketten} }?", + "manage-assigned-edges": "Zugeordnete Rand verwalten", + "unassign-rulechain-from-edge-text": "Nach der Bestätigung wird die Zuordnung aller ausgewählten Regelkette aufgehoben und sie sind für den Rand nicht mehr zugänglich.", + "assigned-edges": "Zugeordnete Rand", + "unassign-from-edge": "Randzuordnung aufheben", + "unassign-rulechains-from-edge-action-title": "Zuordnung { count, plural, 1 {1 Regelkette} other {# Regelketten} } vom Rand aufheben", + "unassign-rulechains-from-edge-text": "Nach der Bestätigung wird die Zuordnung aller ausgewählten Regelketten aufgehoben und sie sind für den Rand nicht mehr zugänglich.", + "assign-rulechains-to-edge-text": "Zuordnen { count, plural, 1 {1 Regelkette} other {# Regelketten} } zum Rand", + "assign-rulechain-to-edge": "Regelkette(n) dem Rand zuordnen", + "assign-rulechain-to-edge-text": "Bitte wählen Sie die Regelketten aus, die Sie dem Rand zuordnen möchten", + "unassign-rulechains-from-edge-action-text": "Zuordnung { count, plural, 1 {1 Regelkette} other {# Regelketten} } vom Rand aufheben", + "assign-to-edges": "Regelkette(n) den Rand zuordnen", + "assign-to-edges-text": "Zuordnung von Regelkette(n) zum Rand aufheben", + "unassign-from-edges": "Unassign Rule Chain(s) From Edges", + "unassign-from-edges-text": "Bitte wählen Sie die Rand aus, für die die Zuordnung von Regelkette(n) aufgehoben werden soll", + "assigned-to-edges": "Regelketten Zuordnung", + "set-default-root-edge": "Machen Sie Randregelkette zur Wurzel Standard", + "set-default-root-edge-rulechain-title": "Sind Sie sicher, dass Sie die Randregelkette '{{ruleChainName}}' zur Wurzel machen Standard?", + "set-default-root-edge-rulechain-text": "Nach der Bestätigung wird die Randregelkette zur Wurzel Standard und behandelt alle eingehenden Transportnachrichten.", + "invalid-rulechain-type-error": "Regelkette konnte nicht importiert werden: Ungültige Regelkettentyp. Erwarteter Typ ist {{expectedRuleChainType}}.", + "set-default-edge": "Machen Sie Regelkette Standard", + "set-default-edge-title": "Sind Sie sicher, dass Sie die Randregelkette '{{ruleChainName}}' machen Standard?", + "set-default-edge-text": "Nach der Bestätigung wird die Randregelkette für neu erstellte Rand vergeben.", + "remove-default-edge": "Randregelkette Standard entfernen", + "remove-default-edge-title": "Sind Sie sicher, dass Sie die Randregelkette '{{ruleChainName}}' aus der Standardliste entfernen?", + "remove-default-edge-text": "Nach der Bestätigung wird die Randregelkette nicht für neu erstellte Rand vergeben." }, "rulenode": { "details": "Details", diff --git a/ui/src/app/locale/locale.constant-en_US.json b/ui/src/app/locale/locale.constant-en_US.json index 1fbb81066e..0c5c4195a8 100644 --- a/ui/src/app/locale/locale.constant-en_US.json +++ b/ui/src/app/locale/locale.constant-en_US.json @@ -1583,7 +1583,7 @@ "set-default-edge-text": "After the confirmation the edge rule chain will be added to default list and handle all incoming transport messages.", "remove-default-edge": "Remove edge rule chain from defaults", "remove-default-edge-title": "Are you sure you want to remove the edge rule chain '{{ruleChainName}}' from default list?", - "remove-default-edge-text": "After the confirmation the edge rule chain will stop handling all incoming transport messages." + "remove-default-edge-text": "After the confirmation the edge rule chain will not be assigned for a newly created edges." }, "rulenode": { "details": "Details", From e59ac007bdbcd1672de8c8951e7fd6cffc4f99da Mon Sep 17 00:00:00 2001 From: deaflynx Date: Thu, 4 Jun 2020 10:16:08 +0300 Subject: [PATCH 29/52] Added french edge locale --- ui/src/app/locale/locale.constant-de_DE.json | 2 +- ui/src/app/locale/locale.constant-fr_FR.json | 174 ++++++++++++++++++- 2 files changed, 171 insertions(+), 5 deletions(-) diff --git a/ui/src/app/locale/locale.constant-de_DE.json b/ui/src/app/locale/locale.constant-de_DE.json index 90782ddf44..5e69ab6758 100644 --- a/ui/src/app/locale/locale.constant-de_DE.json +++ b/ui/src/app/locale/locale.constant-de_DE.json @@ -67,7 +67,7 @@ "general-settings": "Allgemeine Einstellungen", "outgoing-mail": "E-Mail Versand", "outgoing-mail-settings": "Konfiguration des Postausgangsservers", - "system-settings": "Systemeinstellungen", + "system-settings": "Systeminstellungen", "test-mail-sent": "Test E-Mail wurde erfolgreich versendet!", "base-url": "Basis-URL", "base-url-required": "Basis-URL ist erforderlich.", diff --git a/ui/src/app/locale/locale.constant-fr_FR.json b/ui/src/app/locale/locale.constant-fr_FR.json index 274063445f..fd25adced3 100644 --- a/ui/src/app/locale/locale.constant-fr_FR.json +++ b/ui/src/app/locale/locale.constant-fr_FR.json @@ -176,7 +176,7 @@ "entity-filter": "Filtre d'entité", "entity-filter-no-entity-matched": "Aucune entité correspondant au filtre spécifié n'a été trouvée.", "filter-type": "Type de filtre", - "filter-type-asset-search-query": "requête de recherche d'actifs", + "filter-type-asset-search-query": "Requête de recherche d'actifs", "filter-type-asset-search-query-description": "Actifs de types {{assetTypes}} ayant {{relationType}} relation {{direction}} {{rootEntity}}", "filter-type-asset-type": "type d'actif", "filter-type-asset-type-and-name-description": "Actifs de type '{{assetType}}' et dont le nom commence par '{{prefix}}'", @@ -190,9 +190,13 @@ "filter-type-entity-name": "Nom d'entité", "filter-type-entity-view-search-query": "Requête de recherche vue d'entité", "filter-type-entity-view-search-query-description": "Vues d'entité avec les types {{entityViewTypes}} ayant {{relationType}} relation {{direction}} {{rootEntity}}", + "filter-type-edge-search-query": "Requête de recherche de bordure", + "filter-type-edge-search-query-description": "Bordures de types {{edgeTypes}} ayant {{relationType}} relation {{direction}} {{rootEntity}}", "filter-type-entity-view-type": "Type de vue d'entité", "filter-type-entity-view-type-and-name-description": "Vues d'entité de type '{{entityView}}' et dont le nom commence par '{{prefix}}'", "filter-type-entity-view-type-description": "Vues d'entité de type '{{entityView}}'", + "filter-type-edge-type": "Type de la bordure", + "filter-type-edge-type-description": "Dispositifs de type '{{edgeType}}'", "filter-type-relations-query": "Interrogation des relations", "filter-type-relations-query-description": "{{entities}} ayant {{relationType}} relation {{direction}} {{rootEntity}}", "filter-type-required": "Le type de filtre est requis.", @@ -256,6 +260,17 @@ "name": "Nom", "name-required": "Nom est requis.", "name-starts-with": "Le nom de l'actif commence par", + "assign-asset-to-edge": "Attribuer des actifs a la bordure", + "assign-asset-to-edge-text": "Veuillez sélectionner les actifs à attribuer a la bordure", + "unassign-from-edge": "Retirer de la bordure", + "assign-to-edge": "Attribuer a la bordure", + "assign-to-edge-text": "Veuillez sélectionner la bordure pour attribuer le ou les actifs", + "unassign-asset-from-edge": "Retirer de la bordure", + "unassign-asset-from-edge-title": "Êtes-vous sûr de vouloir retirer l'attribution de l'actif '{{assetName}}'?", + "unassign-asset-from-edge-text": "Après la confirmation, l'actif sera non attribué et ne sera pas accessible a la bordure.", + "unassign-assets-from-edge-action-title": "Retirer {count, plural, 1 {1 asset} other {# assets}} de la bordure", + "unassign-assets-from-edge-title": "Êtes-vous sûr de vouloir retirer l'attribution de {count, plural, 1 {1 asset} other {# assets}}?", + "unassign-assets-from-edge-text": "Après la confirmation, tous les actifs sélectionnés ne seront pas attribués et ne seront pas accessibles a la bordure.", "no-asset-types-matching": "Aucun type d'actif correspondant à {{entitySubtype}} n'a été trouvé. ", "no-assets-matching": "Aucun actif correspondant à {{entity}} n'a été trouvé. ", "no-assets-text": "Aucun actif trouvé", @@ -324,6 +339,8 @@ "type-alarm-ack": "Acquitté", "type-alarm-clear": "Effacé", "type-assigned-to-customer": "Attribué au client", + "type-assigned-to-edge": "Attribué a la bordure", + "type-unassigned-from-edge": "Non attribué de la bordure", "type-attributes-deleted": "Attributs supprimés", "type-attributes-read": "Attributs lus", "type-attributes-updated": "Attributs mis à jour", @@ -406,11 +423,13 @@ "description": "Description", "details": "Détails", "devices": "Dispositifs du client", + "edges": "Bordures du client", "entity-views": "Vues de l'entité client", "events": "Événements", "idCopiedMessage": "L'Id du client a été copié dans le presse-papier", "manage-assets": "Gérer les actifs", "manage-customer-assets": "Gérer les actifs du client", + "manage-customer-edges": "Gérer les bordures du client", "manage-customer-dashboards": "Gérer les tableaux de bord du client", "manage-customer-devices": "Gérer les dispositifs du client", "manage-customer-users": "Gérer les utilisateurs du client", @@ -419,6 +438,7 @@ "manage-public-assets": "Gérer les actifs publics", "manage-public-dashboards": "Gérer les tableaux de bord publics", "manage-public-devices": "Gérer les dispositifs publics", + "manage-public-edges": "Gérer les bordures publics", "manage-users": "Gérer les utilisateurs", "management": "Gestion des clients", "no-customers-matching": "Aucun client correspondant à '{{entity}} n'a été trouvé.", @@ -427,6 +447,7 @@ "public-dashboards": "Tableaux de bord publics", "public-devices": "Dispositifs publics", "public-entity-views": "Vues d'entités publiques", + "public-edges": "Bordures publics", "select-customer": "Sélectionner un client", "select-default-customer": "Sélectionnez le client par défaut", "title": "Titre", @@ -571,7 +592,21 @@ "view-dashboards": "Afficher les tableaux de bord", "widget-file": "Fichier du Widget", "widget-import-missing-aliases-title": "Configurer les alias utilisés par le widget importé", - "widgets-margins": "Marge entre les widgets" + "widgets-margins": "Marge entre les widgets", + "manage-assigned-edges": "Gérer les bordures affectés", + "unassign-dashboard-from-edge-text": "Après la confirmation, tableau de bord sera non attribué et ne sera pas accessible a la bordure.", + "assigned-edges": "Bordures affectés", + "unassign-from-edge": "Retirer de la bordure", + "unassign-dashboards-from-edge-action-title": "Annuler l'affectation {count, plural, 1 {1 tableau de bord} other {# tableaux de bord}} de la bordure", + "unassign-dashboards-from-edge-text": "Après la confirmation, tous les tableaux de bord sélectionnés ne seront pas attribués et ne seront pas accessibles a la bordure.", + "assign-dashboard-to-edge": "Attribuer des tableaux de bord a la bordure", + "assign-dashboard-to-edge-text": "Veuillez sélectionner la bordure pour attribuer le ou les tableaux de bord", + "assign-dashboards-to-edge-text": "Attribuer {count, plural, 1 {1 tableau de bord} other {# tableaux de bord}} aux bordures", + "unassign-dashboards-from-edge-action-text": "Annuler l'affectation {count, plural, 1 {1 tableau de bord} other {# tableaux de bord}} des bordures", + "assign-to-edges": "Attribuer des tableaux de bord a la bordures", + "assign-to-edges-text": "Veuillez sélectionner les bordures pour attribuer les tableaux de bord", + "unassign-from-edges": "Retirer de la bordure", + "unassign-from-edges-text": "Veuillez sélectionner les bordures à annuler l'affectation du ou des tableaux de bord" }, "datakey": { "advanced": "Avancé", @@ -709,11 +744,92 @@ "unassign-from-customer": "Retirer du client", "use-device-name-filter": "Utiliser le filtre", "view-credentials": "Afficher les informations d'identification", - "view-devices": "Afficher les dispositifs" + "view-devices": "Afficher les dispositifs", + "assign-device-to-edge": "Attribuer a la bordure", + "assign-device-to-edge-text":"Veuillez sélectionner la bordure pour attribuer le ou les dispositifs", + "unassign-from-edge": "Retirer de la bordure", + "assign-to-edge": "Attribuer a la bordure", + "assign-to-edge-text": "Veuillez sélectionner la bordure pour attribuer le ou les dispositifs", + "unassign-device-from-edge-title": "Êtes-vous sûr de vouloir annuler l'affection du dispositif {{deviceName}} '?", + "unassign-device-from-edge-text": "Après la confirmation, dispositif sera non attribué et ne sera pas accessible a la bordure.", + "unassign-devices-from-edge-action-title": "Annuler l'affectation de {count, plural, 1 {1 device} other {#devices}} de la bordure", + "unassign-device-from-edge": "Retirer de la bordure", + "unassign-devices-from-edge-title": "Voulez-vous vraiment annuler l'affectation de {count, plural, 1 {1 device} other {# devices}}?", + "unassign-devices-from-edge-text": "Après la confirmation, tous les dispositifs sélectionnés ne seront pas attribues et ne seront pas accessibles par la bordure." }, "dialog": { "close": "Fermer le dialogue" }, + "edge": { + "edge": "Bordure", + "edges": "Bordures", + "management": "Gestion des bordures", + "no-edges-matching": "Aucun bordure correspondant à {{entity}} n'a été trouvé.", + "add": "Ajouter un bordure", + "view": "Afficher la bordure", + "no-edges-text": "Aucun bordure trouvé", + "edge-details": "Détails de la bordure", + "add-edge-text": "Ajouter une nouveau bordure", + "delete": "Supprimer la bordure", + "delete-edges": "Supprimer les bordures", + "delete-edge-title": "Êtes-vous sûr de vouloir supprimer la bordure '{{edgeName}}'?", + "delete-edge-text": "Faites attention, après la confirmation, la bordure et toutes les données associées deviendront irrécupérables", + "delete-edges-title": "Êtes-vous sûr de vouloir supprimer {count, plural, 1 {1 bordure} other {# bordure}}?", + "delete-edges-action-title": "Supprimer {count, plural, 1 {1 bordure} other {# bordure}}", + "delete-edges-text": "Faites attention, après la confirmation, tous les bordures sélectionnés seront supprimés et toutes les données associées deviendront irrécupérables.", + "name": "Nom", + "name-required": "Le nom de la bordure est requis", + "description": "Dispositifs", + "events": "Événements", + "details": "Détails de l'entité", + "copy-id": "Copier borudre Id", + "id-copied-message": "Id de la bordure a été copié dans le presse-papier", + "permissions": "Autorisations", + "edge-required": "Bordure est requise", + "edge-type": "Type de la bordure", + "edge-type-required": "Type de la bordure est requise.", + "select-edge-type": "Selectionner un type de la bordure", + "assign-to-customer": "Attribuer au client", + "assign-to-customer-text": "Veuillez sélectionner la bordure pour attribuer le ou les dispositifs", + "assign-edge-to-customer": "Attribuer la bordure au client", + "assign-edge-to-customer-text": "Veuillez sélectionner la bordure pour attribuer le ou les dispositifs", + "assigned-to-customer": "Attribué au client", + "unassign-from-customer": "Retirer du client", + "assign-edges-text": "Attribuer {count, plural, 1 {1 bordure} other {# bordures}} au client", + "unassign-edge-title": "Êtes-vous sûr de vouloir annuler l'affection du dispositif {{edgeName}}", + "unassign-edge-text": "Après la confirmation, le dispositif ne sera pas attribué et ne sera pas accessible au client", + "make-public": "Make edge public", + "make-public-edge-title": "Are you sure you want to make the edge '{{edgeName}}' public?", + "make-public-edge-text": "After the confirmation the edge and all its data will be made public and accessible by others.", + "make-private": "Rendre public Edge", + "public": "Public", + "make-private-edge-title": "Are you sure you want to make the edge '{{edgeName}}' private?", + "make-private-edge-text": "Après la confirmation, la bordure et toutes ses données seront rendues privées et ne seront pas accessibles par d'autres", + "import": "Importer bordure", + "label": "Etiquette", + "assign-new-edge": "Attribuer un nouvel bordure", + "manage-edge-dashboards": "Gérer les tableaux de bord", + "unassign-from-edge": "Retirer de la bordure", + "dashboards": "Tableau de bord de la bordure", + "manage-edge-rulechains": "Gérer les chaînes de règles", + "rulechains": "Chaînes de règles de la bordure", + "rulechain": "Chaîne de règles de la bordure", + "edge-key": "Clé de la bordure", + "copy-edge-key": "Copier clé de la bordure", + "edge-key-copied-message": "Clé de la bordure a été copié dans le presse-papier", + "edge-secret": "Secret de la bordure", + "copy-edge-secret": "Copier secret de la bordure", + "edge-secret-copied-message": "Secret de la bordure a été copié dans le presse-papier", + "manage-edge-assets": "Gérer les actifs de la bordure", + "manage-edge-devices": "Gérer les dispositifs de la bordure", + "manage-edge-entity-views": "Vues de l'entité vues de l'entité", + "assets": "Actifs de la bordure", + "devices": "Dispositifs de la bordure", + "entity-views": "Vues de l'entité bordure", + "set-root-rule-chain-text": "Veuillez sélectionner la chaîne de règles racine pour les bordure(s)", + "set-root-rule-chain-to-edges": "Définir la chaîne de règles racine pour bordure(s)", + "set-root-rule-chain-to-edges-text": "Définir la chaîne de règles racine pour {count, plural, 1 {1 bordure} other {# bordures} }" + }, "entity": { "add-alias": "Ajouter un alias d'entité", "alarm-name-starts-with": "Les actifs dont le nom commence par '{{prefix}}'", @@ -774,6 +890,10 @@ "rule-name-starts-with": "Régles dont les noms commencent par '{{prefix}}'", "rulechain-name-starts-with": "Chaînes de régles dont les noms commencent par '{{prefix}}'", "rulenode-name-starts-with": "Les noeuds de régles dont le nom commence par '{{prefix}}'", + "type-edge": "Bordure", + "type-edges": "Bordures", + "list-of-edges": "{ count, plural, 1 {Une bordure} other {List of # bordures} }", + "edge-name-starts-with": "Bordures dont les noms commencent par '{{prefix}}'", "search": "Recherche d'entités", "select-entities": "Sélectionner des entités", "selected-entities": "{count, plural, 1 {1 entité} other {# entités}} sélectionnées", @@ -884,6 +1004,17 @@ "make-public": "Rendre la vue d'entité publique", "make-public-entity-view-text": "Après la confirmation, la vue de l'entité et toutes ses données seront rendues publiques et accessibles à d'autres", "make-public-entity-view-title": "Voulez-vous vraiment que la vue de l'entité '{{entityViewName}}' soit publique?", + "assign-entity-view-to-edge": "Attribuer a la bordure", + "assign-entity-view-to-edge-text":"Veuillez sélectionner la bordure auquel attribuer la ou les vues d'entité.", + "unassign-from-edge": "Retirer de la bordure", + "assign-to-edge": "Attribuer a la bordure", + "assign-to-edge-text": "Veuillez sélectionner la bordure pour attribuer le ou les actifs", + "unassign-entity-view-from-edge-title": "Voulez-vous vraiment annuler l'attribution de la vue d'entité '{{entityViewName}}'?", + "unassign-entity-view-from-edge-text": "Après la confirmation, la vue de l'entité sera non attribuée et ne sera pas accessible par la bordure.", + "unassign-entity-views-from-edge-action-title": "Annuler l'attribution { count, plural, 1 {1 entityView} other {# entityViews} } de la bordure", + "unassign-entity-view-from-edge": "Annuler l'attribution des vues d'entité", + "unassign-entity-views-from-edge-title": "Êtes-vous sûr de vouloir annuler l'attribution { count, plural, 1 {1 entityView} other {# entityViews} }?", + "unassign-entity-views-from-edge-text": "Après la confirmation, toutes les vues des entités sélectionnées seront non attribuées et ne seront pas accessibles par la bordure.", "management": "Gestion de vue d'entité", "name": "Nom", "name-required": "Un nom est requis.", @@ -1298,10 +1429,45 @@ "rulechain-required": "Chaîne de règles requise", "rulechains": "Chaînes de règles", "select-rulechain": "Sélectionner la chaîne de règles", + "system-rulechains": "Chaînes de règles du système", + "edge-rulechains": "Chaînes de règles de la bordure", "set-root": "Rend la chaîne de règles racine (root) ", "set-root-rulechain-text": "Après la confirmation, la chaîne de règles deviendra racine (root) et gérera tous les messages de transport entrants.", "set-root-rulechain-title": "Voulez-vous vraiment que la chaîne de règles '{{ruleChainName}} soit racine (root) ?", - "system": "Système" + "system": "Système", + "assign-rulechains": "Attribuer aux chaînes de règles", + "assign-new-rulechain": "Attribuer une nouvele chaînes de règles", + "delete-rulechains": "Supprimer une chaînes de règles", + "default": "Défaut", + "unassign-rulechain": "Retirer chaîne de règles", + "unassign-rulechains": "Retirer chaînes de règles", + "unassign-rulechain-title": "AÊtes-vous sûr de vouloir retirer l'attribution de chaînes de règles '{{ruleChainTitle}}'?", + "unassign-rulechains-title": "Êtes-vous sûr de vouloir retirer l'attribution de {count, plural, 1 {1 chaîne de règles} other {# chaînes de règles}}?", + "manage-assigned-edges": "Gérer les bordures affectés", + "unassign-rulechain-from-edge-text": "Après la confirmation, l'actif sera non attribué et ne sera pas accessible a la bordure.", + "assigned-edges": "Bordures affectés", + "unassign-from-edge": "Retirer de la bordure", + "unassign-rulechains-from-edge-action-title": "Retirer {count, plural, 1 {1 chaîne de règles} other {# chaînes de règles}} de la bordure", + "unassign-rulechains-from-edge-text": "Après la confirmation, tous les chaînes de règles sélectionnés ne seront pas attribués et ne seront pas accessibles a la bordure.", + "assign-rulechains-to-edge-text": "Attribuer {count, plural, 1 {1 chaîne de règles} other {# chaînes de règles}} aux bordures", + "assign-rulechain-to-edge": "Attribuer les chaînes de règles a la bordure", + "assign-rulechain-to-edge-text": "Veuillez sélectionner la bordure pour attribuer le ou les chaînes de règles", + "unassign-rulechains-from-edge-action-text": "Annuler l'affectation {count, plural, 1 {1 chaîne de règles} other {# chaînes de règles}} des bordures", + "assign-to-edges": "Attribuer des tableaux de bord a la bordures", + "assign-to-edges-text": "Please select the edges to assign the rulechain(s)", + "unassign-from-edges": "Retirer de la bordure", + "unassign-from-edges-text": "Veuillez sélectionner les bordures à annuler l'affectation du ou des chaînes de règles", + "assigned-to-edges": "Attribué chaînes de règles", + "set-default-root-edge": "Définir la racine par défaut de la chaîne de règles", + "set-default-root-edge-rulechain-title": "AVoulez-vous vraiment créer de chaînes de règles par défaut '{{ruleChainName}}'?", + "set-default-root-edge-rulechain-text": "Après la confirmation, la chaîne de règles deviendra la racine de la bordure par défaut et gérera tous les messages de transport entrants.", + "invalid-rulechain-type-error": "Impossible d'importer la chaîne de règles: type de chaîne de règles non valide. Le type attendu est {{attenduRuleChainType}}.", + "set-default-edge": "Définir la chaîne de règles de la bordure par défaut", + "set-default-edge-title": "Voulez-vous vraiment définir la chaîne de règles de la bordure '{{ruleChainName}}' par défaut?", + "set-default-edge-text": "Après la confirmation, la chaîne de règles d'arête sera ajoutée à la liste par défaut et affectée aux arêtes nouvellement créées.", + "remove-default-edge": "Supprimer la chaîne de règles de la bordure des valeurs par défaut", + "remove-default-edge-title": "Voulez-vous vraiment supprimer la chaîne de règles de la bordure '{{ruleChainName}}' de la liste par défaut", + "remove-default-edge-text": "Après la confirmation, la chaîne de règles d'arête ne sera pas affectée aux arêtes nouvellement créées." }, "rulenode": { "add": "Ajouter un noeud de règle", From ef12b7bee3768b9358b14dffa4587fb41a5cc549 Mon Sep 17 00:00:00 2001 From: deaflynx Date: Thu, 4 Jun 2020 12:01:17 +0300 Subject: [PATCH 30/52] Added spanish edge locale --- ui/src/app/locale/locale.constant-es_ES.json | 163 ++++++++++++++++++- 1 file changed, 159 insertions(+), 4 deletions(-) diff --git a/ui/src/app/locale/locale.constant-es_ES.json b/ui/src/app/locale/locale.constant-es_ES.json index df5752f54b..31083093dc 100644 --- a/ui/src/app/locale/locale.constant-es_ES.json +++ b/ui/src/app/locale/locale.constant-es_ES.json @@ -185,6 +185,8 @@ "filter-type-entity-view-type": "Tipo de vista de entidad", "filter-type-entity-view-type-description": "Vista de entidad del tipo '{{entityView}}'", "filter-type-entity-view-type-and-name-description": "Las vista de entidad del tipo '{{entityView}}' y cuyo nombre comienza con '{{prefix}}'", + "filter-type-edge-type": "Tipo de borde", + "filter-type-edge-type-description": "Bordes del tipo '{{edgeType}}'", "filter-type-relations-query": "Consulta de relaciones", "filter-type-relations-query-description": "{{entities}} que tienen {{relationType}} relación {{direction}} {{rootEntity}}", "filter-type-asset-search-query": "Consultar búsqueda de activos", @@ -193,10 +195,14 @@ "filter-type-device-search-query-description": "Dispositivos con tipos {{deviceTypes}} que tienen {{relationType}} relación {{direction}} {{rootEntity}}", "filter-type-entity-view-search-query": "Consultar vista de entidad", "filter-type-entity-view-search-query-description": "Las vista de entidad de tipo {{entityViewTypes}} que tienen {{relationType}} relación {{direction}} {{rootEntity}}", + "type-assigned-to-edge": "Asignado a borde", + "type-unassigned-from-edge": "Sin asignar desde bordes", "entity-filter": "Filtro de entidad", "resolve-multiple": "Resolver como entidades múltiples", "filter-type": "Tipo de filtro", "filter-type-required": "Tipo de filtro es requerido.", + "filter-type-edge-type": "Tipo de borde", + "filter-type-edge-type-description": "Bordes del tipo '{{edgeType}}'", "entity-filter-no-entity-matched": "No se encontraron entidades que coincidan con el filtro especificado.", "no-entity-filter-specified": "No se especificó el filtro de entidad", "root-state-entity": "Utilizar la entidad del panel de estados como raíz", @@ -384,6 +390,7 @@ "public-devices": "Dispositivos públicos", "public-assets": "Activos públicos", "public-entity-views": "Vista de entidad públicas", + "public-edge": "Bordes públicos", "add": "Agregar cliente", "delete": "Eliminar cliente", "manage-customer-users": "Gestionar usuarios del cliente", @@ -392,6 +399,8 @@ "manage-public-devices": "Gestionar dispositivos públicos", "manage-public-dashboards": "Gestionar paneles públicos", "manage-customer-assets": "Gestionar activos del cliente", + "manage-customer-edge": "Administrar bordes de clientes", + "manage-public-edge": "Administrar bordes públicos", "manage-public-assets": "Gestionar activos públicos", "add-customer-text": "Agregar nuevo cliente", "no-customers-text": "No se encontraron clientes", @@ -410,6 +419,7 @@ "description": "Descripción", "details": "Detalles", "events": "Eventos", + "edge": "Bordes del cliente", "copyId": "Copiar ID del cliente", "idCopiedMessage": "ID del cliente ha sido copiada al portapapeles", "select-customer": "Seleccionar cliente", @@ -564,7 +574,21 @@ "show-details": "Mostrar detalles", "hide-details": "Ocultar detalles", "select-state": "Seleccionar estado objetivo", - "state-controller": "Estado del controlador" + "state-controller": "Estado del controlador", + "manage-assigned-edges": "Administrar bordes asignados", + "unassign-dashboard-from-edge-text": "Después de la confirmación, el tablero no será asignado y el borde no podrá acceder a él", + "assigned-edges": "bordes asignados", + "unassign-from-edge": "Anular asignación de borde", + "unassign-dashboards-from-edge-action-title": "Anular asignación { count, plural, 1 {1 panel} other {# paneles} } de borde", + "unassign-dashboards-from-edge-text": "Después de la confirmación, se anulará la asignación de todos los paneles seleccionados y no serán accesibles por de borde", + "assign-dashboard-to-edge": "Asignar panel(es) al borde", + "assign-dashboard-to-edge-text": "Por favor selecciona los paneles para asignar al borde", + "assign-dashboards-to-edge-text": "Asignar { count, plural, 1 {1 panel} other {# paneles} } a los bordes", + "unassign-dashboards-from-edge-action-text": "Anular asignación { count, plural, 1 {1 dashboard} other {# dashboards} } de los bordes", + "assign-to-edges": "Asignar paneles a los bordes", + "assign-to-edges-text": "Seleccione los bordes para asignar los paneles", + "unassign-from-edges": "Desasignar panel (s) de los bordes", + "unassign-from-edge-text": "Seleccione los bordes para desasignar del panel(es)" }, "datakey": { "settings": "Configuración", @@ -698,7 +722,18 @@ "device-public": "El dispositivo es público", "select-device": "Seleccionar dispositivo", "device-file": "Archivo de dispositivo", - "import": "Importar dispositivo" + "import": "Importar dispositivo", + "assign-device-to-edge": "Asignar dispositivo (s) a borde", + "assign-device-to-edge-text": "Seleccione los dispositivos para asignar al borde", + "unassign-from-edge": "Anular asignación de borde", + "assign-to-edge": "Asignar al borde", + "assign-to-edge-text": "Seleccione el borde para asignar los dispositivos", + "unassign-device-from-edge-title": "¿Está seguro de que desea desasignar el dispositivo '{{deviceName}}'?", + "unassign-device-from-edge-text": "Después de la confirmación, el dispositivo no será asignado y el borde no podrá acceder a él", + "unassign-devices-from-edge-action-title": "Anular asignación {count, plural, 1 {1 device} other {# devices}} from edge", + "unassign-device-from-edge": "Desasignar dispositivo", + "unassign-devices-from-edge-title": "¿Está seguro de que desea desasignar {count, plural, 1 {1 device} other {# devices}}?", + "unassign-devices-from-edge-text": "Después de la confirmación, todos los dispositivos seleccionados quedarán sin asignar y el borde no podrá acceder a ellos" }, "dialog": { "close": "Cerrar diálogo" @@ -707,6 +742,76 @@ "column": "Columna", "row": "Fila" }, + "edge": { + "edge": "Borde", + "edges": "Bordes", + "management": "Gestión de bordes", + "no-edge-matching": "No se encontraron bordes que coincidan con '{{entity}}'", + "add": "Agregar borde", + "view": "Ver borde", + "no-edge-text": "No se encontraron bordes", + "edge-details": "Detalles del borde", + "add-edge-text": "Agregar nuevo borde", + "delete": "Eliminar borde", + "delete-edges": "Eliminar bordes", + "delete-edge-title": "¿Está seguro de que desea eliminar el borde '{{edgeName}}'?", + "delete-edge-text": "Tenga cuidado, después de la confirmación, el borde y todos los datos relacionados serán irrecuperables", + "delete-edges-title": "¿Está seguro de que desea edge {count, plural, 1 {1 borde} other {# bordes}}?", + "delete-edge-action-title": "Eliminar {cuenta, plural, 1 {1 borde} otro {# bordes}}", + "delete-edges-text": "Tenga cuidado, después de la confirmación se eliminarán todos los bordes seleccionados y todos los datos relacionados se volverán irrecuperables", + "name": "Nombre", + "name-required": "Se requiere nombre", + "description": "Descripción", + "events": "Eventos", + "details": "Detalles", + "copy-id": "Copiar ID de borde", + "id-copied-message": "El ID de borde se ha copiado al portapapeles", + "permissions": "Permisos", + "edge-required": "Edge required", + "edge-type": "Type de la bordure", + "edge-type-required": "El tipo de borde es requerido.", + "select-edge-type": "Seleccionar tipo de borde", + "assign-to-customer": "Asignar al cliente", + "assign-to-customer-text": "Seleccione el cliente para asignar los bordes", + "assign-edge-to-customer": "Asignar borde(s) al cliente", + "assign-edge-to-customer-text": "Seleccione los bordes para asignar al cliente", + "assigned-to-customer": "Asignado al cliente", + "unassign-from-customer": "Anular asignación del cliente", + "assign-edges-text": "Asignar {cuenta, plural, 1 {1 borde} otro {# bordes}} al cliente", + "unassign-edge-title": "¿Está seguro de que desea desasignar el borde '{{edgeName}}'?", + "unassign-edge-text": "Después de la confirmación, el borde quedará sin asignar y el cliente no podrá acceder a él", + "make-public": "Hacer público el borde", + "make-public-edge-title": "¿Estás seguro de que quieres hacer público el edge '{{edgeName}}'?", + "make-public-edge-text": "Después de la confirmación, el borde y todos sus datos serán públicos y accesibles para otros", + "make-private": "Hacer que edge sea privado", + "public": "Public", + "make-private-edge-title": "¿Está seguro de que desea que el borde '{{edgeName}}' sea privado?", + "make-private-edge-text": "Después de la confirmación, el borde y todos sus datos se harán privados y otros no podrán acceder a ellos", + "import": "Importar borde", + "label": "Etiqueta", + "assign-new-edge": "Asignar nuevo borde", + "manage-edge-dashboards": "Administrar paneles de borde", + "unassign-from-edge": "Anular asignación de borde", + "dashboards": "Paneles de borde", + "manage-edge-rulechains": "Administrar cadenas de reglas de borde", + "rulechains": "Cadenas de regla de borde", + "rulechain": "Cadena de regla de borde", + "edge-key": "Clave de borde", + "copy-edge-key": "Copiar clave de borde", + "edge-key-copied-message": "La clave de borde se ha copiado al portapapeles", + "edge-secret": "Borde secreto", + "copy-edge-secret": "Copiar borde secreto", + "edge-secret-copied-message": "El secreto de borde se ha copiado al portapapeles", + "manage-edge-assets": "Gestionar activos de bordes", + "manage-edge-devices": "Gestionar dispositivos de borde", + "manage-edge-entity-views": "Gestionar vistas de entidad de borde", + "assets": "Activos de borde", + "devices": "Dispositivos de borde", + "entity-views": "Vistas de entidad de borde", + "set-root-rule-chain-text": "Seleccione la cadena de reglas raíz para los bordes", + "set-root-rule-chain-to-edge": "Establecer la cadena de reglas raíz para Edge (s)", + "set-root-rule-chain-to-edge-text": "Establecer la cadena de la regla raíz para {count, plural, 1 {1 borde} other {# bordes}}" + }, "error": { "unable-to-connect": "¡No se puede conectar al servidor! Por favor, revise su conexión a Internet.", "unhandled-error-code": "Código de error no controlado: {{errorCode}}", @@ -800,6 +905,10 @@ "type-rulenodes": "Nodos de reglas", "list-of-rulenodes": "{ count, plural, 1 {Un nodo de reglas} other {Lista de # nodos de reglas} }", "rulenode-name-starts-with": "Nodos de reglas cuyos nombres comienzan con '{{prefix}}'", + "type-edge": "Borde", + "type-edges": "Bordes", + "list-of-edges": "{cuenta, plural, 1 {Un borde} otro {Lista de # bordes}}", + "edge-name-starts-with": "Bordes cuyos nombres comienzan con '{{prefijo}}'", "type-current-customer": "Cliente Actual", "search": "Buscar entidades", "selected-entities": "{ count, plural, 1 {1 entidad} other {# entidades} } seleccionadas", @@ -919,7 +1028,18 @@ "make-public-entity-view-title": "¿Está seguro de que desea que la vista de entidad '{{entityViewName}}' sea pública?", "make-public-entity-view-text": "Después de la confirmación, la vista de la entidad y todos sus datos se harán públicos y accesibles para otros.", "make-private-entity-view-title": "¿Está seguro de que desea que la vista de entidad '{{entityViewName}}' sea privada?", - "make-private-entity-view-text": "Después de la confirmación, la vista de la entidad y todos sus datos se harán privados y no serán accesibles para otros." + "make-private-entity-view-text": "Después de la confirmación, la vista de la entidad y todos sus datos se harán privados y no serán accesibles para otros.", + "assign-entity-view-to-edge": "Asignar vista (s) de entidad a borde", + "assign-entity-view-to-edge-text": "Seleccione las vistas de entidad para asignar al borde", + "unassign-from-edge": "Anular asignación de borde", + "assign-to-edge": "Asignar al borde", + "assign-to-edge-text": "Seleccione el borde para asignar las vistas de entidad", + "unassign-entity-view-from-edge-title": "¿Está seguro de que desea anular la asignación de la vista de entidad '{{entityViewName}}'?", + "unassign-entity-view-from-edge-text": "Después de la confirmación, la vista de entidad quedará sin asignar y el borde no podrá acceder a ella", + "unassign-entity-views-from-edge-action-title": "Anular asignación {recuento, plural, 1 {1 vista de entidad} otras {# vistas de entidad}} del borde", + "unassign-entity-view-from-edge": "Anular asignación de vista de entidad", + "unassign-entity-views-from-edge-title": "¿Está seguro de que desea desasignar {count, plural, 1 {1 vista de entidad} other {# vistas de entidad}}?", + "unassign-entity-views-from-edge-text": "Después de la confirmación, todas las vistas de entidad seleccionadas no serán asignadas y el borde no podrá acceder a ellas" }, "event": { "event-type": "Tipo de evento", @@ -1290,6 +1410,8 @@ "rulechain": { "rulechain": "Cadena de reglas", "rulechains": "Cadenas de reglas", + "system-rulechains": "Cadenas de reglas del sistema", + "edge-rulechains": "Cadenas de reglas de borde", "root": "Raíz", "delete": "Eliminar cadena de reglas", "name": "Nombre", @@ -1322,7 +1444,40 @@ "no-rulechains-matching": "Cadenas de reglas que coincidan con '{{entity}}' no fueron encontradas.", "rulechain-required": "Cadena de reglas es requerida", "management": "Gestión de reglas", - "debug-mode": "Mode de depuración" + "debug-mode": "Mode de depuración", + "assign-rulechains": "Asignar cadenas de reglas", + "assign-new-rulechain": "Asignar nueva cadena de reglas", + "delete-rulechains": "Eliminar cadenas de reglas", + "default": "Predeterminado", + "unassign-rulechain": "Anular asignación de cadena de reglas", + "unassign-rulechains": "Anular asignación de cadenas de reglas", + "unassign-rulechain-title": "¿Está seguro de que desea desasignar la cadena de reglas '{{ruleChainTitle}}'?", + "unassign-rulechains-title": "¿Está seguro de que desea desasignar {count, plural, 1 {1 cadena de reglas} other {# cadenas de reglas}}?", + "manage-assigned-edges": "Gestionar bordes asignados", + "unassign-rulechain-from-edge-text": "Después de la confirmación, la cadena de reglas quedará sin asignar y el borde no podrá acceder a ella", + "assigned-edges": "Bordes asignados", + "unassign-from-edge": "Anular asignación de borde", + "unassign-rulechains-from-edge-action-title": "Anular asignación {count, plural, 1 {1 cadena de reglas} other {# cadenas de reglas}} des bordes", + "unassign-rulechains-from-edge-text": "Después de la confirmación, todas las cadenas de reglas seleccionadas quedarán sin asignar y el borde no podrá acceder a ellas", + "assign-rulechains-to-edge-text": "Asignar {cuenta, plural, 1 {1 cadena de reglas} otras {# cadenas de reglas}} a las aristas", + "assign-rulechain-to-edge": "Asignar cadena (s) de reglas a borde", + "assign-rulechain-to-edge-text": "Seleccione las cadenas de reglas para asignar al borde", + "unassign-rulechains-from-edge-action-text": "Anular asignación {cuenta, plural, 1 {1 cadena de reglas} otro {# cadenas de reglas}} de los bordes", + "assign-to-edges": "Asignar cadena (s) de reglas a los bordes", + "assign-to-edges-text": "Seleccione los bordes para asignar las cadenas de reglas", + "unassign-from-edges": "Desasignar cadena (s) de reglas de los bordes", + "unassign-from-edges-text": "Seleccione los bordes para desasignar de la (s) cadena (s) de reglas", + "assigned-to-edges": "Asignado a bordes", + "set-default-root-edge": "Hacer que la cadena de reglas sea la raíz predeterminada", + "set-default-root-edge-rulechain-title": "¿Está seguro de que desea hacer que la cadena de reglas '{{ruleChainName}}' sea la raíz de borde predeterminada?", + "set-default-root-edge-rulechain-text": "Después de la confirmación, la cadena de reglas se convertirá en raíz raíz predeterminada y manejará todos los mensajes de transporte entrantes", + "invalid-rulechain-type-error": "No se puede importar la cadena de reglas: Tipo de cadena de reglas no válido. El tipo esperado es {{expectedRuleChainType}}", + "set-default-edge": "Hacer que la cadena de reglas de borde sea predeterminada", + "set-default-edge-title": "¿Está seguro de que desea que la cadena de reglas de borde '{{ruleChainName}}' sea predeterminada?", + "set-default-edge-text": "Después de la confirmación, la cadena de reglas de borde se agregará a la lista predeterminada y se asignará a los bordes recién creados", + "remove-default-edge": "Eliminar la cadena de regla de borde de los valores predeterminados", + "remove-default-edge-title": "¿Está seguro de que desea eliminar la cadena de reglas de borde '{{ruleChainName}}' de la lista predeterminada?", + "remove-default-edge-text": "Después de la confirmación, la cadena de reglas de borde no se asignará a los bordes recién creados" }, "rulenode": { "details": "Detalles", From 9a749bf720c221267ec427088deb5a68eaef027f Mon Sep 17 00:00:00 2001 From: Bohdan Smetaniuk Date: Fri, 5 Jun 2020 12:57:41 +0300 Subject: [PATCH 31/52] fixed bug with saving similar edges in db --- .../server/dao/edge/EdgeServiceImpl.java | 15 ++++++++++++++- .../main/resources/sql/schema-entities-hsql.sql | 4 +++- dao/src/main/resources/sql/schema-entities.sql | 4 +++- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java index c7b1d87ab9..126899e270 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java @@ -23,6 +23,7 @@ import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.binary.Base64; +import org.bouncycastle.util.test.FixedSecureRandom; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; @@ -730,6 +731,11 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic throw new DataValidationException("Edge with such name already exists!"); } ); + edgeDao.findByRoutingKey(edge.getTenantId().getId(), edge.getRoutingKey()).ifPresent( + d -> { + throw new DataValidationException("Edge with such routing_key already exists"); + } + ); } } @@ -743,13 +749,20 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic } } ); + edgeDao.findByRoutingKey(edge.getTenantId().getId(), edge.getRoutingKey()).ifPresent( + e -> { + if (!e.getUuidId().equals(edge.getUuidId())) { + throw new DataValidationException("Edge with such routing_key already exists!"); + } + } + ); } } @Override protected void validateDataImpl(TenantId tenantId, Edge edge) { if (StringUtils.isEmpty(edge.getType())) { - throw new DataValidationException("Edge type should be specified!"); + throw new DataValidationException("Edge typeshould be specified!"); } if (StringUtils.isEmpty(edge.getName())) { throw new DataValidationException("Edge name should be specified!"); diff --git a/dao/src/main/resources/sql/schema-entities-hsql.sql b/dao/src/main/resources/sql/schema-entities-hsql.sql index 42f52a2184..cd8eae9e34 100644 --- a/dao/src/main/resources/sql/schema-entities-hsql.sql +++ b/dao/src/main/resources/sql/schema-entities-hsql.sql @@ -266,5 +266,7 @@ CREATE TABLE IF NOT EXISTS edge ( routing_key varchar(255), secret varchar(255), search_text varchar(255), - tenant_id varchar(31) + tenant_id varchar(31), + CONSTRAINT edge_name_unq_key UNIQUE (tenant_id, name), + CONSTRAINT edge_routing_key_unq_key UNIQUE (routing_key) ); diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index ffea8bdf39..de526788d3 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -266,7 +266,9 @@ CREATE TABLE IF NOT EXISTS edge ( routing_key varchar(255), secret varchar(255), search_text varchar(255), - tenant_id varchar(31) + tenant_id varchar(31), + CONSTRAINT edge_name_unq_key UNIQUE (tenant_id, name), + CONSTRAINT edge_routing_key_unq_key UNIQUE (routing_key) ); CREATE OR REPLACE PROCEDURE cleanup_events_by_ttl(IN ttl bigint, IN debug_ttl bigint, INOUT deleted bigint) From a540298a078ff2bd04cb7faf1ed556b768351b80 Mon Sep 17 00:00:00 2001 From: Bohdan Smetaniuk Date: Fri, 5 Jun 2020 13:02:48 +0300 Subject: [PATCH 32/52] ref fixes --- .../org/thingsboard/server/dao/edge/EdgeServiceImpl.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java index 126899e270..17ff2003bf 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java @@ -23,7 +23,6 @@ import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.binary.Base64; -import org.bouncycastle.util.test.FixedSecureRandom; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; @@ -33,14 +32,12 @@ import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Dashboard; -import org.thingsboard.server.common.data.DashboardInfo; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.Event; -import org.thingsboard.server.common.data.ShortEdgeInfo; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.asset.Asset; @@ -48,13 +45,10 @@ import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeQueueEntityType; import org.thingsboard.server.common.data.edge.EdgeQueueEntry; import org.thingsboard.server.common.data.edge.EdgeSearchQuery; -import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DashboardId; -import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TextPageData; @@ -762,7 +756,7 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic @Override protected void validateDataImpl(TenantId tenantId, Edge edge) { if (StringUtils.isEmpty(edge.getType())) { - throw new DataValidationException("Edge typeshould be specified!"); + throw new DataValidationException("Edge type should be specified!"); } if (StringUtils.isEmpty(edge.getName())) { throw new DataValidationException("Edge name should be specified!"); From c677b1bdc305bc48338a9fb9c7776da00d7139bf Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Fri, 5 Jun 2020 14:23:21 +0300 Subject: [PATCH 33/52] Shutdown before re-opening --- .../java/org/thingsboard/edge/rpc/EdgeGrpcClient.java | 11 +++++++++++ 1 file changed, 11 insertions(+) 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 5ee2f3341f..f974cb9598 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 @@ -79,6 +79,7 @@ public class EdgeGrpcClient implements EdgeRpcClient { throw new RuntimeException(e); } } + gracefulShutdown(); channel = builder.build(); EdgeRpcServiceGrpc.EdgeRpcServiceStub stub = EdgeRpcServiceGrpc.newStub(channel); log.info("[{}] Sending a connect request to the TB!", edgeKey); @@ -89,6 +90,16 @@ public class EdgeGrpcClient implements EdgeRpcClient { .build()); } + private void gracefulShutdown() { + try { + if (channel != null) { + channel.shutdown().awaitTermination(timeoutSecs, TimeUnit.SECONDS); + } + } catch (InterruptedException e) { + log.debug("Error during shutdown of the previous channel", e); + } + } + @Override public void disconnect() throws InterruptedException { inputStream.onCompleted(); From 1a83dbc8e35c2db8a738a309d24be2668d30439c Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Wed, 10 Jun 2020 15:34:10 +0300 Subject: [PATCH 34/52] Use IDs instead of names --- .../RuleChainActorMessageProcessor.java | 2 +- .../service/edge/rpc/EdgeGrpcSession.java | 19 ++++-- .../AssetUpdateMsgConstructor.java | 2 + .../DeviceUpdateMsgConstructor.java | 2 + .../EntityViewUpdateMsgConstructor.java | 45 +++++--------- .../constructor/UserUpdateMsgConstructor.java | 2 + .../thingsboard/edge/rpc/EdgeGrpcClient.java | 2 +- common/edge-api/src/main/proto/edge.proto | 62 +++++++++++-------- 8 files changed, 72 insertions(+), 64 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java index 8bca159d85..30e4e56ff3 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java @@ -224,6 +224,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor Date: Wed, 10 Jun 2020 19:07:16 +0300 Subject: [PATCH 35/52] Handling device creation from edge to cloud --- .../service/edge/EdgeContextComponent.java | 5 + .../service/edge/rpc/EdgeGrpcSession.java | 179 +++++++++++------- common/edge-api/src/main/proto/edge.proto | 13 +- 3 files changed, 118 insertions(+), 79 deletions(-) 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 1e51106717..f936ee82a5 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 @@ -25,6 +25,7 @@ import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.dashboard.DashboardService; +import org.thingsboard.server.dao.device.DeviceCredentialsService; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.entityview.EntityViewService; @@ -57,6 +58,10 @@ public class EdgeContextComponent { @Autowired private DeviceService deviceService; + @Lazy + @Autowired + private DeviceCredentialsService deviceCredentialsService; + @Lazy @Autowired private EntityViewService entityViewService; 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 ea1aad462c..e6b68f486a 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 @@ -31,6 +31,7 @@ import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.Event; import org.thingsboard.server.common.data.User; @@ -56,6 +57,8 @@ import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainMetaData; +import org.thingsboard.server.common.data.security.DeviceCredentials; +import org.thingsboard.server.common.data.security.DeviceCredentialsType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgDataType; import org.thingsboard.server.common.msg.TbMsgMetaData; @@ -99,7 +102,7 @@ import static org.thingsboard.server.gen.edge.UpdateMsgType.ENTITY_CREATED_RPC_M @Data public final class EdgeGrpcSession implements Closeable { - private static final ReentrantLock entityCreationLock = new ReentrantLock(); + private static final ReentrantLock deviceCreationLock = new ReentrantLock(); private static final String QUEUE_START_TS_ATTR_KEY = "queueStartTs"; @@ -518,32 +521,15 @@ public final class EdgeGrpcSession implements Closeable { for (EntityDataProto entityData : uplinkMsg.getEntityDataList()) { TbMsg tbMsg = null; TbMsg originalTbMsg = TbMsg.fromBytes(entityData.getTbMsg().toByteArray(), TbMsgCallback.EMPTY); - switch (originalTbMsg.getOriginator().getEntityType()) { - case DEVICE: - String deviceName = entityData.getEntityName(); - String deviceType = entityData.getEntityType(); - Device device = getOrCreateDevice(deviceName, deviceType); - if (device != null) { - tbMsg = TbMsg.newMsg(originalTbMsg.getType(), device.getId(), originalTbMsg.getMetaData().copy(), - originalTbMsg.getDataType(), originalTbMsg.getData()); - } - break; - case ASSET: - String assetName = entityData.getEntityName(); - Asset asset = ctx.getAssetService().findAssetByTenantIdAndName(edge.getTenantId(), assetName); - if (asset != null) { - tbMsg = TbMsg.newMsg(originalTbMsg.getType(), asset.getId(), originalTbMsg.getMetaData().copy(), - originalTbMsg.getDataType(), originalTbMsg.getData()); - } - break; - case ENTITY_VIEW: - String entityViewName = entityData.getEntityName(); - EntityView entityView = ctx.getEntityViewService().findEntityViewByTenantIdAndName(edge.getTenantId(), entityViewName); - if (entityView != null) { - tbMsg = TbMsg.newMsg(originalTbMsg.getType(), entityView.getId(), originalTbMsg.getMetaData().copy(), - originalTbMsg.getDataType(), originalTbMsg.getData()); - } - break; + if (originalTbMsg.getOriginator().getEntityType() == EntityType.DEVICE) { + String deviceName = entityData.getEntityName(); + Device device = ctx.getDeviceService().findDeviceByTenantIdAndName(edge.getTenantId(), deviceName); + if (device != null) { + tbMsg = TbMsg.newMsg(originalTbMsg.getType(), device.getId(), originalTbMsg.getMetaData().copy(), + originalTbMsg.getDataType(), originalTbMsg.getData()); + } + } else { + tbMsg = originalTbMsg; } if (tbMsg != null) { ctx.getTbClusterService().pushMsgToRuleEngine(edge.getTenantId(), tbMsg.getOriginator(), tbMsg, null); @@ -552,19 +538,7 @@ public final class EdgeGrpcSession implements Closeable { } if (uplinkMsg.getDeviceUpdateMsgList() != null && !uplinkMsg.getDeviceUpdateMsgList().isEmpty()) { for (DeviceUpdateMsg deviceUpdateMsg : uplinkMsg.getDeviceUpdateMsgList()) { - String deviceName = deviceUpdateMsg.getName(); - String deviceType = deviceUpdateMsg.getType(); - switch (deviceUpdateMsg.getMsgType()) { - case ENTITY_CREATED_RPC_MESSAGE: - getOrCreateDevice(deviceName, deviceType); - break; - case ENTITY_DELETED_RPC_MESSAGE: - Device device = ctx.getDeviceService().findDeviceByTenantIdAndName(edge.getTenantId(), deviceName); - if (device != null) { - ctx.getDeviceService().unassignDeviceFromEdge(edge.getTenantId(), device.getId(), edge.getId()); - } - break; - } + onDeviceUpdate(deviceUpdateMsg); } } if (uplinkMsg.getAlarmUpdateMsgList() != null && !uplinkMsg.getAlarmUpdateMsgList().isEmpty()) { @@ -584,6 +558,101 @@ public final class EdgeGrpcSession implements Closeable { return UplinkResponseMsg.newBuilder().setSuccess(true).build(); } + private void onDeviceUpdate(DeviceUpdateMsg deviceUpdateMsg) { + log.info("onDeviceUpdate {}", deviceUpdateMsg); + DeviceId edgeDeviceId = new DeviceId(new UUID(deviceUpdateMsg.getIdMSB(), deviceUpdateMsg.getIdLSB())); + switch (deviceUpdateMsg.getMsgType()) { + case ENTITY_CREATED_RPC_MESSAGE: + String deviceName = deviceUpdateMsg.getName(); + Device device = ctx.getDeviceService().findDeviceByTenantIdAndName(edge.getTenantId(), deviceName); + if (device != null) { + // device with this name already exists on the cloud - update ID on the edge + if (!device.getId().equals(edgeDeviceId)) { + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setDeviceUpdateMsg(ctx.getDeviceUpdateMsgConstructor().constructDeviceUpdatedMsg(UpdateMsgType.DEVICE_CONFLICT_RPC_MESSAGE, device)) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + } + } else { + Device deviceById = ctx.getDeviceService().findDeviceById(edge.getTenantId(), edgeDeviceId); + if (deviceById != null) { + // this ID already used by other device - create new device and update ID on the edge + Device savedDevice = createDevice(deviceUpdateMsg); + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setDeviceUpdateMsg(ctx.getDeviceUpdateMsgConstructor().constructDeviceUpdatedMsg(UpdateMsgType.DEVICE_CONFLICT_RPC_MESSAGE, savedDevice)) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + } else { + createDevice(deviceUpdateMsg); + } + } + break; + case ENTITY_UPDATED_RPC_MESSAGE: + updateDevice(deviceUpdateMsg); + break; + case ENTITY_DELETED_RPC_MESSAGE: + Device deviceToDelete = ctx.getDeviceService().findDeviceById(edge.getTenantId(), edgeDeviceId); + if (deviceToDelete != null) { + ctx.getDeviceService().unassignDeviceFromEdge(edge.getTenantId(), edgeDeviceId, edge.getId()); + } + break; + case UNRECOGNIZED: + log.error("Unsupported msg type"); + } + } + + private void updateDevice(DeviceUpdateMsg deviceUpdateMsg) { + DeviceId deviceId = new DeviceId(new UUID(deviceUpdateMsg.getIdMSB(), deviceUpdateMsg.getIdLSB())); + Device device = ctx.getDeviceService().findDeviceById(edge.getTenantId(), deviceId); + device.setName(deviceUpdateMsg.getName()); + device.setType(deviceUpdateMsg.getType()); + device.setLabel(deviceUpdateMsg.getLabel()); + device = ctx.getDeviceService().saveDevice(device); + updateDeviceCredentials(deviceUpdateMsg, device); + } + + private void updateDeviceCredentials(DeviceUpdateMsg deviceUpdateMsg, Device device) { + log.debug("Updating device credentials for device [{}]. New device credentials Id [{}], value [{}]", + device.getName(), deviceUpdateMsg.getCredentialsId(), deviceUpdateMsg.getCredentialsValue()); + + DeviceCredentials deviceCredentials = ctx.getDeviceCredentialsService().findDeviceCredentialsByDeviceId(edge.getTenantId(), device.getId()); + deviceCredentials.setCredentialsType(DeviceCredentialsType.valueOf(deviceUpdateMsg.getCredentialsType())); + deviceCredentials.setCredentialsId(deviceUpdateMsg.getCredentialsId()); + deviceCredentials.setCredentialsValue(deviceUpdateMsg.getCredentialsValue()); + ctx.getDeviceCredentialsService().updateDeviceCredentials(edge.getTenantId(), deviceCredentials); + log.debug("Updating device credentials for device [{}]. New device credentials Id [{}], value [{}]", + device.getName(), deviceUpdateMsg.getCredentialsId(), deviceUpdateMsg.getCredentialsValue()); + + } + + private Device createDevice(DeviceUpdateMsg deviceUpdateMsg) { + Device device; + try { + deviceCreationLock.lock(); + DeviceId deviceId = new DeviceId(new UUID(deviceUpdateMsg.getIdMSB(), deviceUpdateMsg.getIdLSB())); + device = new Device(); + device.setTenantId(edge.getTenantId()); + device.setCustomerId(edge.getCustomerId()); + device.setId(deviceId); + device.setName(deviceUpdateMsg.getName()); + device.setType(deviceUpdateMsg.getType()); + device.setLabel(deviceUpdateMsg.getLabel()); + device = ctx.getDeviceService().saveDevice(device); + device = ctx.getDeviceService().assignDeviceToEdge(edge.getTenantId(), device.getId(), edge.getId()); + createRelationFromEdge(device.getId()); + ctx.getRelationService().saveRelationAsync(TenantId.SYS_TENANT_ID, new EntityRelation(edge.getId(), device.getId(), "Created")); + ctx.getDeviceStateService().onDeviceAdded(device); + updateDeviceCredentials(deviceUpdateMsg, device); + } finally { + deviceCreationLock.unlock(); + } + return device; + } + private EntityId getAlarmOriginator(String entityName, org.thingsboard.server.common.data.EntityType entityType) { switch (entityType) { case DEVICE: @@ -643,36 +712,6 @@ public final class EdgeGrpcSession implements Closeable { } } - private Device getOrCreateDevice(String deviceName, String deviceType) { - Device device = ctx.getDeviceService().findDeviceByTenantIdAndName(edge.getTenantId(), deviceName); - if (device == null) { - entityCreationLock.lock(); - try { - return processGetOrCreateDevice(deviceName, deviceType); - } finally { - entityCreationLock.unlock(); - } - } - return device; - } - - private Device processGetOrCreateDevice(String deviceName, String deviceType) { - Device device = ctx.getDeviceService().findDeviceByTenantIdAndName(edge.getTenantId(), deviceName); - if (device == null) { - device = new Device(); - device.setName(deviceName); - device.setType(deviceType); - device.setTenantId(edge.getTenantId()); - device.setCustomerId(edge.getCustomerId()); - device = ctx.getDeviceService().saveDevice(device); - device = ctx.getDeviceService().assignDeviceToEdge(edge.getTenantId(), device.getId(), edge.getId()); - createRelationFromEdge(device.getId()); - ctx.getRelationService().saveRelationAsync(TenantId.SYS_TENANT_ID, new EntityRelation(edge.getId(), device.getId(), "Created")); - ctx.getDeviceStateService().onDeviceAdded(device); - } - return device; - } - private ConnectResponseMsg processConnect(ConnectRequestMsg request) { Optional optional = ctx.getEdgeService().findEdgeByRoutingKey(TenantId.SYS_TENANT_ID, request.getEdgeRoutingKey()); if (optional.isPresent()) { diff --git a/common/edge-api/src/main/proto/edge.proto b/common/edge-api/src/main/proto/edge.proto index ca32322977..9c38c71baa 100644 --- a/common/edge-api/src/main/proto/edge.proto +++ b/common/edge-api/src/main/proto/edge.proto @@ -94,14 +94,14 @@ enum UpdateMsgType { ALARM_ACK_RPC_MESSAGE = 3; ALARM_CLEAR_RPC_MESSAGE = 4; RULE_CHAIN_CUSTOM_MESSAGE = 5; + DEVICE_CONFLICT_RPC_MESSAGE = 6; } message EntityDataProto { string entityName = 1; - string entityType = 2; - int64 entityIdMSB = 3; - int64 entityIdLSB = 4; - bytes tbMsg = 5; + int64 entityIdMSB = 2; + int64 entityIdLSB = 3; + bytes tbMsg = 4; } message RuleChainUpdateMsg { @@ -156,7 +156,6 @@ message DashboardUpdateMsg { int64 idLSB = 3; string title = 4; string configuration = 5; - string groupName = 6; } message DeviceUpdateMsg { @@ -169,7 +168,6 @@ message DeviceUpdateMsg { string credentialsType = 7; string credentialsId = 8; string credentialsValue = 9; - string groupName = 10; } message AssetUpdateMsg { @@ -179,7 +177,6 @@ message AssetUpdateMsg { string name = 4; string type = 5; string label = 6; - string groupName = 7; } message EntityViewUpdateMsg { @@ -191,7 +188,6 @@ message EntityViewUpdateMsg { int64 entityIdMSB = 6; int64 entityIdLSB = 7; EdgeEntityType entityType = 8; - string groupName = 9; } message AlarmUpdateMsg { @@ -237,7 +233,6 @@ message UserUpdateMsg { string additionalInfo = 8; bool enabled = 9; string password = 10; - string groupName = 11; } message RuleChainMetadataRequestMsg { From fc1547c9297fb51cbd57bfbf9abf1be12056e8ea Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Wed, 10 Jun 2020 18:20:32 +0300 Subject: [PATCH 36/52] Changed Jenkins repo url scheme to https --- application/pom.xml | 2 +- msa/js-executor/pom.xml | 2 +- msa/tb-node/pom.xml | 2 +- msa/tb/pom.xml | 2 +- msa/transport/coap/pom.xml | 2 +- msa/transport/http/pom.xml | 2 +- msa/transport/mqtt/pom.xml | 2 +- msa/web-ui/pom.xml | 2 +- transport/coap/pom.xml | 2 +- transport/http/pom.xml | 2 +- transport/mqtt/pom.xml | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/application/pom.xml b/application/pom.xml index aa46947b4c..7df34c0a75 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -367,7 +367,7 @@ jenkins Jenkins Repository - http://repo.jenkins-ci.org/releases + https://repo.jenkins-ci.org/releases false diff --git a/msa/js-executor/pom.xml b/msa/js-executor/pom.xml index 16a0bb7570..64d0dcf695 100644 --- a/msa/js-executor/pom.xml +++ b/msa/js-executor/pom.xml @@ -215,7 +215,7 @@ jenkins Jenkins Repository - http://repo.jenkins-ci.org/releases + https://repo.jenkins-ci.org/releases false diff --git a/msa/tb-node/pom.xml b/msa/tb-node/pom.xml index d1a67a891a..c870e18654 100644 --- a/msa/tb-node/pom.xml +++ b/msa/tb-node/pom.xml @@ -180,7 +180,7 @@ jenkins Jenkins Repository - http://repo.jenkins-ci.org/releases + https://repo.jenkins-ci.org/releases false diff --git a/msa/tb/pom.xml b/msa/tb/pom.xml index 3a48db9ae3..6bcbdcb117 100644 --- a/msa/tb/pom.xml +++ b/msa/tb/pom.xml @@ -360,7 +360,7 @@ jenkins Jenkins Repository - http://repo.jenkins-ci.org/releases + https://repo.jenkins-ci.org/releases false diff --git a/msa/transport/coap/pom.xml b/msa/transport/coap/pom.xml index c611578819..76311a2b5f 100644 --- a/msa/transport/coap/pom.xml +++ b/msa/transport/coap/pom.xml @@ -180,7 +180,7 @@ jenkins Jenkins Repository - http://repo.jenkins-ci.org/releases + https://repo.jenkins-ci.org/releases false diff --git a/msa/transport/http/pom.xml b/msa/transport/http/pom.xml index 561106a50e..052fbd9e26 100644 --- a/msa/transport/http/pom.xml +++ b/msa/transport/http/pom.xml @@ -180,7 +180,7 @@ jenkins Jenkins Repository - http://repo.jenkins-ci.org/releases + https://repo.jenkins-ci.org/releases false diff --git a/msa/transport/mqtt/pom.xml b/msa/transport/mqtt/pom.xml index 9412dd422e..1e9c05e4b5 100644 --- a/msa/transport/mqtt/pom.xml +++ b/msa/transport/mqtt/pom.xml @@ -180,7 +180,7 @@ jenkins Jenkins Repository - http://repo.jenkins-ci.org/releases + https://repo.jenkins-ci.org/releases false diff --git a/msa/web-ui/pom.xml b/msa/web-ui/pom.xml index bf328a9153..cd6357c0bf 100644 --- a/msa/web-ui/pom.xml +++ b/msa/web-ui/pom.xml @@ -432,7 +432,7 @@ jenkins Jenkins Repository - http://repo.jenkins-ci.org/releases + https://repo.jenkins-ci.org/releases false diff --git a/transport/coap/pom.xml b/transport/coap/pom.xml index a4f24ed547..756a1cf947 100644 --- a/transport/coap/pom.xml +++ b/transport/coap/pom.xml @@ -123,7 +123,7 @@ jenkins Jenkins Repository - http://repo.jenkins-ci.org/releases + https://repo.jenkins-ci.org/releases false diff --git a/transport/http/pom.xml b/transport/http/pom.xml index 86e952822a..ccc4904fb7 100644 --- a/transport/http/pom.xml +++ b/transport/http/pom.xml @@ -123,7 +123,7 @@ jenkins Jenkins Repository - http://repo.jenkins-ci.org/releases + https://repo.jenkins-ci.org/releases false diff --git a/transport/mqtt/pom.xml b/transport/mqtt/pom.xml index 5caea325da..2344207715 100644 --- a/transport/mqtt/pom.xml +++ b/transport/mqtt/pom.xml @@ -123,7 +123,7 @@ jenkins Jenkins Repository - http://repo.jenkins-ci.org/releases + https://repo.jenkins-ci.org/releases false From 9d2e03922006697dbb306c8b35721e89f784f97f Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Thu, 11 Jun 2020 10:57:50 +0300 Subject: [PATCH 37/52] Version set to 2.5.3-SNAPSHOT --- application/pom.xml | 2 +- common/actor/pom.xml | 2 +- common/dao-api/pom.xml | 2 +- common/data/pom.xml | 2 +- common/message/pom.xml | 2 +- common/pom.xml | 2 +- common/queue/pom.xml | 2 +- common/transport/coap/pom.xml | 2 +- common/transport/http/pom.xml | 2 +- common/transport/mqtt/pom.xml | 2 +- common/transport/pom.xml | 2 +- common/transport/transport-api/pom.xml | 2 +- common/util/pom.xml | 2 +- dao/pom.xml | 2 +- msa/black-box-tests/pom.xml | 2 +- msa/js-executor/package-lock.json | 43 ++++++++-------------- msa/js-executor/package.json | 2 +- msa/js-executor/pom.xml | 2 +- msa/pom.xml | 2 +- msa/tb-node/pom.xml | 2 +- msa/tb/pom.xml | 2 +- msa/transport/coap/pom.xml | 2 +- msa/transport/http/pom.xml | 2 +- msa/transport/mqtt/pom.xml | 2 +- msa/transport/pom.xml | 2 +- msa/web-ui/package-lock.json | 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/mqtt/pom.xml | 2 +- transport/pom.xml | 2 +- ui/package-lock.json | 15 +++++--- ui/package.json | 2 +- ui/pom.xml | 2 +- 42 files changed, 67 insertions(+), 73 deletions(-) diff --git a/application/pom.xml b/application/pom.xml index 7df34c0a75..a20aeaf485 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.2-SNAPSHOT + 2.5.3-SNAPSHOT thingsboard application diff --git a/common/actor/pom.xml b/common/actor/pom.xml index bd1a199cac..9d101a2edb 100644 --- a/common/actor/pom.xml +++ b/common/actor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.2-SNAPSHOT + 2.5.3-SNAPSHOT common org.thingsboard.common diff --git a/common/dao-api/pom.xml b/common/dao-api/pom.xml index 547013c124..b880c42bfd 100644 --- a/common/dao-api/pom.xml +++ b/common/dao-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.2-SNAPSHOT + 2.5.3-SNAPSHOT common org.thingsboard.common diff --git a/common/data/pom.xml b/common/data/pom.xml index c3bea7cbf9..96892bdc59 100644 --- a/common/data/pom.xml +++ b/common/data/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.2-SNAPSHOT + 2.5.3-SNAPSHOT common org.thingsboard.common diff --git a/common/message/pom.xml b/common/message/pom.xml index 54df1085b8..066beec7a1 100644 --- a/common/message/pom.xml +++ b/common/message/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.2-SNAPSHOT + 2.5.3-SNAPSHOT common org.thingsboard.common diff --git a/common/pom.xml b/common/pom.xml index a695f2eca1..13b12bd3cd 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.2-SNAPSHOT + 2.5.3-SNAPSHOT thingsboard common diff --git a/common/queue/pom.xml b/common/queue/pom.xml index 8376d30484..2072d34b40 100644 --- a/common/queue/pom.xml +++ b/common/queue/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.2-SNAPSHOT + 2.5.3-SNAPSHOT common org.thingsboard.common diff --git a/common/transport/coap/pom.xml b/common/transport/coap/pom.xml index cdc4978892..5ec305329c 100644 --- a/common/transport/coap/pom.xml +++ b/common/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 2.5.2-SNAPSHOT + 2.5.3-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/http/pom.xml b/common/transport/http/pom.xml index 5f715a7a27..4089f3c0d8 100644 --- a/common/transport/http/pom.xml +++ b/common/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 2.5.2-SNAPSHOT + 2.5.3-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/mqtt/pom.xml b/common/transport/mqtt/pom.xml index 3fd09077f4..7a3e81348c 100644 --- a/common/transport/mqtt/pom.xml +++ b/common/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 2.5.2-SNAPSHOT + 2.5.3-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/pom.xml b/common/transport/pom.xml index 9f10a8959d..3dfb3ee695 100644 --- a/common/transport/pom.xml +++ b/common/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.2-SNAPSHOT + 2.5.3-SNAPSHOT common org.thingsboard.common diff --git a/common/transport/transport-api/pom.xml b/common/transport/transport-api/pom.xml index a836a98ebb..2724c0eb83 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 - 2.5.2-SNAPSHOT + 2.5.3-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/util/pom.xml b/common/util/pom.xml index d32c56bc1b..25b8925b9a 100644 --- a/common/util/pom.xml +++ b/common/util/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.2-SNAPSHOT + 2.5.3-SNAPSHOT common org.thingsboard.common diff --git a/dao/pom.xml b/dao/pom.xml index 23b6f8f501..90417b8a53 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.2-SNAPSHOT + 2.5.3-SNAPSHOT thingsboard dao diff --git a/msa/black-box-tests/pom.xml b/msa/black-box-tests/pom.xml index f60afff08d..4880410053 100644 --- a/msa/black-box-tests/pom.xml +++ b/msa/black-box-tests/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 2.5.2-SNAPSHOT + 2.5.3-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/js-executor/package-lock.json b/msa/js-executor/package-lock.json index 818b77550e..5636c4f586 100644 --- a/msa/js-executor/package-lock.json +++ b/msa/js-executor/package-lock.json @@ -1,6 +1,6 @@ { "name": "thingsboard-js-executor", - "version": "2.5.1", + "version": "2.5.3", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -1461,7 +1461,7 @@ }, "enabled": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz", + "resolved": "http://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz", "integrity": "sha1-ll9lE9LC0cX0ZStkouM5ZGf8L5M=", "requires": { "env-variable": "0.0.x" @@ -1740,7 +1740,7 @@ }, "fecha": { "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz", + "resolved": "http://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz", "integrity": "sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg==" }, "file-stream-rotator": { @@ -1872,14 +1872,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1894,20 +1892,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -2024,8 +2019,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -2037,7 +2031,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -2052,7 +2045,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -2164,8 +2156,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -2177,7 +2168,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -2299,7 +2289,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -2401,7 +2390,7 @@ }, "get-stream": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", "dev": true }, @@ -2558,7 +2547,7 @@ }, "got": { "version": "6.7.1", - "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", + "resolved": "http://registry.npmjs.org/got/-/got-6.7.1.tgz", "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", "dev": true, "requires": { @@ -2890,7 +2879,7 @@ }, "is-obj": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "resolved": "http://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", "dev": true }, @@ -3252,7 +3241,7 @@ }, "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" }, "mixin-deep": { @@ -3551,7 +3540,7 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, @@ -3990,7 +3979,7 @@ }, "safe-regex": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, "requires": { @@ -4300,7 +4289,7 @@ }, "strip-eof": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "resolved": "http://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, diff --git a/msa/js-executor/package.json b/msa/js-executor/package.json index be49865fae..cedef9f059 100644 --- a/msa/js-executor/package.json +++ b/msa/js-executor/package.json @@ -1,7 +1,7 @@ { "name": "thingsboard-js-executor", "private": true, - "version": "2.5.2", + "version": "2.5.3", "description": "ThingsBoard JavaScript Executor Microservice", "main": "server.js", "bin": "server.js", diff --git a/msa/js-executor/pom.xml b/msa/js-executor/pom.xml index 64d0dcf695..ab810ab4da 100644 --- a/msa/js-executor/pom.xml +++ b/msa/js-executor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.2-SNAPSHOT + 2.5.3-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/pom.xml b/msa/pom.xml index e37ecf0af8..deee02a8a4 100644 --- a/msa/pom.xml +++ b/msa/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.2-SNAPSHOT + 2.5.3-SNAPSHOT thingsboard msa diff --git a/msa/tb-node/pom.xml b/msa/tb-node/pom.xml index c870e18654..4a20d19a90 100644 --- a/msa/tb-node/pom.xml +++ b/msa/tb-node/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.2-SNAPSHOT + 2.5.3-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/tb/pom.xml b/msa/tb/pom.xml index 6bcbdcb117..b27fe66615 100644 --- a/msa/tb/pom.xml +++ b/msa/tb/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.2-SNAPSHOT + 2.5.3-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/transport/coap/pom.xml b/msa/transport/coap/pom.xml index 76311a2b5f..373e6416ff 100644 --- a/msa/transport/coap/pom.xml +++ b/msa/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 2.5.2-SNAPSHOT + 2.5.3-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/http/pom.xml b/msa/transport/http/pom.xml index 052fbd9e26..e3e1d47a9b 100644 --- a/msa/transport/http/pom.xml +++ b/msa/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 2.5.2-SNAPSHOT + 2.5.3-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/mqtt/pom.xml b/msa/transport/mqtt/pom.xml index 1e9c05e4b5..b84d064f31 100644 --- a/msa/transport/mqtt/pom.xml +++ b/msa/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 2.5.2-SNAPSHOT + 2.5.3-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/pom.xml b/msa/transport/pom.xml index cc2b196f2c..c4c17d5b3e 100644 --- a/msa/transport/pom.xml +++ b/msa/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.2-SNAPSHOT + 2.5.3-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/web-ui/package-lock.json b/msa/web-ui/package-lock.json index 695ef56342..b7785ef48b 100644 --- a/msa/web-ui/package-lock.json +++ b/msa/web-ui/package-lock.json @@ -1,6 +1,6 @@ { "name": "thingsboard-web-ui", - "version": "2.5.1", + "version": "2.5.3", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/msa/web-ui/package.json b/msa/web-ui/package.json index 9e969242c8..749fb2df72 100644 --- a/msa/web-ui/package.json +++ b/msa/web-ui/package.json @@ -1,7 +1,7 @@ { "name": "thingsboard-web-ui", "private": true, - "version": "2.5.2", + "version": "2.5.3", "description": "ThingsBoard Web UI Microservice", "main": "server.js", "bin": "server.js", diff --git a/msa/web-ui/pom.xml b/msa/web-ui/pom.xml index cd6357c0bf..54e3094db8 100644 --- a/msa/web-ui/pom.xml +++ b/msa/web-ui/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.2-SNAPSHOT + 2.5.3-SNAPSHOT msa org.thingsboard.msa diff --git a/netty-mqtt/pom.xml b/netty-mqtt/pom.xml index 00f5a9ef4f..eb32d84f20 100644 --- a/netty-mqtt/pom.xml +++ b/netty-mqtt/pom.xml @@ -19,11 +19,11 @@ 4.0.0 org.thingsboard - 2.5.2-SNAPSHOT + 2.5.3-SNAPSHOT thingsboard netty-mqtt - 2.5.2-SNAPSHOT + 2.5.3-SNAPSHOT jar Netty MQTT Client diff --git a/pom.xml b/pom.xml index 04479347a5..377c52fb66 100755 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard thingsboard - 2.5.2-SNAPSHOT + 2.5.3-SNAPSHOT pom Thingsboard diff --git a/rest-client/pom.xml b/rest-client/pom.xml index b2a9ecd668..edb6e9be2b 100644 --- a/rest-client/pom.xml +++ b/rest-client/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.2-SNAPSHOT + 2.5.3-SNAPSHOT thingsboard rest-client diff --git a/rule-engine/pom.xml b/rule-engine/pom.xml index c7a1e8eb79..66f8cec913 100644 --- a/rule-engine/pom.xml +++ b/rule-engine/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.2-SNAPSHOT + 2.5.3-SNAPSHOT thingsboard rule-engine diff --git a/rule-engine/rule-engine-api/pom.xml b/rule-engine/rule-engine-api/pom.xml index 792fcd329d..49386f1f86 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 - 2.5.2-SNAPSHOT + 2.5.3-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 3df5494af9..1b6fc2fc74 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 - 2.5.2-SNAPSHOT + 2.5.3-SNAPSHOT rule-engine org.thingsboard.rule-engine diff --git a/tools/pom.xml b/tools/pom.xml index d5b17b26c9..6cf3467015 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.2-SNAPSHOT + 2.5.3-SNAPSHOT thingsboard tools diff --git a/transport/coap/pom.xml b/transport/coap/pom.xml index 756a1cf947..f265d6e9cf 100644 --- a/transport/coap/pom.xml +++ b/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.2-SNAPSHOT + 2.5.3-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/http/pom.xml b/transport/http/pom.xml index ccc4904fb7..fddc86a8da 100644 --- a/transport/http/pom.xml +++ b/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.2-SNAPSHOT + 2.5.3-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/mqtt/pom.xml b/transport/mqtt/pom.xml index 2344207715..cb37b62a34 100644 --- a/transport/mqtt/pom.xml +++ b/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.2-SNAPSHOT + 2.5.3-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/pom.xml b/transport/pom.xml index 20d4f188b2..9f2d726073 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.2-SNAPSHOT + 2.5.3-SNAPSHOT thingsboard transport diff --git a/ui/package-lock.json b/ui/package-lock.json index 4de10ff7d8..709137d524 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -1,6 +1,6 @@ { "name": "thingsboard", - "version": "2.5.1", + "version": "2.5.3", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -5238,7 +5238,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -5662,7 +5663,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -5718,6 +5720,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -5761,12 +5764,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, diff --git a/ui/package.json b/ui/package.json index 0d793853cd..c507bbe7e9 100644 --- a/ui/package.json +++ b/ui/package.json @@ -1,7 +1,7 @@ { "name": "thingsboard", "private": true, - "version": "2.5.2", + "version": "2.5.3", "description": "ThingsBoard UI", "licenses": [ { diff --git a/ui/pom.xml b/ui/pom.xml index cee463337e..00b8d3e87b 100644 --- a/ui/pom.xml +++ b/ui/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 2.5.2-SNAPSHOT + 2.5.3-SNAPSHOT thingsboard org.thingsboard From eaff2406db49298c23cd30d67bc6a4b4906ef6b9 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Thu, 11 Jun 2020 14:25:32 +0300 Subject: [PATCH 38/52] Hotfix of upgrade script. --- .../service/install/CassandraTsDatabaseUpgradeService.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/application/src/main/java/org/thingsboard/server/service/install/CassandraTsDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/CassandraTsDatabaseUpgradeService.java index 4bc68e92bc..103e8090d9 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/CassandraTsDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/CassandraTsDatabaseUpgradeService.java @@ -48,6 +48,8 @@ public class CassandraTsDatabaseUpgradeService extends AbstractCassandraDatabase } log.info("Schema updated."); break; + case "2.5.0": + break; default: throw new RuntimeException("Unable to upgrade Cassandra database, unsupported fromVersion: " + fromVersion); } From a726b3486025869eb31dad644ea38030e448d9d7 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Thu, 11 Jun 2020 16:49:45 +0300 Subject: [PATCH 39/52] Send updated in case CONFLICT msg --- .../edge/rpc/constructor/DeviceUpdateMsgConstructor.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceUpdateMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceUpdateMsgConstructor.java index 98e7d5aa3e..791abc8cf7 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceUpdateMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceUpdateMsgConstructor.java @@ -42,7 +42,8 @@ public class DeviceUpdateMsgConstructor { builder.setLabel(device.getLabel()); } if (msgType.equals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE) || - msgType.equals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE)) { + msgType.equals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE) || + msgType.equals(UpdateMsgType.DEVICE_CONFLICT_RPC_MESSAGE)) { DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getTenantId(), device.getId()); if (deviceCredentials != null) { From 3a416131c6f93194746f970d82eebf1bd465785e Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Thu, 11 Jun 2020 19:25:31 +0300 Subject: [PATCH 40/52] Added relation update msg. Push relation to the edge on sync --- .../service/edge/EdgeContextComponent.java | 9 +- .../service/edge/rpc/EdgeGrpcSession.java | 17 ++- .../RelationUpdateMsgConstructor.java | 45 +++++++ ...rvice.java => DefaultSyncEdgeService.java} | 118 ++++++++++++++---- ...tEdgeService.java => SyncEdgeService.java} | 6 +- .../common/data/edge/EdgeQueueEntityType.java | 2 +- common/edge-api/src/main/proto/edge.proto | 14 +++ 7 files changed, 182 insertions(+), 29 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RelationUpdateMsgConstructor.java rename application/src/main/java/org/thingsboard/server/service/edge/rpc/init/{DefaultInitEdgeService.java => DefaultSyncEdgeService.java} (69%) rename application/src/main/java/org/thingsboard/server/service/edge/rpc/init/{InitEdgeService.java => SyncEdgeService.java} (85%) 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 f936ee82a5..6959a522bf 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 @@ -36,8 +36,9 @@ import org.thingsboard.server.service.edge.rpc.constructor.AssetUpdateMsgConstru import org.thingsboard.server.service.edge.rpc.constructor.DashboardUpdateMsgConstructor; import org.thingsboard.server.service.edge.rpc.constructor.DeviceUpdateMsgConstructor; import org.thingsboard.server.service.edge.rpc.constructor.EntityViewUpdateMsgConstructor; +import org.thingsboard.server.service.edge.rpc.constructor.RelationUpdateMsgConstructor; import org.thingsboard.server.service.edge.rpc.constructor.UserUpdateMsgConstructor; -import org.thingsboard.server.service.edge.rpc.init.InitEdgeService; +import org.thingsboard.server.service.edge.rpc.init.SyncEdgeService; import org.thingsboard.server.service.edge.rpc.constructor.RuleChainUpdateMsgConstructor; import org.thingsboard.server.service.queue.TbClusterService; import org.thingsboard.server.service.state.DeviceStateService; @@ -100,7 +101,7 @@ public class EdgeContextComponent { @Lazy @Autowired - private InitEdgeService initEdgeService; + private SyncEdgeService syncEdgeService; @Lazy @Autowired @@ -130,6 +131,10 @@ public class EdgeContextComponent { @Autowired private UserUpdateMsgConstructor userUpdateMsgConstructor; + @Lazy + @Autowired + private RelationUpdateMsgConstructor relationUpdateMsgConstructor; + @Lazy @Autowired private EdgeEventStorageSettings edgeEventStorageSettings; 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 e6b68f486a..d029b5ff20 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 @@ -141,7 +141,7 @@ public final class EdgeGrpcSession implements Closeable { outputStream.onError(new RuntimeException(responseMsg.getErrorMsg())); } if (ConnectResponseCode.ACCEPTED == responseMsg.getResponseCode()) { - ctx.getInitEdgeService().init(edge, outputStream); + ctx.getSyncEdgeService().sync(edge, outputStream); } } if (connected) { @@ -360,6 +360,10 @@ public final class EdgeGrpcSession implements Closeable { User user = objectMapper.readValue(entry.getData(), User.class); onUserUpdated(msgType, user); break; + case RELATION: + EntityRelation entityRelation = objectMapper.readValue(entry.getData(), EntityRelation.class); + onEntityRelationUpdated(msgType, entityRelation); + break; } } @@ -463,6 +467,15 @@ public final class EdgeGrpcSession implements Closeable { .build()); } + private void onEntityRelationUpdated(UpdateMsgType msgType, EntityRelation entityRelation) { + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setRelationUpdateMsg(ctx.getRelationUpdateMsgConstructor().constructRelationUpdatedMsg(msgType, entityRelation)) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + } + private UpdateMsgType getResponseMsgType(String msgType) { if (msgType.equals(SessionMsgType.POST_TELEMETRY_REQUEST.name()) || msgType.equals(SessionMsgType.POST_ATTRIBUTES_REQUEST.name()) || @@ -548,7 +561,7 @@ public final class EdgeGrpcSession implements Closeable { } if (uplinkMsg.getRuleChainMetadataRequestMsgList() != null && !uplinkMsg.getRuleChainMetadataRequestMsgList().isEmpty()) { for (RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg : uplinkMsg.getRuleChainMetadataRequestMsgList()) { - ctx.getInitEdgeService().initRuleChainMetadata(edge, ruleChainMetadataRequestMsg, outputStream); + ctx.getSyncEdgeService().syncRuleChainMetadata(edge, ruleChainMetadataRequestMsg, outputStream); } } } catch (Exception e) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RelationUpdateMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RelationUpdateMsgConstructor.java new file mode 100644 index 0000000000..1c0af319f3 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RelationUpdateMsgConstructor.java @@ -0,0 +1,45 @@ +/** + * Copyright © 2016-2020 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.server.common.data.relation.EntityRelation; +import org.thingsboard.server.dao.util.mapping.JacksonUtil; +import org.thingsboard.server.gen.edge.RelationUpdateMsg; +import org.thingsboard.server.gen.edge.UpdateMsgType; + +@Component +@Slf4j +public class RelationUpdateMsgConstructor { + + public RelationUpdateMsg constructRelationUpdatedMsg(UpdateMsgType msgType, EntityRelation entityRelation) { + RelationUpdateMsg.Builder builder = RelationUpdateMsg.newBuilder() + .setMsgType(msgType) + .setFromIdMSB(entityRelation.getFrom().getId().getMostSignificantBits()) + .setFromIdLSB(entityRelation.getFrom().getId().getLeastSignificantBits()) + .setFromEntityType(entityRelation.getFrom().getEntityType().name()) + .setToIdMSB(entityRelation.getTo().getId().getMostSignificantBits()) + .setToIdLSB(entityRelation.getTo().getId().getLeastSignificantBits()) + .setToEntityType(entityRelation.getTo().getEntityType().name()) + .setType(entityRelation.getType()) + .setAdditionalInfo(JacksonUtil.toString(entityRelation.getAdditionalInfo())); + if (entityRelation.getTypeGroup() != null) { + builder.setTypeGroup(entityRelation.getTypeGroup().name()); + } + return builder.build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/DefaultInitEdgeService.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/DefaultSyncEdgeService.java similarity index 69% rename from application/src/main/java/org/thingsboard/server/service/edge/rpc/init/DefaultInitEdgeService.java rename to application/src/main/java/org/thingsboard/server/service/edge/rpc/init/DefaultSyncEdgeService.java index 192fb8395b..d51474046e 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/DefaultInitEdgeService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/DefaultSyncEdgeService.java @@ -16,6 +16,8 @@ package org.thingsboard.server.service.edge.rpc.init; import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.MoreExecutors; import io.grpc.stub.StreamObserver; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -24,25 +26,31 @@ import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.DashboardInfo; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntityView; +import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.RuleChainId; -import org.thingsboard.server.common.data.page.TextPageData; -import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.page.TimePageData; import org.thingsboard.server.common.data.page.TimePageLink; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.EntityRelationsQuery; +import org.thingsboard.server.common.data.relation.EntitySearchDirection; +import org.thingsboard.server.common.data.relation.RelationsSearchParameters; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.entityview.EntityViewService; +import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.gen.edge.AssetUpdateMsg; import org.thingsboard.server.gen.edge.DashboardUpdateMsg; import org.thingsboard.server.gen.edge.DeviceUpdateMsg; import org.thingsboard.server.gen.edge.EntityUpdateMsg; import org.thingsboard.server.gen.edge.EntityViewUpdateMsg; +import org.thingsboard.server.gen.edge.RelationUpdateMsg; import org.thingsboard.server.gen.edge.ResponseMsg; import org.thingsboard.server.gen.edge.RuleChainMetadataRequestMsg; import org.thingsboard.server.gen.edge.RuleChainMetadataUpdateMsg; @@ -52,18 +60,25 @@ import org.thingsboard.server.service.edge.rpc.constructor.AssetUpdateMsgConstru import org.thingsboard.server.service.edge.rpc.constructor.DashboardUpdateMsgConstructor; import org.thingsboard.server.service.edge.rpc.constructor.DeviceUpdateMsgConstructor; import org.thingsboard.server.service.edge.rpc.constructor.EntityViewUpdateMsgConstructor; +import org.thingsboard.server.service.edge.rpc.constructor.RelationUpdateMsgConstructor; import org.thingsboard.server.service.edge.rpc.constructor.RuleChainUpdateMsgConstructor; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import java.util.UUID; -import java.util.concurrent.Future; @Service @Slf4j -public class DefaultInitEdgeService implements InitEdgeService { +public class DefaultSyncEdgeService implements SyncEdgeService { @Autowired private RuleChainService ruleChainService; + @Autowired + private RelationService relationService; + @Autowired private DeviceService deviceService; @@ -79,6 +94,9 @@ public class DefaultInitEdgeService implements InitEdgeService { @Autowired private RuleChainUpdateMsgConstructor ruleChainUpdateMsgConstructor; + @Autowired + private RelationUpdateMsgConstructor relationUpdateMsgConstructor; + @Autowired private DeviceUpdateMsgConstructor deviceUpdateMsgConstructor; @@ -92,15 +110,68 @@ public class DefaultInitEdgeService implements InitEdgeService { private DashboardUpdateMsgConstructor dashboardUpdateMsgConstructor; @Override - public void init(Edge edge, StreamObserver outputStream) { - initRuleChains(edge, outputStream); - initDevices(edge, outputStream); - initAssets(edge, outputStream); - initEntityViews(edge, outputStream); - initDashboards(edge, outputStream); + public void sync(Edge edge, StreamObserver outputStream) { + Set pushedEntityIds = new HashSet<>(); + syncRuleChains(edge, pushedEntityIds, outputStream); + syncDevices(edge, pushedEntityIds, outputStream); + syncAssets(edge, pushedEntityIds, outputStream); + syncEntityViews(edge, pushedEntityIds, outputStream); + syncDashboards(edge, pushedEntityIds, outputStream); + syncRelations(edge, pushedEntityIds, outputStream); + } + + private void syncRelations(Edge edge, Set pushedEntityIds, StreamObserver outputStream) { + if (!pushedEntityIds.isEmpty()) { + List>> futures = new ArrayList<>(); + for (EntityId entityId : pushedEntityIds) { + futures.add(syncRelations(edge, entityId, EntitySearchDirection.FROM)); + futures.add(syncRelations(edge, entityId, EntitySearchDirection.TO)); + } + ListenableFuture>> relationsListFuture = Futures.allAsList(futures); + Futures.transform(relationsListFuture, relationsList -> { + try { + Set uniqueEntityRelations = new HashSet<>(); + if (!relationsList.isEmpty()) { + for (List entityRelations : relationsList) { + if (!entityRelations.isEmpty()) { + uniqueEntityRelations.addAll(entityRelations); + } + } + } + if (!uniqueEntityRelations.isEmpty()) { + log.trace("[{}] [{}] relation(s) are going to be pushed to edge.", edge.getId(), uniqueEntityRelations.size()); + for (EntityRelation relation : uniqueEntityRelations) { + try { + RelationUpdateMsg relationUpdateMsg = + relationUpdateMsgConstructor.constructRelationUpdatedMsg( + UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, + relation); + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setRelationUpdateMsg(relationUpdateMsg) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + } catch (Exception e) { + log.error("Exception during loading relation [{}] to edge on init!", relation, e); + } + } + } + } catch (Exception e) { + log.error("Exception during loading relation(s) to edge on init!", e); + } + return null; + }, MoreExecutors.directExecutor()); + } + } + + private ListenableFuture> syncRelations(Edge edge, EntityId entityId, EntitySearchDirection direction) { + EntityRelationsQuery query = new EntityRelationsQuery(); + query.setParameters(new RelationsSearchParameters(entityId, direction, -1, false)); + return relationService.findByQuery(edge.getTenantId(), query); } - private void initDevices(Edge edge, StreamObserver outputStream) { + private void syncDevices(Edge edge, Set pushedEntityIds, StreamObserver outputStream) { try { TimePageLink pageLink = new TimePageLink(100); TimePageData pageData; @@ -119,6 +190,7 @@ public class DefaultInitEdgeService implements InitEdgeService { outputStream.onNext(ResponseMsg.newBuilder() .setEntityUpdateMsg(entityUpdateMsg) .build()); + pushedEntityIds.add(device.getId()); } } if (pageData.hasNext()) { @@ -126,11 +198,11 @@ public class DefaultInitEdgeService implements InitEdgeService { } } while (pageData.hasNext()); } catch (Exception e) { - log.error("Exception during loading edge device(s) on init!"); + log.error("Exception during loading edge device(s) on init!", e); } } - private void initAssets(Edge edge, StreamObserver outputStream) { + private void syncAssets(Edge edge, Set pushedEntityIds, StreamObserver outputStream) { try { TimePageLink pageLink = new TimePageLink(100); TimePageData pageData; @@ -149,6 +221,7 @@ public class DefaultInitEdgeService implements InitEdgeService { outputStream.onNext(ResponseMsg.newBuilder() .setEntityUpdateMsg(entityUpdateMsg) .build()); + pushedEntityIds.add(asset.getId()); } } if (pageData.hasNext()) { @@ -156,11 +229,11 @@ public class DefaultInitEdgeService implements InitEdgeService { } } while (pageData.hasNext()); } catch (Exception e) { - log.error("Exception during loading edge asset(s) on init!"); + log.error("Exception during loading edge asset(s) on init!", e); } } - private void initEntityViews(Edge edge, StreamObserver outputStream) { + private void syncEntityViews(Edge edge, Set pushedEntityIds, StreamObserver outputStream) { try { TimePageLink pageLink = new TimePageLink(100); TimePageData pageData; @@ -179,6 +252,7 @@ public class DefaultInitEdgeService implements InitEdgeService { outputStream.onNext(ResponseMsg.newBuilder() .setEntityUpdateMsg(entityUpdateMsg) .build()); + pushedEntityIds.add(entityView.getId()); } } if (pageData.hasNext()) { @@ -186,11 +260,11 @@ public class DefaultInitEdgeService implements InitEdgeService { } } while (pageData.hasNext()); } catch (Exception e) { - log.error("Exception during loading edge entity view(s) on init!"); + log.error("Exception during loading edge entity view(s) on init!", e); } } - private void initDashboards(Edge edge, StreamObserver outputStream) { + private void syncDashboards(Edge edge, Set pushedEntityIds, StreamObserver outputStream) { try { TimePageLink pageLink = new TimePageLink(100); TimePageData pageData; @@ -210,6 +284,7 @@ public class DefaultInitEdgeService implements InitEdgeService { outputStream.onNext(ResponseMsg.newBuilder() .setEntityUpdateMsg(entityUpdateMsg) .build()); + pushedEntityIds.add(dashboard.getId()); } } if (pageData.hasNext()) { @@ -217,11 +292,11 @@ public class DefaultInitEdgeService implements InitEdgeService { } } while (pageData.hasNext()); } catch (Exception e) { - log.error("Exception during loading edge dashboard(s) on init!"); + log.error("Exception during loading edge dashboard(s) on init!", e); } } - private void initRuleChains(Edge edge, StreamObserver outputStream) { + private void syncRuleChains(Edge edge, Set pushedEntityIds, StreamObserver outputStream) { try { TimePageLink pageLink = new TimePageLink(100); TimePageData pageData; @@ -241,6 +316,7 @@ public class DefaultInitEdgeService implements InitEdgeService { outputStream.onNext(ResponseMsg.newBuilder() .setEntityUpdateMsg(entityUpdateMsg) .build()); + pushedEntityIds.add(ruleChain.getId()); } } if (pageData.hasNext()) { @@ -248,12 +324,12 @@ public class DefaultInitEdgeService implements InitEdgeService { } } while (pageData.hasNext()); } catch (Exception e) { - log.error("Exception during loading edge rule chain(s) on init!"); + log.error("Exception during loading edge rule chain(s) on init!", e); } } @Override - public void initRuleChainMetadata(Edge edge, RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg, StreamObserver outputStream) { + public void syncRuleChainMetadata(Edge edge, RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg, StreamObserver outputStream) { if (ruleChainMetadataRequestMsg.getRuleChainIdMSB() != 0 && ruleChainMetadataRequestMsg.getRuleChainIdLSB() != 0) { RuleChainId ruleChainId = new RuleChainId(new UUID(ruleChainMetadataRequestMsg.getRuleChainIdMSB(), ruleChainMetadataRequestMsg.getRuleChainIdLSB())); RuleChainMetaData ruleChainMetaData = ruleChainService.loadRuleChainMetaData(edge.getTenantId(), ruleChainId); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/InitEdgeService.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/SyncEdgeService.java similarity index 85% rename from application/src/main/java/org/thingsboard/server/service/edge/rpc/init/InitEdgeService.java rename to application/src/main/java/org/thingsboard/server/service/edge/rpc/init/SyncEdgeService.java index 8aeb89bf23..c83a9ec3b0 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/InitEdgeService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/SyncEdgeService.java @@ -20,9 +20,9 @@ import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.gen.edge.ResponseMsg; import org.thingsboard.server.gen.edge.RuleChainMetadataRequestMsg; -public interface InitEdgeService { +public interface SyncEdgeService { - void init(Edge edge, StreamObserver outputStream); + void sync(Edge edge, StreamObserver outputStream); - void initRuleChainMetadata(Edge edge, RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg, StreamObserver outputStream); + void syncRuleChainMetadata(Edge edge, RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg, StreamObserver outputStream); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeQueueEntityType.java b/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeQueueEntityType.java index accae613c1..7ba316a529 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeQueueEntityType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeQueueEntityType.java @@ -16,5 +16,5 @@ package org.thingsboard.server.common.data.edge; public enum EdgeQueueEntityType { - DASHBOARD, ASSET, DEVICE, ENTITY_VIEW, ALARM, RULE_CHAIN, RULE_CHAIN_METADATA, EDGE, USER, CUSTOMER + DASHBOARD, ASSET, DEVICE, ENTITY_VIEW, ALARM, RULE_CHAIN, RULE_CHAIN_METADATA, EDGE, USER, CUSTOMER, RELATION } diff --git a/common/edge-api/src/main/proto/edge.proto b/common/edge-api/src/main/proto/edge.proto index 9c38c71baa..2256acccf8 100644 --- a/common/edge-api/src/main/proto/edge.proto +++ b/common/edge-api/src/main/proto/edge.proto @@ -54,6 +54,7 @@ message EntityUpdateMsg { AlarmUpdateMsg alarmUpdateMsg = 7; UserUpdateMsg userUpdateMsg = 8; CustomerUpdateMsg customerUpdateMsg = 9; + RelationUpdateMsg relationUpdateMsg = 10; } enum RequestMsgType { @@ -222,6 +223,19 @@ message CustomerUpdateMsg { string additionalInfo = 13; } +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; + string typeGroup = 9; + string additionalInfo = 10; +} + message UserUpdateMsg { UpdateMsgType msgType = 1; int64 idMSB = 2; From 3fc18988be0cb633b0da8ef08272333a91c8a237 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Thu, 11 Jun 2020 20:10:47 +0300 Subject: [PATCH 41/52] Added update relation msg functionality --- .../server/controller/UserController.java | 90 +++++++++ .../service/edge/EdgeContextComponent.java | 10 +- .../service/edge/rpc/EdgeGrpcSession.java | 4 +- .../RelationUpdateMsgConstructor.java | 2 +- .../edge/rpc/init/DefaultSyncEdgeService.java | 182 +++++++++++------- .../edge/rpc/init/SyncEdgeService.java | 5 +- .../server/dao/user/UserService.java | 11 ++ .../common/data/edge/EdgeQueueEntityType.java | 2 +- .../server/dao/asset/BaseAssetService.java | 2 - .../dao/entity/AbstractEntityService.java | 3 + .../server/dao/sql/user/JpaUserDao.java | 28 +++ .../server/dao/user/CassandraUserDao.java | 27 +++ .../thingsboard/server/dao/user/UserDao.java | 12 ++ .../server/dao/user/UserServiceImpl.java | 66 +++++++ .../dao/service/BaseRuleChainServiceTest.java | 1 + 15 files changed, 365 insertions(+), 80 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/UserController.java b/application/src/main/java/org/thingsboard/server/controller/UserController.java index b3d66adc53..13409e0d07 100644 --- a/application/src/main/java/org/thingsboard/server/controller/UserController.java +++ b/application/src/main/java/org/thingsboard/server/controller/UserController.java @@ -35,13 +35,17 @@ import org.thingsboard.rule.engine.api.MailService; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardException; 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.id.UserId; import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.common.data.page.TimePageData; +import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.UserCredentials; import org.thingsboard.server.queue.util.TbCoreComponent; @@ -56,6 +60,8 @@ import org.thingsboard.server.utils.MiscUtils; import javax.servlet.http.HttpServletRequest; +import static org.thingsboard.server.controller.EdgeController.EDGE_ID; + @RestController @TbCoreComponent @RequestMapping("/api") @@ -300,4 +306,88 @@ public class UserController extends BaseController { throw handleException(e); } } + + @PreAuthorize("hasAuthority('TENANT_ADMIN')") + @RequestMapping(value = "/edge/{edgeId}/user/{userId}", method = RequestMethod.POST) + @ResponseBody + public User assignUserToEdge(@PathVariable(EDGE_ID) String strEdgeId, + @PathVariable(USER_ID) String strUserId) throws ThingsboardException { + checkParameter(EDGE_ID, strEdgeId); + checkParameter(USER_ID, strUserId); + try { + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); + Edge edge = checkEdgeId(edgeId, Operation.READ); + + UserId userId = new UserId(toUUID(strUserId)); + checkUserId(userId, Operation.ASSIGN_TO_EDGE); + + User savedUser = checkNotNull(userService.assignUserToEdge(getTenantId(), userId, edgeId)); + + logEntityAction(userId, savedUser, + savedUser.getCustomerId(), + ActionType.ASSIGNED_TO_EDGE, null, strUserId, strEdgeId, edge.getName()); + + return savedUser; + } catch (Exception e) { + + logEntityAction(emptyId(EntityType.USER), null, + null, + ActionType.ASSIGNED_TO_EDGE, e, strUserId, strEdgeId); + + throw handleException(e); + } + } + + @PreAuthorize("hasAuthority('TENANT_ADMIN')") + @RequestMapping(value = "/edge/{edgeId}/user/{userId}", method = RequestMethod.DELETE) + @ResponseBody + public User unassignUserFromEdge(@PathVariable(EDGE_ID) String strEdgeId, + @PathVariable(USER_ID) String strUserId) throws ThingsboardException { + checkParameter(EDGE_ID, strEdgeId); + checkParameter(USER_ID, strUserId); + try { + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); + Edge edge = checkEdgeId(edgeId, Operation.READ); + + UserId userId = new UserId(toUUID(strUserId)); + User user = checkUserId(userId, Operation.UNASSIGN_FROM_EDGE); + + User savedUser = checkNotNull(userService.unassignUserFromEdge(getTenantId(), userId, edgeId)); + + logEntityAction(userId, savedUser, + savedUser.getCustomerId(), + ActionType.UNASSIGNED_FROM_EDGE, null, strUserId, edge.getId().toString(), edge.getName()); + + return savedUser; + } catch (Exception e) { + + logEntityAction(emptyId(EntityType.USER), null, + null, + ActionType.UNASSIGNED_FROM_EDGE, e, strUserId); + + throw handleException(e); + } + } + + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") + @RequestMapping(value = "/edge/{edgeId}/users", params = {"limit"}, method = RequestMethod.GET) + @ResponseBody + public TimePageData getEdgeUsers( + @PathVariable(EDGE_ID) String strEdgeId, + @RequestParam int limit, + @RequestParam(required = false) Long startTime, + @RequestParam(required = false) Long endTime, + @RequestParam(required = false, defaultValue = "false") boolean ascOrder, + @RequestParam(required = false) String offset) throws ThingsboardException { + checkParameter(EDGE_ID, strEdgeId); + try { + TenantId tenantId = getCurrentUser().getTenantId(); + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); + checkEdgeId(edgeId, Operation.READ); + TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset); + return checkNotNull(userService.findUsersByTenantIdAndEdgeId(tenantId, edgeId, pageLink).get()); + } catch (Exception e) { + throw handleException(e); + } + } } 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 6959a522bf..db1b2411eb 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 @@ -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, @@ -16,6 +16,7 @@ package org.thingsboard.server.service.edge; import lombok.Data; +import lombok.Getter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; @@ -37,9 +38,10 @@ import org.thingsboard.server.service.edge.rpc.constructor.DashboardUpdateMsgCon import org.thingsboard.server.service.edge.rpc.constructor.DeviceUpdateMsgConstructor; import org.thingsboard.server.service.edge.rpc.constructor.EntityViewUpdateMsgConstructor; import org.thingsboard.server.service.edge.rpc.constructor.RelationUpdateMsgConstructor; +import org.thingsboard.server.service.edge.rpc.constructor.RuleChainUpdateMsgConstructor; import org.thingsboard.server.service.edge.rpc.constructor.UserUpdateMsgConstructor; import org.thingsboard.server.service.edge.rpc.init.SyncEdgeService; -import org.thingsboard.server.service.edge.rpc.constructor.RuleChainUpdateMsgConstructor; +import org.thingsboard.server.service.executors.DbCallbackExecutorService; import org.thingsboard.server.service.queue.TbClusterService; import org.thingsboard.server.service.state.DeviceStateService; @@ -138,4 +140,8 @@ public class EdgeContextComponent { @Lazy @Autowired private EdgeEventStorageSettings edgeEventStorageSettings; + + @Autowired + @Getter + private DbCallbackExecutorService dbCallbackExecutor; } 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 d029b5ff20..a095675fb5 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 @@ -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, @@ -141,7 +141,7 @@ public final class EdgeGrpcSession implements Closeable { outputStream.onError(new RuntimeException(responseMsg.getErrorMsg())); } if (ConnectResponseCode.ACCEPTED == responseMsg.getResponseCode()) { - ctx.getSyncEdgeService().sync(edge, outputStream); + ctx.getSyncEdgeService().sync(ctx, edge, outputStream); } } if (connected) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RelationUpdateMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RelationUpdateMsgConstructor.java index 1c0af319f3..e09b9f2e8b 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RelationUpdateMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RelationUpdateMsgConstructor.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/edge/rpc/init/DefaultSyncEdgeService.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/DefaultSyncEdgeService.java index d51474046e..510fa6978a 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/DefaultSyncEdgeService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/DefaultSyncEdgeService.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, @@ -17,7 +17,6 @@ package org.thingsboard.server.service.edge.rpc.init; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.MoreExecutors; import io.grpc.stub.StreamObserver; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -26,7 +25,7 @@ import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.DashboardInfo; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntityView; -import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.id.EntityId; @@ -45,6 +44,7 @@ import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.rule.RuleChainService; +import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.gen.edge.AssetUpdateMsg; import org.thingsboard.server.gen.edge.DashboardUpdateMsg; import org.thingsboard.server.gen.edge.DeviceUpdateMsg; @@ -56,12 +56,15 @@ import org.thingsboard.server.gen.edge.RuleChainMetadataRequestMsg; import org.thingsboard.server.gen.edge.RuleChainMetadataUpdateMsg; import org.thingsboard.server.gen.edge.RuleChainUpdateMsg; import org.thingsboard.server.gen.edge.UpdateMsgType; +import org.thingsboard.server.gen.edge.UserUpdateMsg; +import org.thingsboard.server.service.edge.EdgeContextComponent; import org.thingsboard.server.service.edge.rpc.constructor.AssetUpdateMsgConstructor; import org.thingsboard.server.service.edge.rpc.constructor.DashboardUpdateMsgConstructor; import org.thingsboard.server.service.edge.rpc.constructor.DeviceUpdateMsgConstructor; import org.thingsboard.server.service.edge.rpc.constructor.EntityViewUpdateMsgConstructor; import org.thingsboard.server.service.edge.rpc.constructor.RelationUpdateMsgConstructor; import org.thingsboard.server.service.edge.rpc.constructor.RuleChainUpdateMsgConstructor; +import org.thingsboard.server.service.edge.rpc.constructor.UserUpdateMsgConstructor; import java.util.ArrayList; import java.util.HashSet; @@ -92,10 +95,10 @@ public class DefaultSyncEdgeService implements SyncEdgeService { private DashboardService dashboardService; @Autowired - private RuleChainUpdateMsgConstructor ruleChainUpdateMsgConstructor; + private UserService userService; @Autowired - private RelationUpdateMsgConstructor relationUpdateMsgConstructor; + private RuleChainUpdateMsgConstructor ruleChainUpdateMsgConstructor; @Autowired private DeviceUpdateMsgConstructor deviceUpdateMsgConstructor; @@ -109,68 +112,56 @@ public class DefaultSyncEdgeService implements SyncEdgeService { @Autowired private DashboardUpdateMsgConstructor dashboardUpdateMsgConstructor; + @Autowired + private UserUpdateMsgConstructor userUpdateMsgConstructor; + + @Autowired + private RelationUpdateMsgConstructor relationUpdateMsgConstructor; + @Override - public void sync(Edge edge, StreamObserver outputStream) { + public void sync(EdgeContextComponent ctx, Edge edge, StreamObserver outputStream) { Set pushedEntityIds = new HashSet<>(); syncRuleChains(edge, pushedEntityIds, outputStream); syncDevices(edge, pushedEntityIds, outputStream); syncAssets(edge, pushedEntityIds, outputStream); syncEntityViews(edge, pushedEntityIds, outputStream); syncDashboards(edge, pushedEntityIds, outputStream); - syncRelations(edge, pushedEntityIds, outputStream); + syncUsers(ctx, edge, pushedEntityIds, outputStream); + syncRelations(ctx, edge, pushedEntityIds, outputStream); } - private void syncRelations(Edge edge, Set pushedEntityIds, StreamObserver outputStream) { - if (!pushedEntityIds.isEmpty()) { - List>> futures = new ArrayList<>(); - for (EntityId entityId : pushedEntityIds) { - futures.add(syncRelations(edge, entityId, EntitySearchDirection.FROM)); - futures.add(syncRelations(edge, entityId, EntitySearchDirection.TO)); - } - ListenableFuture>> relationsListFuture = Futures.allAsList(futures); - Futures.transform(relationsListFuture, relationsList -> { - try { - Set uniqueEntityRelations = new HashSet<>(); - if (!relationsList.isEmpty()) { - for (List entityRelations : relationsList) { - if (!entityRelations.isEmpty()) { - uniqueEntityRelations.addAll(entityRelations); - } - } - } - if (!uniqueEntityRelations.isEmpty()) { - log.trace("[{}] [{}] relation(s) are going to be pushed to edge.", edge.getId(), uniqueEntityRelations.size()); - for (EntityRelation relation : uniqueEntityRelations) { - try { - RelationUpdateMsg relationUpdateMsg = - relationUpdateMsgConstructor.constructRelationUpdatedMsg( - UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, - relation); - EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() - .setRelationUpdateMsg(relationUpdateMsg) - .build(); - outputStream.onNext(ResponseMsg.newBuilder() - .setEntityUpdateMsg(entityUpdateMsg) - .build()); - } catch (Exception e) { - log.error("Exception during loading relation [{}] to edge on init!", relation, e); - } - } + private void syncRuleChains(Edge edge, Set pushedEntityIds, StreamObserver outputStream) { + try { + TimePageLink pageLink = new TimePageLink(100); + TimePageData pageData; + do { + pageData = ruleChainService.findRuleChainsByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), pageLink).get(); + if (!pageData.getData().isEmpty()) { + log.trace("[{}] [{}] rule chains(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size()); + for (RuleChain ruleChain : pageData.getData()) { + RuleChainUpdateMsg ruleChainUpdateMsg = + ruleChainUpdateMsgConstructor.constructRuleChainUpdatedMsg( + edge.getRootRuleChainId(), + UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, + ruleChain); + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setRuleChainUpdateMsg(ruleChainUpdateMsg) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + pushedEntityIds.add(ruleChain.getId()); } - } catch (Exception e) { - log.error("Exception during loading relation(s) to edge on init!", e); } - return null; - }, MoreExecutors.directExecutor()); + if (pageData.hasNext()) { + pageLink = pageData.getNextPageLink(); + } + } while (pageData.hasNext()); + } catch (Exception e) { + log.error("Exception during loading edge rule chain(s) on sync!", e); } } - private ListenableFuture> syncRelations(Edge edge, EntityId entityId, EntitySearchDirection direction) { - EntityRelationsQuery query = new EntityRelationsQuery(); - query.setParameters(new RelationsSearchParameters(entityId, direction, -1, false)); - return relationService.findByQuery(edge.getTenantId(), query); - } - private void syncDevices(Edge edge, Set pushedEntityIds, StreamObserver outputStream) { try { TimePageLink pageLink = new TimePageLink(100); @@ -198,7 +189,7 @@ public class DefaultSyncEdgeService implements SyncEdgeService { } } while (pageData.hasNext()); } catch (Exception e) { - log.error("Exception during loading edge device(s) on init!", e); + log.error("Exception during loading edge device(s) on sync!", e); } } @@ -229,7 +220,7 @@ public class DefaultSyncEdgeService implements SyncEdgeService { } } while (pageData.hasNext()); } catch (Exception e) { - log.error("Exception during loading edge asset(s) on init!", e); + log.error("Exception during loading edge asset(s) on sync!", e); } } @@ -260,7 +251,7 @@ public class DefaultSyncEdgeService implements SyncEdgeService { } } while (pageData.hasNext()); } catch (Exception e) { - log.error("Exception during loading edge entity view(s) on init!", e); + log.error("Exception during loading edge entity view(s) on sync!", e); } } @@ -292,31 +283,30 @@ public class DefaultSyncEdgeService implements SyncEdgeService { } } while (pageData.hasNext()); } catch (Exception e) { - log.error("Exception during loading edge dashboard(s) on init!", e); + log.error("Exception during loading edge dashboard(s) on sync!", e); } } - private void syncRuleChains(Edge edge, Set pushedEntityIds, StreamObserver outputStream) { + private void syncUsers(EdgeContextComponent ctx, Edge edge, Set pushedEntityIds, StreamObserver outputStream) { try { TimePageLink pageLink = new TimePageLink(100); - TimePageData pageData; + TimePageData pageData; do { - pageData = ruleChainService.findRuleChainsByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), pageLink).get(); + pageData = userService.findUsersByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), pageLink).get(); if (!pageData.getData().isEmpty()) { - log.trace("[{}] [{}] rule chains(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size()); - for (RuleChain ruleChain : pageData.getData()) { - RuleChainUpdateMsg ruleChainUpdateMsg = - ruleChainUpdateMsgConstructor.constructRuleChainUpdatedMsg( - edge.getRootRuleChainId(), - UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, - ruleChain); + log.trace("[{}] [{}] user(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size()); + for (User user : pageData.getData()) { + UserUpdateMsg userUpdateMsg = + userUpdateMsgConstructor.constructUserUpdatedMsg( + UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, + user); EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() - .setRuleChainUpdateMsg(ruleChainUpdateMsg) + .setUserUpdateMsg(userUpdateMsg) .build(); outputStream.onNext(ResponseMsg.newBuilder() .setEntityUpdateMsg(entityUpdateMsg) .build()); - pushedEntityIds.add(ruleChain.getId()); + pushedEntityIds.add(user.getId()); } } if (pageData.hasNext()) { @@ -324,10 +314,62 @@ public class DefaultSyncEdgeService implements SyncEdgeService { } } while (pageData.hasNext()); } catch (Exception e) { - log.error("Exception during loading edge rule chain(s) on init!", e); + log.error("Exception during loading edge user(s) on sync!", e); + } + } + + private void syncRelations(EdgeContextComponent ctx, Edge edge, Set pushedEntityIds, StreamObserver outputStream) { + if (!pushedEntityIds.isEmpty()) { + List>> futures = new ArrayList<>(); + for (EntityId entityId : pushedEntityIds) { + futures.add(syncRelations(edge, entityId, EntitySearchDirection.FROM)); + futures.add(syncRelations(edge, entityId, EntitySearchDirection.TO)); + } + ListenableFuture>> relationsListFuture = Futures.allAsList(futures); + Futures.transform(relationsListFuture, relationsList -> { + try { + Set uniqueEntityRelations = new HashSet<>(); + if (!relationsList.isEmpty()) { + for (List entityRelations : relationsList) { + if (!entityRelations.isEmpty()) { + uniqueEntityRelations.addAll(entityRelations); + } + } + } + if (!uniqueEntityRelations.isEmpty()) { + log.trace("[{}] [{}] relation(s) are going to be pushed to edge.", edge.getId(), uniqueEntityRelations.size()); + for (EntityRelation relation : uniqueEntityRelations) { + try { + RelationUpdateMsg relationUpdateMsg = + relationUpdateMsgConstructor.constructRelationUpdatedMsg( + UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, + relation); + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setRelationUpdateMsg(relationUpdateMsg) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + } catch (Exception e) { + log.error("Exception during loading relation [{}] to edge on sync!", relation, e); + } + } + } + } catch (Exception e) { + log.error("Exception during loading relation(s) to edge on sync!", e); + } + return null; + }, ctx.getDbCallbackExecutor()); } } + private ListenableFuture> syncRelations(Edge edge, EntityId entityId, EntitySearchDirection direction) { + EntityRelationsQuery query = new EntityRelationsQuery(); + query.setParameters(new RelationsSearchParameters(entityId, direction, -1, false)); + return relationService.findByQuery(edge.getTenantId(), query); + } + + @Override public void syncRuleChainMetadata(Edge edge, RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg, StreamObserver outputStream) { if (ruleChainMetadataRequestMsg.getRuleChainIdMSB() != 0 && ruleChainMetadataRequestMsg.getRuleChainIdLSB() != 0) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/SyncEdgeService.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/SyncEdgeService.java index c83a9ec3b0..e408eedd65 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/SyncEdgeService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/SyncEdgeService.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, @@ -19,10 +19,11 @@ import io.grpc.stub.StreamObserver; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.gen.edge.ResponseMsg; import org.thingsboard.server.gen.edge.RuleChainMetadataRequestMsg; +import org.thingsboard.server.service.edge.EdgeContextComponent; public interface SyncEdgeService { - void sync(Edge edge, StreamObserver outputStream); + void sync(EdgeContextComponent ctx, Edge edge, StreamObserver outputStream); void syncRuleChainMetadata(Edge edge, RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg, StreamObserver outputStream); } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java index f0e2a0e7a1..32e22a84e8 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java @@ -17,12 +17,17 @@ package org.thingsboard.server.dao.user; import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.asset.Asset; +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.id.UserCredentialsId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.common.data.page.TimePageData; +import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.security.UserCredentials; public interface UserService { @@ -66,4 +71,10 @@ public interface UserService { void onUserLoginSuccessful(TenantId tenantId, UserId userId); int onUserLoginIncorrectCredentials(TenantId tenantId, UserId userId); + + User assignUserToEdge(TenantId tenantId, UserId userId, EdgeId edgeId); + + User unassignUserFromEdge(TenantId tenantId, UserId userId, EdgeId edgeId); + + ListenableFuture> findUsersByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeQueueEntityType.java b/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeQueueEntityType.java index 7ba316a529..ca268380f4 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeQueueEntityType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeQueueEntityType.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/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java index 8c0f4cf644..62f68e962c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java @@ -80,10 +80,8 @@ import static org.thingsboard.server.dao.service.Validator.validateString; public class BaseAssetService extends AbstractEntityService implements AssetService { public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; - public static final String INCORRECT_PAGE_LINK = "Incorrect page link "; public static final String INCORRECT_CUSTOMER_ID = "Incorrect customerId "; public static final String INCORRECT_ASSET_ID = "Incorrect assetId "; - public static final String INCORRECT_EDGE_ID = "Incorrect edgeId "; @Autowired private AssetDao assetDao; diff --git a/dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java b/dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java index 17fa7bcb3e..523df59db5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java @@ -31,6 +31,9 @@ import java.util.concurrent.ExecutionException; @Slf4j public abstract class AbstractEntityService { + public static final String INCORRECT_EDGE_ID = "Incorrect edgeId "; + public static final String INCORRECT_PAGE_LINK = "Incorrect page link "; + @Autowired protected RelationService relationService; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java index f30f6ba6d1..5240fe2dc4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java @@ -15,20 +15,31 @@ */ package org.thingsboard.server.dao.sql.user; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.MoreExecutors; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.PageRequest; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.common.data.page.TimePageLink; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.model.sql.UserEntity; +import org.thingsboard.server.dao.relation.RelationDao; import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; import org.thingsboard.server.dao.user.UserDao; import org.thingsboard.server.dao.util.SqlDao; +import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.UUID; @@ -41,11 +52,15 @@ import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID_STR; */ @Component @SqlDao +@Slf4j public class JpaUserDao extends JpaAbstractSearchTextDao implements UserDao { @Autowired private UserRepository userRepository; + @Autowired + private RelationDao relationDao; + @Override protected Class getEntityClass() { return UserEntity.class; @@ -87,4 +102,17 @@ public class JpaUserDao extends JpaAbstractSearchTextDao imple PageRequest.of(0, pageLink.getLimit()))); } + + @Override + public ListenableFuture> findUsersByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink) { + log.debug("Try to find users by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink); + ListenableFuture> relations = relationDao.findRelations(new TenantId(tenantId), new EdgeId(edgeId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE, EntityType.USER, pageLink); + return Futures.transformAsync(relations, input -> { + List> userFutures = new ArrayList<>(input.size()); + for (EntityRelation relation : input) { + userFutures.add(findByIdAsync(new TenantId(tenantId), relation.getTo().getId())); + } + return Futures.successfulAsList(userFutures); + }, MoreExecutors.directExecutor()); + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/CassandraUserDao.java b/dao/src/main/java/org/thingsboard/server/dao/user/CassandraUserDao.java index 2b4546676a..2f134e9839 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/CassandraUserDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/CassandraUserDao.java @@ -16,18 +16,30 @@ package org.thingsboard.server.dao.user; import com.datastax.driver.core.querybuilder.Select.Where; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.MoreExecutors; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.common.data.page.TimePageLink; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.nosql.UserEntity; import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTextDao; +import org.thingsboard.server.dao.relation.RelationDao; import org.thingsboard.server.dao.util.NoSqlDao; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.UUID; @@ -40,6 +52,9 @@ import static com.datastax.driver.core.querybuilder.QueryBuilder.select; @NoSqlDao public class CassandraUserDao extends CassandraAbstractSearchTextDao implements UserDao { + @Autowired + private RelationDao relationDao; + @Override protected Class getColumnFamilyClass() { return UserEntity.class; @@ -86,4 +101,16 @@ public class CassandraUserDao extends CassandraAbstractSearchTextDao> findUsersByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink) { + log.debug("Try to find users by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink); + ListenableFuture> relations = relationDao.findRelations(new TenantId(tenantId), new EdgeId(edgeId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE, EntityType.USER, pageLink); + return Futures.transformAsync(relations, input -> { + List> userFutures = new ArrayList<>(input.size()); + for (EntityRelation relation : input) { + userFutures.add(findByIdAsync(new TenantId(tenantId), relation.getTo().getId())); + } + return Futures.successfulAsList(userFutures); + }, MoreExecutors.directExecutor()); + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java index 4e7b222c66..5de17132ba 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java @@ -15,9 +15,11 @@ */ package org.thingsboard.server.dao.user; +import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.dao.Dao; import java.util.List; @@ -59,5 +61,15 @@ public interface UserDao extends Dao { * @return the list of user entities */ List findCustomerUsers(UUID tenantId, UUID customerId, TextPageLink pageLink); + + /** + * Find users by tenantId, edgeId and page link. + * + * @param tenantId the tenantId + * @param edgeId the edgeId + * @param pageLink the page link + * @return the list of user objects + */ + ListenableFuture> findUsersByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java index 914ba5d3ec..cd7b7ef860 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java @@ -18,7 +18,10 @@ package org.thingsboard.server.dao.user; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.base.Function; +import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.MoreExecutors; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.StringUtils; @@ -28,15 +31,22 @@ import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.edge.Edge; 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.id.UserCredentialsId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.common.data.page.TimePageData; +import org.thingsboard.server.common.data.page.TimePageLink; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.UserCredentials; import org.thingsboard.server.dao.customer.CustomerDao; +import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.IncorrectParameterException; @@ -45,9 +55,11 @@ import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.tenant.TenantDao; +import javax.annotation.Nullable; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.ExecutionException; import static org.thingsboard.server.dao.service.Validator.validateId; import static org.thingsboard.server.dao.service.Validator.validatePageLink; @@ -85,6 +97,9 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic @Autowired private CustomerDao customerDao; + @Autowired + private EdgeService edgeService; + @Override public User findUserByEmail(TenantId tenantId, String email) { log.trace("Executing findUserByEmail [{}]", email); @@ -312,6 +327,57 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic return failedLoginAttempts; } + @Override + public User assignUserToEdge(TenantId tenantId, UserId userId, EdgeId edgeId) { + User user = findUserById(tenantId, userId); + Edge edge = edgeService.findEdgeById(tenantId, edgeId); + if (edge == null) { + throw new DataValidationException("Can't assign user to non-existent edge!"); + } + if (!edge.getTenantId().getId().equals(user.getTenantId().getId())) { + throw new DataValidationException("Can't assign user to edge from different tenant!"); + } + try { + createRelation(tenantId, new EntityRelation(edgeId, userId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE)); + } catch (ExecutionException | InterruptedException e) { + log.warn("[{}] Failed to create user relation. Edge Id: [{}]", userId, edgeId); + throw new RuntimeException(e); + } + return user; + } + + @Override + public User unassignUserFromEdge(TenantId tenantId, UserId userId, EdgeId edgeId) { + User user = findUserById(tenantId, userId); + Edge edge = edgeService.findEdgeById(tenantId, edgeId); + if (edge == null) { + throw new DataValidationException("Can't unassign user from non-existent edge!"); + } + try { + deleteRelation(tenantId, new EntityRelation(edgeId, userId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE)); + } catch (ExecutionException | InterruptedException e) { + log.warn("[{}] Failed to delete user relation. Edge Id: [{}]", userId, edgeId); + throw new RuntimeException(e); + } + return user; + } + + @Override + public ListenableFuture> findUsersByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink) { + log.trace("Executing findUsersByTenantIdAndEdgeId, tenantId [{}], edgeId [{}], pageLink [{}]", tenantId, edgeId, pageLink); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(edgeId, INCORRECT_EDGE_ID + edgeId); + validatePageLink(pageLink, INCORRECT_PAGE_LINK + pageLink); + ListenableFuture> users = userDao.findUsersByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink); + return Futures.transform(users, new Function, TimePageData>() { + @Nullable + @Override + public TimePageData apply(@Nullable List users) { + return new TimePageData<>(users, pageLink); + } + }, MoreExecutors.directExecutor()); + } + private int increaseFailedLoginAttempts(User user) { JsonNode additionalInfo = user.getAdditionalInfo(); if (!(additionalInfo instanceof ObjectNode)) { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseRuleChainServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseRuleChainServiceTest.java index f37017068e..b7ecc1f784 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseRuleChainServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseRuleChainServiceTest.java @@ -331,6 +331,7 @@ public abstract class BaseRuleChainServiceTest extends AbstractServiceTest { result = ruleChainService.findDefaultEdgeRuleChainsByTenantId(tenantId).get(); Assert.assertEquals(1, result.size()); } + private RuleChainId saveRuleChainAndSetDefaultEdge(String name) { RuleChain edgeRuleChain = new RuleChain(); edgeRuleChain.setTenantId(tenantId); From ab1014bf16b5468f5df9bbbd2aa66f4f7e88eaa1 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Thu, 11 Jun 2020 20:28:06 +0300 Subject: [PATCH 42/52] Formatting and license fix --- .../server/service/edge/EdgeContextComponent.java | 2 +- .../server/service/edge/rpc/EdgeGrpcSession.java | 2 +- .../service/edge/rpc/init/DefaultSyncEdgeService.java | 2 +- .../server/service/edge/rpc/init/SyncEdgeService.java | 2 +- .../org/thingsboard/server/dao/user/UserService.java | 2 -- common/edge-api/src/main/proto/edge.proto | 10 +++++----- 6 files changed, 9 insertions(+), 11 deletions(-) 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 db1b2411eb..a7a9ee1bed 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 @@ -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/edge/rpc/EdgeGrpcSession.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java index a095675fb5..f722e09de5 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 @@ -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/edge/rpc/init/DefaultSyncEdgeService.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/DefaultSyncEdgeService.java index 510fa6978a..bea926372d 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/DefaultSyncEdgeService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/DefaultSyncEdgeService.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/edge/rpc/init/SyncEdgeService.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/SyncEdgeService.java index e408eedd65..84b4d9ffff 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/SyncEdgeService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/SyncEdgeService.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/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java index 32e22a84e8..d612f4f9d4 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java @@ -17,8 +17,6 @@ package org.thingsboard.server.dao.user; import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.User; -import org.thingsboard.server.common.data.asset.Asset; -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; diff --git a/common/edge-api/src/main/proto/edge.proto b/common/edge-api/src/main/proto/edge.proto index 2256acccf8..4a941b279e 100644 --- a/common/edge-api/src/main/proto/edge.proto +++ b/common/edge-api/src/main/proto/edge.proto @@ -122,9 +122,9 @@ message RuleChainMetadataUpdateMsg { int64 ruleChainIdMSB = 2; int64 ruleChainIdLSB = 3; int32 firstNodeIndex = 4; - repeated RuleNodeProto nodes = 5; - repeated NodeConnectionInfoProto connections = 6; - repeated RuleChainConnectionInfoProto ruleChainConnections = 7; + repeated RuleNodeProto nodes = 5; + repeated NodeConnectionInfoProto connections = 6; + repeated RuleChainConnectionInfoProto ruleChainConnections = 7; } message RuleNodeProto { @@ -255,8 +255,8 @@ message RuleChainMetadataRequestMsg { } enum EdgeEntityType { - DEVICE = 0; - ASSET = 1; + DEVICE = 0; + ASSET = 1; } /** From e8afd976057fa3d63ccec07f92d43d1d6a0139ac Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Fri, 12 Jun 2020 10:36:03 +0300 Subject: [PATCH 43/52] Pushed users from CE to edge --- .../server/controller/UserController.java | 84 ------ .../edge/rpc/init/DefaultSyncEdgeService.java | 253 +++++++++--------- .../server/dao/user/UserService.java | 9 - .../common/data/edge/EdgeQueueEntityType.java | 2 +- .../server/dao/sql/user/JpaUserDao.java | 26 -- .../server/dao/user/CassandraUserDao.java | 28 -- .../thingsboard/server/dao/user/UserDao.java | 17 +- .../server/dao/user/UserServiceImpl.java | 62 ----- 8 files changed, 133 insertions(+), 348 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/UserController.java b/application/src/main/java/org/thingsboard/server/controller/UserController.java index 13409e0d07..32ce338a37 100644 --- a/application/src/main/java/org/thingsboard/server/controller/UserController.java +++ b/application/src/main/java/org/thingsboard/server/controller/UserController.java @@ -306,88 +306,4 @@ public class UserController extends BaseController { throw handleException(e); } } - - @PreAuthorize("hasAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/edge/{edgeId}/user/{userId}", method = RequestMethod.POST) - @ResponseBody - public User assignUserToEdge(@PathVariable(EDGE_ID) String strEdgeId, - @PathVariable(USER_ID) String strUserId) throws ThingsboardException { - checkParameter(EDGE_ID, strEdgeId); - checkParameter(USER_ID, strUserId); - try { - EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); - Edge edge = checkEdgeId(edgeId, Operation.READ); - - UserId userId = new UserId(toUUID(strUserId)); - checkUserId(userId, Operation.ASSIGN_TO_EDGE); - - User savedUser = checkNotNull(userService.assignUserToEdge(getTenantId(), userId, edgeId)); - - logEntityAction(userId, savedUser, - savedUser.getCustomerId(), - ActionType.ASSIGNED_TO_EDGE, null, strUserId, strEdgeId, edge.getName()); - - return savedUser; - } catch (Exception e) { - - logEntityAction(emptyId(EntityType.USER), null, - null, - ActionType.ASSIGNED_TO_EDGE, e, strUserId, strEdgeId); - - throw handleException(e); - } - } - - @PreAuthorize("hasAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/edge/{edgeId}/user/{userId}", method = RequestMethod.DELETE) - @ResponseBody - public User unassignUserFromEdge(@PathVariable(EDGE_ID) String strEdgeId, - @PathVariable(USER_ID) String strUserId) throws ThingsboardException { - checkParameter(EDGE_ID, strEdgeId); - checkParameter(USER_ID, strUserId); - try { - EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); - Edge edge = checkEdgeId(edgeId, Operation.READ); - - UserId userId = new UserId(toUUID(strUserId)); - User user = checkUserId(userId, Operation.UNASSIGN_FROM_EDGE); - - User savedUser = checkNotNull(userService.unassignUserFromEdge(getTenantId(), userId, edgeId)); - - logEntityAction(userId, savedUser, - savedUser.getCustomerId(), - ActionType.UNASSIGNED_FROM_EDGE, null, strUserId, edge.getId().toString(), edge.getName()); - - return savedUser; - } catch (Exception e) { - - logEntityAction(emptyId(EntityType.USER), null, - null, - ActionType.UNASSIGNED_FROM_EDGE, e, strUserId); - - throw handleException(e); - } - } - - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/edge/{edgeId}/users", params = {"limit"}, method = RequestMethod.GET) - @ResponseBody - public TimePageData getEdgeUsers( - @PathVariable(EDGE_ID) String strEdgeId, - @RequestParam int limit, - @RequestParam(required = false) Long startTime, - @RequestParam(required = false) Long endTime, - @RequestParam(required = false, defaultValue = "false") boolean ascOrder, - @RequestParam(required = false) String offset) throws ThingsboardException { - checkParameter(EDGE_ID, strEdgeId); - try { - TenantId tenantId = getCurrentUser().getTenantId(); - EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); - checkEdgeId(edgeId, Operation.READ); - TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset); - return checkNotNull(userService.findUsersByTenantIdAndEdgeId(tenantId, edgeId, pageLink).get()); - } catch (Exception e) { - throw handleException(e); - } - } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/DefaultSyncEdgeService.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/DefaultSyncEdgeService.java index bea926372d..08612596da 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/DefaultSyncEdgeService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/DefaultSyncEdgeService.java @@ -17,6 +17,7 @@ package org.thingsboard.server.service.edge.rpc.init; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.MoreExecutors; import io.grpc.stub.StreamObserver; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -30,6 +31,8 @@ import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.page.TextPageData; +import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.page.TimePageData; import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.relation.EntityRelation; @@ -121,53 +124,57 @@ public class DefaultSyncEdgeService implements SyncEdgeService { @Override public void sync(EdgeContextComponent ctx, Edge edge, StreamObserver outputStream) { Set pushedEntityIds = new HashSet<>(); - syncRuleChains(edge, pushedEntityIds, outputStream); - syncDevices(edge, pushedEntityIds, outputStream); - syncAssets(edge, pushedEntityIds, outputStream); - syncEntityViews(edge, pushedEntityIds, outputStream); - syncDashboards(edge, pushedEntityIds, outputStream); syncUsers(ctx, edge, pushedEntityIds, outputStream); - syncRelations(ctx, edge, pushedEntityIds, outputStream); + List> futures = new ArrayList<>(); + futures.add(syncRuleChains(ctx, edge, pushedEntityIds, outputStream)); + futures.add(syncDevices(ctx, edge, pushedEntityIds, outputStream)); + futures.add(syncAssets(ctx, edge, pushedEntityIds, outputStream)); + futures.add(syncEntityViews(ctx, edge, pushedEntityIds, outputStream)); + futures.add(syncDashboards(ctx, edge, pushedEntityIds, outputStream)); + ListenableFuture> joinFuture = Futures.allAsList(futures); + Futures.transform(joinFuture, result -> { + syncRelations(ctx, edge, pushedEntityIds, outputStream); + return null; + }, MoreExecutors.directExecutor()); } - private void syncRuleChains(Edge edge, Set pushedEntityIds, StreamObserver outputStream) { + private ListenableFuture syncRuleChains(EdgeContextComponent ctx, Edge edge, Set pushedEntityIds, StreamObserver outputStream) { try { - TimePageLink pageLink = new TimePageLink(100); - TimePageData pageData; - do { - pageData = ruleChainService.findRuleChainsByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), pageLink).get(); - if (!pageData.getData().isEmpty()) { - log.trace("[{}] [{}] rule chains(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size()); - for (RuleChain ruleChain : pageData.getData()) { - RuleChainUpdateMsg ruleChainUpdateMsg = - ruleChainUpdateMsgConstructor.constructRuleChainUpdatedMsg( - edge.getRootRuleChainId(), - UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, - ruleChain); - EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() - .setRuleChainUpdateMsg(ruleChainUpdateMsg) - .build(); - outputStream.onNext(ResponseMsg.newBuilder() - .setEntityUpdateMsg(entityUpdateMsg) - .build()); - pushedEntityIds.add(ruleChain.getId()); + ListenableFuture> future = ruleChainService.findRuleChainsByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), new TimePageLink(Integer.MAX_VALUE)); + return Futures.transform(future, pageData -> { + try { + if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) { + log.trace("[{}] [{}] rule chains(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size()); + for (RuleChain ruleChain : pageData.getData()) { + RuleChainUpdateMsg ruleChainUpdateMsg = + ruleChainUpdateMsgConstructor.constructRuleChainUpdatedMsg( + edge.getRootRuleChainId(), + UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, + ruleChain); + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setRuleChainUpdateMsg(ruleChainUpdateMsg) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + pushedEntityIds.add(ruleChain.getId()); + } } + } catch (Exception e) { + log.error("Exception during loading edge rule chain(s) on sync!", e); } - if (pageData.hasNext()) { - pageLink = pageData.getNextPageLink(); - } - } while (pageData.hasNext()); + return null; + }, ctx.getDbCallbackExecutor()); } catch (Exception e) { log.error("Exception during loading edge rule chain(s) on sync!", e); + return Futures.immediateFuture(null); } } - private void syncDevices(Edge edge, Set pushedEntityIds, StreamObserver outputStream) { + private ListenableFuture syncDevices(EdgeContextComponent ctx, Edge edge, Set pushedEntityIds, StreamObserver outputStream) { try { - TimePageLink pageLink = new TimePageLink(100); - TimePageData pageData; - do { - pageData = deviceService.findDevicesByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), pageLink).get(); + ListenableFuture> future = deviceService.findDevicesByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), new TimePageLink(Integer.MAX_VALUE)); + return Futures.transform(future, pageData -> { if (!pageData.getData().isEmpty()) { log.trace("[{}] [{}] device(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size()); for (Device device : pageData.getData()) { @@ -184,22 +191,19 @@ public class DefaultSyncEdgeService implements SyncEdgeService { pushedEntityIds.add(device.getId()); } } - if (pageData.hasNext()) { - pageLink = pageData.getNextPageLink(); - } - } while (pageData.hasNext()); + return null; + }, ctx.getDbCallbackExecutor()); } catch (Exception e) { log.error("Exception during loading edge device(s) on sync!", e); + return Futures.immediateFuture(null); } } - private void syncAssets(Edge edge, Set pushedEntityIds, StreamObserver outputStream) { + private ListenableFuture syncAssets(EdgeContextComponent ctx, Edge edge, Set pushedEntityIds, StreamObserver outputStream) { try { - TimePageLink pageLink = new TimePageLink(100); - TimePageData pageData; - do { - pageData = assetService.findAssetsByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), pageLink).get(); - if (!pageData.getData().isEmpty()) { + ListenableFuture> future = assetService.findAssetsByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), new TimePageLink(Integer.MAX_VALUE)); + return Futures.transform(future, pageData -> { + if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) { log.trace("[{}] [{}] asset(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size()); for (Asset asset : pageData.getData()) { AssetUpdateMsg assetUpdateMsg = @@ -215,110 +219,112 @@ public class DefaultSyncEdgeService implements SyncEdgeService { pushedEntityIds.add(asset.getId()); } } - if (pageData.hasNext()) { - pageLink = pageData.getNextPageLink(); - } - } while (pageData.hasNext()); + return null; + }, ctx.getDbCallbackExecutor()); } catch (Exception e) { log.error("Exception during loading edge asset(s) on sync!", e); + return Futures.immediateFuture(null); } } - private void syncEntityViews(Edge edge, Set pushedEntityIds, StreamObserver outputStream) { + private ListenableFuture syncEntityViews(EdgeContextComponent ctx, Edge edge, Set pushedEntityIds, StreamObserver outputStream) { try { - TimePageLink pageLink = new TimePageLink(100); - TimePageData pageData; - do { - pageData = entityViewService.findEntityViewsByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), pageLink).get(); - if (!pageData.getData().isEmpty()) { - log.trace("[{}] [{}] entity view(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size()); - for (EntityView entityView : pageData.getData()) { - EntityViewUpdateMsg entityViewUpdateMsg = - entityViewUpdateMsgConstructor.constructEntityViewUpdatedMsg( - UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, - entityView); - EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() - .setEntityViewUpdateMsg(entityViewUpdateMsg) - .build(); - outputStream.onNext(ResponseMsg.newBuilder() - .setEntityUpdateMsg(entityUpdateMsg) - .build()); - pushedEntityIds.add(entityView.getId()); + ListenableFuture> future = entityViewService.findEntityViewsByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), new TimePageLink(Integer.MAX_VALUE)); + return Futures.transform(future, pageData -> { + try { + if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) { + log.trace("[{}] [{}] entity view(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size()); + for (EntityView entityView : pageData.getData()) { + EntityViewUpdateMsg entityViewUpdateMsg = + entityViewUpdateMsgConstructor.constructEntityViewUpdatedMsg( + UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, + entityView); + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setEntityViewUpdateMsg(entityViewUpdateMsg) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + pushedEntityIds.add(entityView.getId()); + } } + } catch (Exception e) { + log.error("Exception during loading edge entity view(s) on sync!", e); } - if (pageData.hasNext()) { - pageLink = pageData.getNextPageLink(); - } - } while (pageData.hasNext()); + return null; + }, ctx.getDbCallbackExecutor()); } catch (Exception e) { log.error("Exception during loading edge entity view(s) on sync!", e); + return Futures.immediateFuture(null); } } - private void syncDashboards(Edge edge, Set pushedEntityIds, StreamObserver outputStream) { + private ListenableFuture syncDashboards(EdgeContextComponent ctx, Edge edge, Set pushedEntityIds, StreamObserver outputStream) { try { - TimePageLink pageLink = new TimePageLink(100); - TimePageData pageData; - do { - pageData = dashboardService.findDashboardsByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), pageLink).get(); - if (!pageData.getData().isEmpty()) { - log.trace("[{}] [{}] dashboard(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size()); - for (DashboardInfo dashboardInfo : pageData.getData()) { - Dashboard dashboard = dashboardService.findDashboardById(edge.getTenantId(), dashboardInfo.getId()); - DashboardUpdateMsg dashboardUpdateMsg = - dashboardUpdateMsgConstructor.constructDashboardUpdatedMsg( - UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, - dashboard); - EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() - .setDashboardUpdateMsg(dashboardUpdateMsg) - .build(); - outputStream.onNext(ResponseMsg.newBuilder() - .setEntityUpdateMsg(entityUpdateMsg) - .build()); - pushedEntityIds.add(dashboard.getId()); + ListenableFuture> future = dashboardService.findDashboardsByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), new TimePageLink(Integer.MAX_VALUE)); + return Futures.transform(future, pageData -> { + try { + if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) { + log.trace("[{}] [{}] dashboard(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size()); + for (DashboardInfo dashboardInfo : pageData.getData()) { + Dashboard dashboard = dashboardService.findDashboardById(edge.getTenantId(), dashboardInfo.getId()); + DashboardUpdateMsg dashboardUpdateMsg = + dashboardUpdateMsgConstructor.constructDashboardUpdatedMsg( + UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, + dashboard); + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setDashboardUpdateMsg(dashboardUpdateMsg) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + pushedEntityIds.add(dashboard.getId()); + } } + } catch (Exception e) { + log.error("Exception during loading edge dashboard(s) on sync!", e); } - if (pageData.hasNext()) { - pageLink = pageData.getNextPageLink(); - } - } while (pageData.hasNext()); + return null; + }, ctx.getDbCallbackExecutor()); } catch (Exception e) { log.error("Exception during loading edge dashboard(s) on sync!", e); + return Futures.immediateFuture(null); } } private void syncUsers(EdgeContextComponent ctx, Edge edge, Set pushedEntityIds, StreamObserver outputStream) { try { - TimePageLink pageLink = new TimePageLink(100); - TimePageData pageData; - do { - pageData = userService.findUsersByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), pageLink).get(); - if (!pageData.getData().isEmpty()) { - log.trace("[{}] [{}] user(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size()); - for (User user : pageData.getData()) { - UserUpdateMsg userUpdateMsg = - userUpdateMsgConstructor.constructUserUpdatedMsg( - UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, - user); - EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() - .setUserUpdateMsg(userUpdateMsg) - .build(); - outputStream.onNext(ResponseMsg.newBuilder() - .setEntityUpdateMsg(entityUpdateMsg) - .build()); - pushedEntityIds.add(user.getId()); - } - } - if (pageData.hasNext()) { - pageLink = pageData.getNextPageLink(); - } - } while (pageData.hasNext()); + TextPageData pageData = userService.findTenantAdmins(edge.getTenantId(), new TextPageLink(Integer.MAX_VALUE)); + pushUsersToEdge(pageData, edge, pushedEntityIds, outputStream); + if (edge.getCustomerId() != null && !EntityId.NULL_UUID.equals(edge.getCustomerId().getId())) { + pageData = userService.findCustomerUsers(edge.getTenantId(), edge.getCustomerId(), new TextPageLink(Integer.MAX_VALUE)); + pushUsersToEdge(pageData, edge, pushedEntityIds, outputStream); + } } catch (Exception e) { log.error("Exception during loading edge user(s) on sync!", e); } } - private void syncRelations(EdgeContextComponent ctx, Edge edge, Set pushedEntityIds, StreamObserver outputStream) { + private void pushUsersToEdge(TextPageData pageData, Edge edge, Set pushedEntityIds, StreamObserver outputStream) { + if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) { + log.trace("[{}] [{}] user(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size()); + for (User user : pageData.getData()) { + UserUpdateMsg userUpdateMsg = + userUpdateMsgConstructor.constructUserUpdatedMsg( + UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, + user); + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setUserUpdateMsg(userUpdateMsg) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + pushedEntityIds.add(user.getId()); + } + } + } + + private ListenableFuture syncRelations(EdgeContextComponent ctx, Edge edge, Set pushedEntityIds, StreamObserver outputStream) { if (!pushedEntityIds.isEmpty()) { List>> futures = new ArrayList<>(); for (EntityId entityId : pushedEntityIds) { @@ -326,7 +332,7 @@ public class DefaultSyncEdgeService implements SyncEdgeService { futures.add(syncRelations(edge, entityId, EntitySearchDirection.TO)); } ListenableFuture>> relationsListFuture = Futures.allAsList(futures); - Futures.transform(relationsListFuture, relationsList -> { + return Futures.transform(relationsListFuture, relationsList -> { try { Set uniqueEntityRelations = new HashSet<>(); if (!relationsList.isEmpty()) { @@ -360,6 +366,8 @@ public class DefaultSyncEdgeService implements SyncEdgeService { } return null; }, ctx.getDbCallbackExecutor()); + } else { + return Futures.immediateFuture(null); } } @@ -369,7 +377,6 @@ public class DefaultSyncEdgeService implements SyncEdgeService { return relationService.findByQuery(edge.getTenantId(), query); } - @Override public void syncRuleChainMetadata(Edge edge, RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg, StreamObserver outputStream) { if (ruleChainMetadataRequestMsg.getRuleChainIdMSB() != 0 && ruleChainMetadataRequestMsg.getRuleChainIdLSB() != 0) { diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java index d612f4f9d4..f0e2a0e7a1 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java @@ -18,14 +18,11 @@ package org.thingsboard.server.dao.user; import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.User; 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.id.UserCredentialsId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageLink; -import org.thingsboard.server.common.data.page.TimePageData; -import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.security.UserCredentials; public interface UserService { @@ -69,10 +66,4 @@ public interface UserService { void onUserLoginSuccessful(TenantId tenantId, UserId userId); int onUserLoginIncorrectCredentials(TenantId tenantId, UserId userId); - - User assignUserToEdge(TenantId tenantId, UserId userId, EdgeId edgeId); - - User unassignUserFromEdge(TenantId tenantId, UserId userId, EdgeId edgeId); - - ListenableFuture> findUsersByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeQueueEntityType.java b/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeQueueEntityType.java index ca268380f4..7ba316a529 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeQueueEntityType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeQueueEntityType.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/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java index 5240fe2dc4..47093fdcb3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java @@ -15,31 +15,21 @@ */ package org.thingsboard.server.dao.sql.user; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.MoreExecutors; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.PageRequest; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.User; -import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TextPageLink; -import org.thingsboard.server.common.data.page.TimePageLink; -import org.thingsboard.server.common.data.relation.EntityRelation; -import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.model.sql.UserEntity; -import org.thingsboard.server.dao.relation.RelationDao; import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; import org.thingsboard.server.dao.user.UserDao; import org.thingsboard.server.dao.util.SqlDao; -import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.UUID; @@ -58,9 +48,6 @@ public class JpaUserDao extends JpaAbstractSearchTextDao imple @Autowired private UserRepository userRepository; - @Autowired - private RelationDao relationDao; - @Override protected Class getEntityClass() { return UserEntity.class; @@ -102,17 +89,4 @@ public class JpaUserDao extends JpaAbstractSearchTextDao imple PageRequest.of(0, pageLink.getLimit()))); } - - @Override - public ListenableFuture> findUsersByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink) { - log.debug("Try to find users by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink); - ListenableFuture> relations = relationDao.findRelations(new TenantId(tenantId), new EdgeId(edgeId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE, EntityType.USER, pageLink); - return Futures.transformAsync(relations, input -> { - List> userFutures = new ArrayList<>(input.size()); - for (EntityRelation relation : input) { - userFutures.add(findByIdAsync(new TenantId(tenantId), relation.getTo().getId())); - } - return Futures.successfulAsList(userFutures); - }, MoreExecutors.directExecutor()); - } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/CassandraUserDao.java b/dao/src/main/java/org/thingsboard/server/dao/user/CassandraUserDao.java index 2f134e9839..a192d41a01 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/CassandraUserDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/CassandraUserDao.java @@ -16,30 +16,18 @@ package org.thingsboard.server.dao.user; import com.datastax.driver.core.querybuilder.Select.Where; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.MoreExecutors; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.User; -import org.thingsboard.server.common.data.asset.Asset; -import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TextPageLink; -import org.thingsboard.server.common.data.page.TimePageLink; -import org.thingsboard.server.common.data.relation.EntityRelation; -import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.nosql.UserEntity; import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTextDao; -import org.thingsboard.server.dao.relation.RelationDao; import org.thingsboard.server.dao.util.NoSqlDao; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.UUID; @@ -52,9 +40,6 @@ import static com.datastax.driver.core.querybuilder.QueryBuilder.select; @NoSqlDao public class CassandraUserDao extends CassandraAbstractSearchTextDao implements UserDao { - @Autowired - private RelationDao relationDao; - @Override protected Class getColumnFamilyClass() { return UserEntity.class; @@ -100,17 +85,4 @@ public class CassandraUserDao extends CassandraAbstractSearchTextDao> findUsersByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink) { - log.debug("Try to find users by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink); - ListenableFuture> relations = relationDao.findRelations(new TenantId(tenantId), new EdgeId(edgeId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE, EntityType.USER, pageLink); - return Futures.transformAsync(relations, input -> { - List> userFutures = new ArrayList<>(input.size()); - for (EntityRelation relation : input) { - userFutures.add(findByIdAsync(new TenantId(tenantId), relation.getTo().getId())); - } - return Futures.successfulAsList(userFutures); - }, MoreExecutors.directExecutor()); - } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java index 5de17132ba..5c84cf6f01 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java @@ -15,11 +15,9 @@ */ package org.thingsboard.server.dao.user; -import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TextPageLink; -import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.dao.Dao; import java.util.List; @@ -42,7 +40,7 @@ public interface UserDao extends Dao { * @return the user entity */ User findByEmail(TenantId tenantId, String email); - + /** * Find tenant admin users by tenantId and page link. * @@ -51,7 +49,7 @@ public interface UserDao extends Dao { * @return the list of user entities */ List findTenantAdmins(UUID tenantId, TextPageLink pageLink); - + /** * Find customer users by tenantId, customerId and page link. * @@ -61,15 +59,4 @@ public interface UserDao extends Dao { * @return the list of user entities */ List findCustomerUsers(UUID tenantId, UUID customerId, TextPageLink pageLink); - - /** - * Find users by tenantId, edgeId and page link. - * - * @param tenantId the tenantId - * @param edgeId the edgeId - * @param pageLink the page link - * @return the list of user objects - */ - ListenableFuture> findUsersByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink); - } diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java index cd7b7ef860..a5e3495c9e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java @@ -18,10 +18,7 @@ package org.thingsboard.server.dao.user; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; -import com.google.common.base.Function; -import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.MoreExecutors; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.StringUtils; @@ -31,18 +28,12 @@ import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; -import org.thingsboard.server.common.data.edge.Edge; 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.id.UserCredentialsId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageLink; -import org.thingsboard.server.common.data.page.TimePageData; -import org.thingsboard.server.common.data.page.TimePageLink; -import org.thingsboard.server.common.data.relation.EntityRelation; -import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.UserCredentials; import org.thingsboard.server.dao.customer.CustomerDao; @@ -55,11 +46,9 @@ import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.tenant.TenantDao; -import javax.annotation.Nullable; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.ExecutionException; import static org.thingsboard.server.dao.service.Validator.validateId; import static org.thingsboard.server.dao.service.Validator.validatePageLink; @@ -327,57 +316,6 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic return failedLoginAttempts; } - @Override - public User assignUserToEdge(TenantId tenantId, UserId userId, EdgeId edgeId) { - User user = findUserById(tenantId, userId); - Edge edge = edgeService.findEdgeById(tenantId, edgeId); - if (edge == null) { - throw new DataValidationException("Can't assign user to non-existent edge!"); - } - if (!edge.getTenantId().getId().equals(user.getTenantId().getId())) { - throw new DataValidationException("Can't assign user to edge from different tenant!"); - } - try { - createRelation(tenantId, new EntityRelation(edgeId, userId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE)); - } catch (ExecutionException | InterruptedException e) { - log.warn("[{}] Failed to create user relation. Edge Id: [{}]", userId, edgeId); - throw new RuntimeException(e); - } - return user; - } - - @Override - public User unassignUserFromEdge(TenantId tenantId, UserId userId, EdgeId edgeId) { - User user = findUserById(tenantId, userId); - Edge edge = edgeService.findEdgeById(tenantId, edgeId); - if (edge == null) { - throw new DataValidationException("Can't unassign user from non-existent edge!"); - } - try { - deleteRelation(tenantId, new EntityRelation(edgeId, userId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE)); - } catch (ExecutionException | InterruptedException e) { - log.warn("[{}] Failed to delete user relation. Edge Id: [{}]", userId, edgeId); - throw new RuntimeException(e); - } - return user; - } - - @Override - public ListenableFuture> findUsersByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink) { - log.trace("Executing findUsersByTenantIdAndEdgeId, tenantId [{}], edgeId [{}], pageLink [{}]", tenantId, edgeId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(edgeId, INCORRECT_EDGE_ID + edgeId); - validatePageLink(pageLink, INCORRECT_PAGE_LINK + pageLink); - ListenableFuture> users = userDao.findUsersByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink); - return Futures.transform(users, new Function, TimePageData>() { - @Nullable - @Override - public TimePageData apply(@Nullable List users) { - return new TimePageData<>(users, pageLink); - } - }, MoreExecutors.directExecutor()); - } - private int increaseFailedLoginAttempts(User user) { JsonNode additionalInfo = user.getAdditionalInfo(); if (!(additionalInfo instanceof ObjectNode)) { From 600c9ec565fe803e7405b3f6ea9a1c2e6896c23c Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Sun, 14 Jun 2020 22:24:25 +0300 Subject: [PATCH 44/52] Refactoring of notification to edge. Added EdgeNotificationService and queue msg proto --- .../edge/DefaultEdgeNotificationService.java | 433 ++++++++++++++++++ .../service/edge/EdgeContextComponent.java | 4 + .../service/edge/EdgeNotificationService.java | 22 + .../service/edge/rpc/EdgeGrpcSession.java | 2 +- .../queue/DefaultTbCoreConsumerService.java | 16 +- .../service/queue/TbCoreConsumerStats.java | 6 + .../server/dao/edge/EdgeService.java | 13 +- common/queue/src/main/proto/queue.proto | 7 + .../server/dao/edge/EdgeServiceImpl.java | 380 +-------------- 9 files changed, 490 insertions(+), 393 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/EdgeNotificationService.java diff --git a/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java b/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java new file mode 100644 index 0000000000..a65e95d0ff --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java @@ -0,0 +1,433 @@ +package org.thingsboard.server.service.edge; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.MoreExecutors; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.codec.binary.Base64; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.Dashboard; +import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.EntityView; +import org.thingsboard.server.common.data.Event; +import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.edge.EdgeQueueEntityType; +import org.thingsboard.server.common.data.edge.EdgeQueueEntry; +import org.thingsboard.server.common.data.id.EdgeId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.TimePageData; +import org.thingsboard.server.common.data.page.TimePageLink; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; +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.msg.TbMsg; +import org.thingsboard.server.common.msg.queue.TbCallback; +import org.thingsboard.server.common.msg.session.SessionMsgType; +import org.thingsboard.server.dao.asset.AssetService; +import org.thingsboard.server.dao.device.DeviceService; +import org.thingsboard.server.dao.edge.EdgeService; +import org.thingsboard.server.dao.entityview.EntityViewService; +import org.thingsboard.server.dao.event.EventService; +import org.thingsboard.server.dao.relation.RelationService; +import org.thingsboard.server.dao.rule.RuleChainService; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.queue.util.TbCoreComponent; + +import javax.annotation.Nullable; +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import java.io.IOException; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +@Service +@TbCoreComponent +@Slf4j +public class DefaultEdgeNotificationService implements EdgeNotificationService { + + private static final ObjectMapper mapper = new ObjectMapper(); + + @Autowired + private EdgeService edgeService; + + @Autowired + private DeviceService deviceService; + + @Autowired + private AssetService assetService; + + @Autowired + private EntityViewService entityViewService; + + @Autowired + private RuleChainService ruleChainService; + + @Autowired + private RelationService relationService; + + @Autowired + private EventService eventService; + + private ExecutorService tsCallBackExecutor; + + @PostConstruct + public void initExecutor() { + tsCallBackExecutor = Executors.newSingleThreadExecutor(); + } + + @PreDestroy + public void shutdownExecutor() { + if (tsCallBackExecutor != null) { + tsCallBackExecutor.shutdownNow(); + } + } + + @Override + public TimePageData findQueueEvents(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink) { + return eventService.findEvents(tenantId, edgeId, DataConstants.EDGE_QUEUE_EVENT_TYPE, pageLink); + } + + @Override + public Edge setEdgeRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) throws IOException { + edge.setRootRuleChainId(ruleChainId); + Edge savedEdge = edgeService.saveEdge(edge); + RuleChain ruleChain = ruleChainService.findRuleChainById(tenantId, ruleChainId); + saveEventToEdgeQueue(tenantId, edge.getId(), EdgeQueueEntityType.RULE_CHAIN, DataConstants.ENTITY_UPDATED, mapper.writeValueAsString(ruleChain), new FutureCallback() { + @Override + public void onSuccess(@Nullable Void aVoid) { + log.debug("Event saved successfully!"); + } + + @Override + public void onFailure(Throwable t) { + log.debug("Failure during event save", t); + } + }); + return savedEdge; + } + + private void saveEventToEdgeQueue(TenantId tenantId, EdgeId edgeId, EdgeQueueEntityType entityType, String type, String data, FutureCallback callback) throws IOException { + log.debug("Pushing single event to edge queue. tenantId [{}], edgeId [{}], entityType [{}], type[{}], data [{}]", tenantId, edgeId, entityType, type, data); + + EdgeQueueEntry queueEntry = new EdgeQueueEntry(); + queueEntry.setEntityType(entityType); + queueEntry.setType(type); + queueEntry.setData(data); + + Event event = new Event(); + event.setEntityId(edgeId); + event.setTenantId(tenantId); + event.setType(DataConstants.EDGE_QUEUE_EVENT_TYPE); + event.setBody(mapper.valueToTree(queueEntry)); + ListenableFuture saveFuture = eventService.saveAsync(event); + + addMainCallback(saveFuture, callback); + } + + private void addMainCallback(ListenableFuture saveFuture, final FutureCallback callback) { + Futures.addCallback(saveFuture, new FutureCallback() { + @Override + public void onSuccess(@Nullable Event result) { + callback.onSuccess(null); + } + + @Override + public void onFailure(Throwable t) { + callback.onFailure(t); + } + }, tsCallBackExecutor); + } + + @Override + public void pushNotificationToEdge(TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg, TbCallback callback) { + if (tbMsg.getType().equals(SessionMsgType.POST_TELEMETRY_REQUEST.name()) || + tbMsg.getType().equals(SessionMsgType.POST_ATTRIBUTES_REQUEST.name()) || + tbMsg.getType().equals(DataConstants.ATTRIBUTES_UPDATED) || + tbMsg.getType().equals(DataConstants.ATTRIBUTES_DELETED)) { + processCustomTbMsg(tenantId, tbMsg, callback); + } else { + try { + switch (tbMsg.getOriginator().getEntityType()) { + case EDGE: + processEdge(tenantId, tbMsg, callback); + break; + case ASSET: + processAsset(tenantId, tbMsg, callback); + break; + case DEVICE: + processDevice(tenantId, tbMsg, callback); + break; + case DASHBOARD: + processDashboard(tenantId, tbMsg, callback); + break; + case RULE_CHAIN: + processRuleChain(tenantId, tbMsg, callback); + break; + case ENTITY_VIEW: + processEntityView(tenantId, tbMsg, callback); + break; + case ALARM: + processAlarm(tenantId, tbMsg, callback); + break; + default: + log.debug("Entity type [{}] is not designed to be pushed to edge", tbMsg.getOriginator().getEntityType()); + } + } catch (IOException e) { + log.error("Can't push to edge updates, entity type [{}], data [{}]", tbMsg.getOriginator().getEntityType(), tbMsg.getData(), e); + } + } + } + + + private void processCustomTbMsg(TenantId tenantId, TbMsg tbMsg, FutureCallback callback) { + ListenableFuture edgeIdFuture = getEdgeIdByOriginatorId(tenantId, tbMsg.getOriginator()); + Futures.transform(edgeIdFuture, edgeId -> { + EdgeQueueEntityType edgeQueueEntityType = getEdgeQueueTypeByEntityType(tbMsg.getOriginator().getEntityType()); + if (edgeId != null && edgeQueueEntityType != null) { + try { + saveEventToEdgeQueue(tenantId, edgeId, edgeQueueEntityType, tbMsg.getType(), Base64.encodeBase64String(TbMsg.toByteArray(tbMsg)), callback); + } catch (IOException e) { + log.error("Error while saving custom tbMsg into Edge Queue", e); + } + } + return null; + }, MoreExecutors.directExecutor()); + } + + private void processDevice(TenantId tenantId, TbMsg tbMsg, FutureCallback callback) throws IOException { + switch (tbMsg.getType()) { + case DataConstants.ENTITY_ASSIGNED_TO_EDGE: + case DataConstants.ENTITY_UNASSIGNED_FROM_EDGE: + processAssignedEntity(tenantId, tbMsg, EdgeQueueEntityType.DEVICE, callback); + break; + case DataConstants.ENTITY_DELETED: + case DataConstants.ENTITY_CREATED: + case DataConstants.ENTITY_UPDATED: + Device device = mapper.readValue(tbMsg.getData(), Device.class); + pushEventToEdge(tenantId, device.getId(), EdgeQueueEntityType.DEVICE, tbMsg, callback); + break; + default: + log.warn("Unsupported msgType [{}], tbMsg [{}]", tbMsg.getType(), tbMsg); + } + } + + private void processEdge(TenantId tenantId, TbMsg tbMsg, FutureCallback callback) throws IOException { + switch (tbMsg.getType()) { + case DataConstants.ENTITY_DELETED: + case DataConstants.ENTITY_CREATED: + case DataConstants.ENTITY_UPDATED: + // TODO: voba - handle properly edge creation + break; + default: + log.warn("Unsupported msgType [{}], tbMsg [{}]", tbMsg.getType(), tbMsg); + } + } + + private void processAsset(TenantId tenantId, TbMsg tbMsg, FutureCallback callback) throws IOException { + switch (tbMsg.getType()) { + case DataConstants.ENTITY_ASSIGNED_TO_EDGE: + case DataConstants.ENTITY_UNASSIGNED_FROM_EDGE: + processAssignedEntity(tenantId, tbMsg, EdgeQueueEntityType.ASSET, callback); + break; + case DataConstants.ENTITY_DELETED: + case DataConstants.ENTITY_CREATED: + case DataConstants.ENTITY_UPDATED: + Asset asset = mapper.readValue(tbMsg.getData(), Asset.class); + pushEventToEdge(tenantId, asset.getId(), EdgeQueueEntityType.ASSET, tbMsg, callback); + break; + default: + log.warn("Unsupported msgType [{}], tbMsg [{}]", tbMsg.getType(), tbMsg); + } + } + + private void processEntityView(TenantId tenantId, TbMsg tbMsg, FutureCallback callback) throws IOException { + switch (tbMsg.getType()) { + case DataConstants.ENTITY_ASSIGNED_TO_EDGE: + case DataConstants.ENTITY_UNASSIGNED_FROM_EDGE: + processAssignedEntity(tenantId, tbMsg, EdgeQueueEntityType.ENTITY_VIEW, callback); + break; + case DataConstants.ENTITY_DELETED: + case DataConstants.ENTITY_CREATED: + case DataConstants.ENTITY_UPDATED: + EntityView entityView = mapper.readValue(tbMsg.getData(), EntityView.class); + pushEventToEdge(tenantId, entityView.getId(), EdgeQueueEntityType.ENTITY_VIEW, tbMsg, callback); + break; + default: + log.warn("Unsupported msgType [{}], tbMsg [{}]", tbMsg.getType(), tbMsg); + } + } + + private void processAlarm(TenantId tenantId, TbMsg tbMsg, FutureCallback callback) throws IOException { + switch (tbMsg.getType()) { + case DataConstants.ENTITY_DELETED: + case DataConstants.ENTITY_CREATED: + case DataConstants.ENTITY_UPDATED: + case DataConstants.ALARM_ACK: + case DataConstants.ALARM_CLEAR: + Alarm alarm = mapper.readValue(tbMsg.getData(), Alarm.class); + EdgeQueueEntityType edgeQueueEntityType = getEdgeQueueTypeByEntityType(alarm.getOriginator().getEntityType()); + if (edgeQueueEntityType != null) { + pushEventToEdge(tenantId, alarm.getOriginator(), EdgeQueueEntityType.ALARM, tbMsg, callback); + } + break; + default: + log.warn("Unsupported msgType [{}], tbMsg [{}]", tbMsg.getType(), tbMsg); + } + } + + private void processDashboard(TenantId tenantId, TbMsg tbMsg, FutureCallback callback) throws IOException { + switch (tbMsg.getType()) { + case DataConstants.ENTITY_ASSIGNED_TO_EDGE: + case DataConstants.ENTITY_UNASSIGNED_FROM_EDGE: + processAssignedEntity(tenantId, tbMsg, EdgeQueueEntityType.DASHBOARD, callback); + break; + case DataConstants.ENTITY_DELETED: + case DataConstants.ENTITY_CREATED: + case DataConstants.ENTITY_UPDATED: + Dashboard dashboard = mapper.readValue(tbMsg.getData(), Dashboard.class); + ListenableFuture> future = edgeService.findEdgesByTenantIdAndDashboardId(tenantId, dashboard.getId(), new TimePageLink(Integer.MAX_VALUE)); + Futures.transform(future, edges -> { + if (edges != null && edges.getData() != null && !edges.getData().isEmpty()) { + try { + for (Edge edge : edges.getData()) { + pushEventToEdge(tenantId, edge.getId(), EdgeQueueEntityType.DASHBOARD, tbMsg, callback); + } + } catch (IOException e) { + log.error("Can't push event to edge", e); + } + } + return null; + }, MoreExecutors.directExecutor()); + break; + default: + log.warn("Unsupported msgType [{}], tbMsg [{}]", tbMsg.getType(), tbMsg); + } + } + + private void processRuleChain(TenantId tenantId, TbMsg tbMsg, FutureCallback callback) throws IOException { + switch (tbMsg.getType()) { + case DataConstants.ENTITY_ASSIGNED_TO_EDGE: + case DataConstants.ENTITY_UNASSIGNED_FROM_EDGE: + processAssignedEntity(tenantId, tbMsg, EdgeQueueEntityType.RULE_CHAIN, callback); + break; + case DataConstants.ENTITY_DELETED: + case DataConstants.ENTITY_CREATED: + case DataConstants.ENTITY_UPDATED: + RuleChain ruleChain = mapper.readValue(tbMsg.getData(), RuleChain.class); + if (RuleChainType.EDGE.equals(ruleChain.getType())) { + ListenableFuture> future = edgeService.findEdgesByTenantIdAndRuleChainId(tenantId, ruleChain.getId(), new TimePageLink(Integer.MAX_VALUE)); + Futures.transform(future, edges -> { + if (edges != null && edges.getData() != null && !edges.getData().isEmpty()) { + try { + for (Edge edge : edges.getData()) { + pushEventToEdge(tenantId, edge.getId(), EdgeQueueEntityType.RULE_CHAIN, tbMsg, callback); + } + } catch (IOException e) { + log.error("Can't push event to edge", e); + } + } + return null; + }, MoreExecutors.directExecutor()); + } + break; + default: + log.warn("Unsupported msgType [{}], tbMsg [{}]", tbMsg.getType(), tbMsg); + } + } + + + private void processAssignedEntity(TenantId tenantId, TbMsg tbMsg, EdgeQueueEntityType entityType, FutureCallback callback) throws IOException { + EdgeId edgeId; + switch (tbMsg.getType()) { + case DataConstants.ENTITY_ASSIGNED_TO_EDGE: + edgeId = new EdgeId(UUID.fromString(tbMsg.getMetaData().getValue("assignedEdgeId"))); + pushEventToEdge(tenantId, edgeId, entityType, tbMsg, callback); + break; + case DataConstants.ENTITY_UNASSIGNED_FROM_EDGE: + edgeId = new EdgeId(UUID.fromString(tbMsg.getMetaData().getValue("unassignedEdgeId"))); + pushEventToEdge(tenantId, edgeId, entityType, tbMsg, callback); + break; + + } + } + + + private void pushEventToEdge(TenantId tenantId, EntityId originatorId, EdgeQueueEntityType edgeQueueEntityType, TbMsg tbMsg, FutureCallback callback) { + ListenableFuture edgeIdFuture = getEdgeIdByOriginatorId(tenantId, originatorId); + Futures.transform(edgeIdFuture, edgeId -> { + if (edgeId != null) { + try { + pushEventToEdge(tenantId, edgeId, edgeQueueEntityType, tbMsg, callback); + } catch (Exception e) { + log.error("Failed to push event to edge, edgeId [{}], tbMsg [{}]", edgeId, tbMsg, e); + } + } + return null; + }, + MoreExecutors.directExecutor()); + } + + + private ListenableFuture getEdgeIdByOriginatorId(TenantId tenantId, EntityId originatorId) { + List originatorEdgeRelations = relationService.findByToAndType(tenantId, originatorId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE); + if (originatorEdgeRelations != null && originatorEdgeRelations.size() > 0) { + return Futures.immediateFuture(new EdgeId(originatorEdgeRelations.get(0).getFrom().getId())); + } else { + return Futures.immediateFuture(null); + } + } + + + private void pushEventToEdge(TenantId tenantId, EdgeId edgeId, EdgeQueueEntityType entityType, TbMsg tbMsg, FutureCallback callback) throws IOException { + log.debug("Pushing event(s) to edge queue. tenantId [{}], edgeId [{}], entityType [{}], tbMsg [{}]", tenantId, edgeId, entityType, tbMsg); + + saveEventToEdgeQueue(tenantId, edgeId, entityType, tbMsg.getType(), tbMsg.getData(), callback); + + if (entityType.equals(EdgeQueueEntityType.RULE_CHAIN)) { + pushRuleChainMetadataToEdge(tenantId, edgeId, tbMsg, callback); + } + } + + private void pushRuleChainMetadataToEdge(TenantId tenantId, EdgeId edgeId, TbMsg tbMsg, FutureCallback callback) throws IOException { + RuleChain ruleChain = mapper.readValue(tbMsg.getData(), RuleChain.class); + switch (tbMsg.getType()) { + case DataConstants.ENTITY_ASSIGNED_TO_EDGE: + case DataConstants.ENTITY_UNASSIGNED_FROM_EDGE: + case DataConstants.ENTITY_UPDATED: + RuleChainMetaData ruleChainMetaData = ruleChainService.loadRuleChainMetaData(tenantId, ruleChain.getId()); + saveEventToEdgeQueue(tenantId, edgeId, EdgeQueueEntityType.RULE_CHAIN_METADATA, tbMsg.getType(), mapper.writeValueAsString(ruleChainMetaData), callback); + break; + default: + log.warn("Unsupported msgType [{}], tbMsg [{}]", tbMsg.getType(), tbMsg); + } + } + + + private EdgeQueueEntityType getEdgeQueueTypeByEntityType(EntityType entityType) { + switch (entityType) { + case DEVICE: + return EdgeQueueEntityType.DEVICE; + case ASSET: + return EdgeQueueEntityType.ASSET; + case ENTITY_VIEW: + return EdgeQueueEntityType.ENTITY_VIEW; + default: + log.info("Unsupported entity type: [{}]", entityType); + return null; + } + } +} + 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 a7a9ee1bed..26e094f9b3 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 @@ -53,6 +53,10 @@ public class EdgeContextComponent { @Autowired private EdgeService edgeService; + @Lazy + @Autowired + private EdgeNotificationService edgeNotificationService; + @Lazy @Autowired private AssetService assetService; diff --git a/application/src/main/java/org/thingsboard/server/service/edge/EdgeNotificationService.java b/application/src/main/java/org/thingsboard/server/service/edge/EdgeNotificationService.java new file mode 100644 index 0000000000..f4de0f974e --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/EdgeNotificationService.java @@ -0,0 +1,22 @@ +package org.thingsboard.server.service.edge; + +import org.thingsboard.server.common.data.Event; +import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.id.EdgeId; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.TimePageData; +import org.thingsboard.server.common.data.page.TimePageLink; +import org.thingsboard.server.common.msg.queue.TbCallback; +import org.thingsboard.server.gen.transport.TransportProtos; + +import java.io.IOException; + +public interface EdgeNotificationService { + + TimePageData findQueueEvents(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink); + + Edge setEdgeRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) throws IOException; + + void pushNotificationToEdge(TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg, TbCallback callback); +} 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 f722e09de5..1e77f438c5 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 @@ -172,7 +172,7 @@ public final class EdgeGrpcSession implements Closeable { TimePageData pageData; UUID ifOffset = null; do { - pageData = ctx.getEdgeService().findQueueEvents(edge.getTenantId(), edge.getId(), pageLink); + pageData = ctx.getEdgeNotificationService().findQueueEvents(edge.getTenantId(), edge.getId(), pageLink); if (isConnected() && !pageData.getData().isEmpty()) { log.trace("[{}] [{}] event(s) are going to be processed.", this.sessionId, pageData.getData().size()); for (Event event : pageData.getData()) { 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 b7f6bd3dd2..3c6e991d5e 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 @@ -28,6 +28,7 @@ import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.gen.transport.TransportProtos.DeviceStateServiceMsgProto; +import org.thingsboard.server.gen.transport.TransportProtos.EdgeNotificationMsgProto; import org.thingsboard.server.gen.transport.TransportProtos.FromDeviceRPCResponseProto; import org.thingsboard.server.gen.transport.TransportProtos.LocalSubscriptionServiceMsgProto; import org.thingsboard.server.gen.transport.TransportProtos.SubscriptionMgrMsgProto; @@ -42,6 +43,7 @@ import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.discovery.PartitionChangeEvent; import org.thingsboard.server.queue.provider.TbCoreQueueFactory; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.EdgeNotificationService; import org.thingsboard.server.service.encoding.DataDecodingEncodingService; import org.thingsboard.server.service.queue.processing.AbstractConsumerService; import org.thingsboard.server.service.rpc.FromDeviceRpcResponse; @@ -82,18 +84,20 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService actorMsg = encodingService.decode(toCoreMsg.getToDeviceActorNotificationMsg().toByteArray()); if (actorMsg.isPresent()) { @@ -275,6 +282,13 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService> findEdgeTypesByTenantId(TenantId tenantId); - void pushEventToEdge(TenantId tenantId, TbMsg tbMsg, FutureCallback callback); - - TimePageData findQueueEvents(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink); - - Edge setEdgeRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) throws IOException; void assignDefaultRuleChainsToEdge(TenantId tenantId, EdgeId edgeId); diff --git a/common/queue/src/main/proto/queue.proto b/common/queue/src/main/proto/queue.proto index d3e850b39c..ab0193318a 100644 --- a/common/queue/src/main/proto/queue.proto +++ b/common/queue/src/main/proto/queue.proto @@ -352,6 +352,12 @@ message FromDeviceRPCResponseProto { string response = 3; int32 error = 4; } + +message EdgeNotificationMsgProto { + int64 tenantIdMSB = 1; + int64 tenantIdLSB = 2; +} + /** * Main messages; */ @@ -377,6 +383,7 @@ message ToCoreMsg { DeviceStateServiceMsgProto deviceStateServiceMsg = 2; SubscriptionMgrMsgProto toSubscriptionMgrMsg = 3; bytes toDeviceActorNotificationMsg = 4; + EdgeNotificationMsgProto edgeNotificationMsg = 5; } /* High priority messages with low latency are handled by ThingsBoard Core Service separately */ diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java index 17ff2003bf..48e8912225 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.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,14 +15,11 @@ */ package org.thingsboard.server.dao.edge; -import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.base.Function; -import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.codec.binary.Base64; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; @@ -31,19 +28,10 @@ import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import org.thingsboard.server.common.data.Customer; -import org.thingsboard.server.common.data.Dashboard; -import org.thingsboard.server.common.data.DataConstants; -import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.EntityView; -import org.thingsboard.server.common.data.Event; import org.thingsboard.server.common.data.Tenant; -import org.thingsboard.server.common.data.alarm.Alarm; -import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.edge.Edge; -import org.thingsboard.server.common.data.edge.EdgeQueueEntityType; -import org.thingsboard.server.common.data.edge.EdgeQueueEntry; import org.thingsboard.server.common.data.edge.EdgeSearchQuery; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DashboardId; @@ -57,19 +45,10 @@ import org.thingsboard.server.common.data.page.TimePageData; import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntitySearchDirection; -import org.thingsboard.server.common.data.relation.RelationTypeGroup; 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.msg.TbMsg; -import org.thingsboard.server.common.msg.session.SessionMsgType; -import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.customer.CustomerDao; import org.thingsboard.server.dao.dashboard.DashboardService; -import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.entity.AbstractEntityService; -import org.thingsboard.server.dao.entityview.EntityViewService; -import org.thingsboard.server.dao.event.EventService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.rule.RuleChainService; @@ -79,17 +58,11 @@ import org.thingsboard.server.dao.service.Validator; import org.thingsboard.server.dao.tenant.TenantDao; import javax.annotation.Nullable; -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; -import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Optional; -import java.util.UUID; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.stream.Collectors; import static org.thingsboard.server.common.data.CacheConstants.EDGE_CACHE; @@ -104,8 +77,6 @@ import static org.thingsboard.server.dao.service.Validator.validateString; @Slf4j public class EdgeServiceImpl extends AbstractEntityService implements EdgeService { - private static final ObjectMapper mapper = new ObjectMapper(); - public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; public static final String INCORRECT_PAGE_LINK = "Incorrect page link "; public static final String INCORRECT_CUSTOMER_ID = "Incorrect customerId "; @@ -123,41 +94,15 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic @Autowired private CacheManager cacheManager; - @Autowired - private EventService eventService; - @Autowired private DashboardService dashboardService; @Autowired private RuleChainService ruleChainService; - @Autowired - private DeviceService deviceService; - - @Autowired - private AssetService assetService; - - @Autowired - private EntityViewService entityViewService; - @Autowired private RelationService relationService; - private ExecutorService tsCallBackExecutor; - - @PostConstruct - public void initExecutor() { - tsCallBackExecutor = Executors.newSingleThreadExecutor(); - } - - @PreDestroy - public void shutdownExecutor() { - if (tsCallBackExecutor != null) { - tsCallBackExecutor.shutdownNow(); - } - } - @Override public Edge findEdgeById(TenantId tenantId, EdgeId edgeId) { log.trace("Executing findEdgeById [{}]", edgeId); @@ -259,7 +204,6 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic return edgeDao.findEdgesByTenantIdAndIdsAsync(tenantId.getId(), toUUIDs(edgeIds)); } - @Override public void deleteEdgesByTenantId(TenantId tenantId) { log.trace("Executing deleteEdgesByTenantId, tenantId [{}]", tenantId); @@ -344,327 +288,6 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic }, MoreExecutors.directExecutor()); } - @Override - public void pushEventToEdge(TenantId tenantId, TbMsg tbMsg, FutureCallback callback) { - if (tbMsg.getType().equals(SessionMsgType.POST_TELEMETRY_REQUEST.name()) || - tbMsg.getType().equals(SessionMsgType.POST_ATTRIBUTES_REQUEST.name()) || - tbMsg.getType().equals(DataConstants.ATTRIBUTES_UPDATED) || - tbMsg.getType().equals(DataConstants.ATTRIBUTES_DELETED)) { - processCustomTbMsg(tenantId, tbMsg, callback); - } else { - try { - switch (tbMsg.getOriginator().getEntityType()) { - case EDGE: - processEdge(tenantId, tbMsg, callback); - break; - case ASSET: - processAsset(tenantId, tbMsg, callback); - break; - case DEVICE: - processDevice(tenantId, tbMsg, callback); - break; - case DASHBOARD: - processDashboard(tenantId, tbMsg, callback); - break; - case RULE_CHAIN: - processRuleChain(tenantId, tbMsg, callback); - break; - case ENTITY_VIEW: - processEntityView(tenantId, tbMsg, callback); - break; - case ALARM: - processAlarm(tenantId, tbMsg, callback); - break; - default: - log.debug("Entity type [{}] is not designed to be pushed to edge", tbMsg.getOriginator().getEntityType()); - } - } catch (IOException e) { - log.error("Can't push to edge updates, entity type [{}], data [{}]", tbMsg.getOriginator().getEntityType(), tbMsg.getData(), e); - } - } - } - - private void processCustomTbMsg(TenantId tenantId, TbMsg tbMsg, FutureCallback callback) { - ListenableFuture edgeIdFuture = getEdgeIdByOriginatorId(tenantId, tbMsg.getOriginator()); - Futures.transform(edgeIdFuture, edgeId -> { - EdgeQueueEntityType edgeQueueEntityType = getEdgeQueueTypeByEntityType(tbMsg.getOriginator().getEntityType()); - if (edgeId != null && edgeQueueEntityType != null) { - try { - saveEventToEdgeQueue(tenantId, edgeId, edgeQueueEntityType, tbMsg.getType(), Base64.encodeBase64String(TbMsg.toByteArray(tbMsg)), callback); - } catch (IOException e) { - log.error("Error while saving custom tbMsg into Edge Queue", e); - } - } - return null; - }, MoreExecutors.directExecutor()); - } - - private EdgeQueueEntityType getEdgeQueueTypeByEntityType(EntityType entityType) { - switch (entityType) { - case DEVICE: - return EdgeQueueEntityType.DEVICE; - case ASSET: - return EdgeQueueEntityType.ASSET; - case ENTITY_VIEW: - return EdgeQueueEntityType.ENTITY_VIEW; - default: - log.info("Unsupported entity type: [{}]", entityType); - return null; - } - } - - private ListenableFuture getEdgeIdByOriginatorId(TenantId tenantId, EntityId originatorId) { - List originatorEdgeRelations = relationService.findByToAndType(tenantId, originatorId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE); - if (originatorEdgeRelations != null && originatorEdgeRelations.size() > 0) { - return Futures.immediateFuture(new EdgeId(originatorEdgeRelations.get(0).getFrom().getId())); - } else { - return Futures.immediateFuture(null); - } - } - - private void pushEventToEdge(TenantId tenantId, EntityId originatorId, EdgeQueueEntityType edgeQueueEntityType, TbMsg tbMsg, FutureCallback callback) { - ListenableFuture edgeIdFuture = getEdgeIdByOriginatorId(tenantId, originatorId); - Futures.transform(edgeIdFuture, edgeId -> { - if (edgeId != null) { - try { - pushEventToEdge(tenantId, edgeId, edgeQueueEntityType, tbMsg, callback); - } catch (Exception e) { - log.error("Failed to push event to edge, edgeId [{}], tbMsg [{}]", edgeId, tbMsg, e); - } - } - return null; - }, - MoreExecutors.directExecutor()); - } - - private void processDevice(TenantId tenantId, TbMsg tbMsg, FutureCallback callback) throws IOException { - switch (tbMsg.getType()) { - case DataConstants.ENTITY_ASSIGNED_TO_EDGE: - case DataConstants.ENTITY_UNASSIGNED_FROM_EDGE: - processAssignedEntity(tenantId, tbMsg, EdgeQueueEntityType.DEVICE, callback); - break; - case DataConstants.ENTITY_DELETED: - case DataConstants.ENTITY_CREATED: - case DataConstants.ENTITY_UPDATED: - Device device = mapper.readValue(tbMsg.getData(), Device.class); - pushEventToEdge(tenantId, device.getId(), EdgeQueueEntityType.DEVICE, tbMsg, callback); - break; - default: - log.warn("Unsupported msgType [{}], tbMsg [{}]", tbMsg.getType(), tbMsg); - } - } - - private void processEdge(TenantId tenantId, TbMsg tbMsg, FutureCallback callback) throws IOException { - switch (tbMsg.getType()) { - case DataConstants.ENTITY_DELETED: - case DataConstants.ENTITY_CREATED: - case DataConstants.ENTITY_UPDATED: - // TODO: voba - handle properly edge creation - break; - default: - log.warn("Unsupported msgType [{}], tbMsg [{}]", tbMsg.getType(), tbMsg); - } - } - - private void processAsset(TenantId tenantId, TbMsg tbMsg, FutureCallback callback) throws IOException { - switch (tbMsg.getType()) { - case DataConstants.ENTITY_ASSIGNED_TO_EDGE: - case DataConstants.ENTITY_UNASSIGNED_FROM_EDGE: - processAssignedEntity(tenantId, tbMsg, EdgeQueueEntityType.ASSET, callback); - break; - case DataConstants.ENTITY_DELETED: - case DataConstants.ENTITY_CREATED: - case DataConstants.ENTITY_UPDATED: - Asset asset = mapper.readValue(tbMsg.getData(), Asset.class); - pushEventToEdge(tenantId, asset.getId(), EdgeQueueEntityType.ASSET, tbMsg, callback); - break; - default: - log.warn("Unsupported msgType [{}], tbMsg [{}]", tbMsg.getType(), tbMsg); - } - } - - private void processEntityView(TenantId tenantId, TbMsg tbMsg, FutureCallback callback) throws IOException { - switch (tbMsg.getType()) { - case DataConstants.ENTITY_ASSIGNED_TO_EDGE: - case DataConstants.ENTITY_UNASSIGNED_FROM_EDGE: - processAssignedEntity(tenantId, tbMsg, EdgeQueueEntityType.ENTITY_VIEW, callback); - break; - case DataConstants.ENTITY_DELETED: - case DataConstants.ENTITY_CREATED: - case DataConstants.ENTITY_UPDATED: - EntityView entityView = mapper.readValue(tbMsg.getData(), EntityView.class); - pushEventToEdge(tenantId, entityView.getId(), EdgeQueueEntityType.ENTITY_VIEW, tbMsg, callback); - break; - default: - log.warn("Unsupported msgType [{}], tbMsg [{}]", tbMsg.getType(), tbMsg); - } - } - - private void processAlarm(TenantId tenantId, TbMsg tbMsg, FutureCallback callback) throws IOException { - switch (tbMsg.getType()) { - case DataConstants.ENTITY_DELETED: - case DataConstants.ENTITY_CREATED: - case DataConstants.ENTITY_UPDATED: - case DataConstants.ALARM_ACK: - case DataConstants.ALARM_CLEAR: - Alarm alarm = mapper.readValue(tbMsg.getData(), Alarm.class); - EdgeQueueEntityType edgeQueueEntityType = getEdgeQueueTypeByEntityType(alarm.getOriginator().getEntityType()); - if (edgeQueueEntityType != null) { - pushEventToEdge(tenantId, alarm.getOriginator(), EdgeQueueEntityType.ALARM, tbMsg, callback); - } - break; - default: - log.warn("Unsupported msgType [{}], tbMsg [{}]", tbMsg.getType(), tbMsg); - } - } - - private void processDashboard(TenantId tenantId, TbMsg tbMsg, FutureCallback callback) throws IOException { - processAssignedEntity(tenantId, tbMsg, EdgeQueueEntityType.DASHBOARD, callback); - } - - private void processRuleChain(TenantId tenantId, TbMsg tbMsg, FutureCallback callback) throws IOException { - switch (tbMsg.getType()) { - case DataConstants.ENTITY_ASSIGNED_TO_EDGE: - case DataConstants.ENTITY_UNASSIGNED_FROM_EDGE: - processAssignedEntity(tenantId, tbMsg, EdgeQueueEntityType.RULE_CHAIN, callback); - break; - case DataConstants.ENTITY_DELETED: - case DataConstants.ENTITY_CREATED: - case DataConstants.ENTITY_UPDATED: - RuleChain ruleChain = mapper.readValue(tbMsg.getData(), RuleChain.class); - if (RuleChainType.EDGE.equals(ruleChain.getType())) { - ListenableFuture> future = findEdgesByTenantIdAndRuleChainId(tenantId, ruleChain.getId(), new TimePageLink(Integer.MAX_VALUE)); - Futures.transform(future, edges -> { - if (edges != null && edges.getData() != null && !edges.getData().isEmpty()) { - try { - for (Edge edge : edges.getData()) { - pushEventToEdge(tenantId, edge.getId(), EdgeQueueEntityType.RULE_CHAIN, tbMsg, callback); - } - } catch (IOException e) { - log.error("Can't push event to edge", e); - } - } - return null; - }, MoreExecutors.directExecutor()); - } - break; - default: - log.warn("Unsupported msgType [{}], tbMsg [{}]", tbMsg.getType(), tbMsg); - } - } - - private void processAssignedEntity(TenantId tenantId, TbMsg tbMsg, EdgeQueueEntityType entityType, FutureCallback callback) throws IOException { - EdgeId edgeId; - switch (tbMsg.getType()) { - case DataConstants.ENTITY_ASSIGNED_TO_EDGE: - edgeId = new EdgeId(UUID.fromString(tbMsg.getMetaData().getValue("assignedEdgeId"))); - pushEventToEdge(tenantId, edgeId, entityType, tbMsg, callback); - break; - case DataConstants.ENTITY_UNASSIGNED_FROM_EDGE: - edgeId = new EdgeId(UUID.fromString(tbMsg.getMetaData().getValue("unassignedEdgeId"))); - pushEventToEdge(tenantId, edgeId, entityType, tbMsg, callback); - break; - case DataConstants.ENTITY_DELETED: - case DataConstants.ENTITY_CREATED: - case DataConstants.ENTITY_UPDATED: - Dashboard dashboard = mapper.readValue(tbMsg.getData(), Dashboard.class); - ListenableFuture> future = findEdgesByTenantIdAndDashboardId(tenantId, dashboard.getId(), new TimePageLink(Integer.MAX_VALUE)); - Futures.transform(future, edges -> { - if (edges != null && edges.getData() != null && !edges.getData().isEmpty()) { - try { - for (Edge edge : edges.getData()) { - pushEventToEdge(tenantId, edge.getId(), EdgeQueueEntityType.DASHBOARD, tbMsg, callback); - } - } catch (IOException e) { - log.error("Can't push event to edge", e); - } - } - return null; - }, MoreExecutors.directExecutor()); - break; - } - } - - private void pushEventToEdge(TenantId tenantId, EdgeId edgeId, EdgeQueueEntityType entityType, TbMsg tbMsg, FutureCallback callback) throws IOException { - log.debug("Pushing event(s) to edge queue. tenantId [{}], edgeId [{}], entityType [{}], tbMsg [{}]", tenantId, edgeId, entityType, tbMsg); - - saveEventToEdgeQueue(tenantId, edgeId, entityType, tbMsg.getType(), tbMsg.getData(), callback); - - if (entityType.equals(EdgeQueueEntityType.RULE_CHAIN)) { - pushRuleChainMetadataToEdge(tenantId, edgeId, tbMsg, callback); - } - } - - private void saveEventToEdgeQueue(TenantId tenantId, EdgeId edgeId, EdgeQueueEntityType entityType, String type, String data, FutureCallback callback) throws IOException { - log.debug("Pushing single event to edge queue. tenantId [{}], edgeId [{}], entityType [{}], type[{}], data [{}]", tenantId, edgeId, entityType, type, data); - - EdgeQueueEntry queueEntry = new EdgeQueueEntry(); - queueEntry.setEntityType(entityType); - queueEntry.setType(type); - queueEntry.setData(data); - - Event event = new Event(); - event.setEntityId(edgeId); - event.setTenantId(tenantId); - event.setType(DataConstants.EDGE_QUEUE_EVENT_TYPE); - event.setBody(mapper.valueToTree(queueEntry)); - ListenableFuture saveFuture = eventService.saveAsync(event); - - addMainCallback(saveFuture, callback); - } - - private void addMainCallback(ListenableFuture saveFuture, final FutureCallback callback) { - Futures.addCallback(saveFuture, new FutureCallback() { - @Override - public void onSuccess(@Nullable Event result) { - callback.onSuccess(null); - } - - @Override - public void onFailure(Throwable t) { - callback.onFailure(t); - } - }, tsCallBackExecutor); - } - - private void pushRuleChainMetadataToEdge(TenantId tenantId, EdgeId edgeId, TbMsg tbMsg, FutureCallback callback) throws IOException { - RuleChain ruleChain = mapper.readValue(tbMsg.getData(), RuleChain.class); - switch (tbMsg.getType()) { - case DataConstants.ENTITY_ASSIGNED_TO_EDGE: - case DataConstants.ENTITY_UNASSIGNED_FROM_EDGE: - case DataConstants.ENTITY_UPDATED: - RuleChainMetaData ruleChainMetaData = ruleChainService.loadRuleChainMetaData(tenantId, ruleChain.getId()); - saveEventToEdgeQueue(tenantId, edgeId, EdgeQueueEntityType.RULE_CHAIN_METADATA, tbMsg.getType(), mapper.writeValueAsString(ruleChainMetaData), callback); - break; - default: - log.warn("Unsupported msgType [{}], tbMsg [{}]", tbMsg.getType(), tbMsg); - } - } - - @Override - public TimePageData findQueueEvents(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink) { - return eventService.findEvents(tenantId, edgeId, DataConstants.EDGE_QUEUE_EVENT_TYPE, pageLink); - } - - @Override - public Edge setEdgeRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) throws IOException { - edge.setRootRuleChainId(ruleChainId); - Edge savedEdge = saveEdge(edge); - RuleChain ruleChain = ruleChainService.findRuleChainById(tenantId, ruleChainId); - saveEventToEdgeQueue(tenantId, edge.getId(), EdgeQueueEntityType.RULE_CHAIN, DataConstants.ENTITY_UPDATED, mapper.writeValueAsString(ruleChain), new FutureCallback() { - @Override - public void onSuccess(@Nullable Void aVoid) { - log.debug("Event saved successfully!"); - } - - @Override - public void onFailure(Throwable t) { - log.debug("Failure during event save", t); - } - }); - return savedEdge; - } - @Override public void assignDefaultRuleChainsToEdge(TenantId tenantId, EdgeId edgeId) { log.trace("Executing assignDefaultRuleChainsToEdge, tenantId [{}], edgeId [{}]", tenantId, edgeId); @@ -713,7 +336,6 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic }, MoreExecutors.directExecutor()); } - private DataValidator edgeValidator = new DataValidator() { From 7a555fca8d78388c0a66890935007dc3fb008e9a Mon Sep 17 00:00:00 2001 From: Yevhen Bondarenko <56396344+YevhenBondarenko@users.noreply.github.com> Date: Mon, 15 Jun 2020 12:37:43 +0300 Subject: [PATCH 45/52] removed Apache Email Validator (#2945) * removed Apache Email Validator * improvements Email Validator regex * refactored Email Validator --- dao/pom.xml | 82 +++++++++---------- .../server/dao/service/DataValidator.java | 18 +++- pom.xml | 6 -- 3 files changed, 55 insertions(+), 51 deletions(-) diff --git a/dao/pom.xml b/dao/pom.xml index 90417b8a53..86139125cb 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -91,26 +91,26 @@ mockito-all test - - org.apache.commons - commons-lang3 - - - commons-validator - commons-validator - - - com.fasterxml.jackson.core - jackson-databind - + + org.apache.commons + commons-lang3 + + + commons-collections + commons-collections + + + com.fasterxml.jackson.core + jackson-databind + org.springframework spring-context - - org.springframework - spring-tx - + + org.springframework + spring-tx + org.springframework spring-web @@ -120,24 +120,24 @@ org.springframework.security spring-security-oauth2-client - - com.datastax.cassandra - cassandra-driver-core - - - com.datastax.cassandra - cassandra-driver-mapping - - - com.datastax.cassandra - cassandra-driver-extras - + + com.datastax.cassandra + cassandra-driver-core + + + com.datastax.cassandra + cassandra-driver-mapping + + + com.datastax.cassandra + cassandra-driver-extras + io.takari.junit takari-cpsuite test - - + + com.google.guava guava @@ -215,18 +215,18 @@ - - org.apache.maven.plugins - maven-jar-plugin + + org.apache.maven.plugins + maven-jar-plugin ${jar-plugin.version} - - - - test-jar - - - - + + + + test-jar + + + + 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 05969874ed..7f04ddf2aa 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 @@ -17,7 +17,6 @@ package org.thingsboard.server.dao.service; import com.fasterxml.jackson.databind.JsonNode; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.validator.routines.EmailValidator; import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.exception.DataValidationException; @@ -26,11 +25,13 @@ import java.util.HashSet; import java.util.Iterator; import java.util.Set; import java.util.function.Function; +import java.util.regex.Matcher; +import java.util.regex.Pattern; @Slf4j public abstract class DataValidator> { - - private static EmailValidator emailValidator = EmailValidator.getInstance(); + private static final Pattern EMAIL_PATTERN = + Pattern.compile("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,6}$", Pattern.CASE_INSENSITIVE); public void validate(D data, Function tenantIdFunction) { try { @@ -64,11 +65,20 @@ public abstract class DataValidator> { } protected static void validateEmail(String email) { - if (!emailValidator.isValid(email)) { + if (!doValidateEmail(email)) { throw new DataValidationException("Invalid email address format '" + email + "'!"); } } + private static boolean doValidateEmail(String email) { + if (email == null) { + return false; + } + + Matcher emailMatcher = EMAIL_PATTERN.matcher(email); + return emailMatcher.matches(); + } + protected static void validateJsonStructure(JsonNode expectedNode, JsonNode actualNode) { Set expectedFields = new HashSet<>(); Iterator fieldsIterator = expectedNode.fieldNames(); diff --git a/pom.xml b/pom.xml index 377c52fb66..4361423b0b 100755 --- a/pom.xml +++ b/pom.xml @@ -55,7 +55,6 @@ 28.2-jre 2.6.1 3.4 - 1.6 2.5 1.4 2.10.2 @@ -1075,11 +1074,6 @@ commons-lang3 ${commons-lang3.version} - - commons-validator - commons-validator - ${commons-validator.version} - commons-io commons-io From ebed307757328d06de36e9d55fe0c4aefb71a153 Mon Sep 17 00:00:00 2001 From: Yevhen Bondarenko <56396344+YevhenBondarenko@users.noreply.github.com> Date: Tue, 16 Jun 2020 10:13:22 +0300 Subject: [PATCH 46/52] Added proxy configs to rest api call rule node (#2943) * Added proxy configs to rest api call rule node * added validation proxyHost and proxyPort * refactored checkProxyPort * TbHttpClient improvements --- .../rule/engine/rest/TbHttpClient.java | 49 ++++++++++++++++++- .../rest/TbRestApiCallNodeConfiguration.java | 8 +++ .../static/rulenode/rulenode-core-config.js | 2 +- 3 files changed, 56 insertions(+), 3 deletions(-) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java index ba860c103b..8948d30081 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java @@ -20,11 +20,20 @@ import io.netty.channel.nio.NioEventLoopGroup; import io.netty.handler.ssl.SslContextBuilder; import lombok.Data; import lombok.extern.slf4j.Slf4j; +import org.apache.http.HttpHost; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.CredentialsProvider; +import org.apache.http.conn.ssl.DefaultHostnameVerifier; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; +import org.springframework.http.client.HttpComponentsAsyncClientHttpRequestFactory; import org.springframework.http.client.Netty4ClientHttpRequestFactory; +import org.springframework.util.StringUtils; import org.springframework.util.concurrent.ListenableFuture; import org.springframework.util.concurrent.ListenableFutureCallback; import org.springframework.web.client.AsyncRestTemplate; @@ -36,7 +45,9 @@ import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; +import javax.net.ssl.SSLContext; import javax.net.ssl.SSLException; +import java.security.NoSuchAlgorithmException; import java.util.Deque; import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.TimeUnit; @@ -63,7 +74,30 @@ class TbHttpClient { if (config.getMaxParallelRequestsCount() > 0) { pendingFutures = new ConcurrentLinkedDeque<>(); } - if (config.isUseSimpleClientHttpFactory()) { + + if (config.isEnableProxy()) { + checkProxyHost(config.getProxyHost()); + checkProxyPort(config.getProxyPort()); + + HttpAsyncClientBuilder httpAsyncClientBuilder = HttpAsyncClientBuilder.create() + .setSSLHostnameVerifier(new DefaultHostnameVerifier()) + .setSSLContext(SSLContext.getDefault()) + .setProxy(new HttpHost(config.getProxyHost(), config.getProxyPort())); + + if (!StringUtils.isEmpty(config.getProxyUser()) && !StringUtils.isEmpty(config.getProxyPassword())) { + CredentialsProvider credsProvider = new BasicCredentialsProvider(); + credsProvider.setCredentials( + new AuthScope(config.getProxyHost(), config.getProxyPort()), + new UsernamePasswordCredentials(config.getProxyUser(), config.getProxyPassword()) + ); + httpAsyncClientBuilder.setDefaultCredentialsProvider(credsProvider); + } + + HttpComponentsAsyncClientHttpRequestFactory requestFactory = new HttpComponentsAsyncClientHttpRequestFactory(); + requestFactory.setAsyncClient(httpAsyncClientBuilder.build()); + requestFactory.setReadTimeout(config.getReadTimeoutMs()); + httpClient = new AsyncRestTemplate(requestFactory); + } else if (config.isUseSimpleClientHttpFactory()) { httpClient = new AsyncRestTemplate(); } else { this.eventLoopGroup = new NioEventLoopGroup(); @@ -72,7 +106,7 @@ class TbHttpClient { nettyFactory.setReadTimeout(config.getReadTimeoutMs()); httpClient = new AsyncRestTemplate(nettyFactory); } - } catch (SSLException e) { + } catch (SSLException | NoSuchAlgorithmException e) { throw new TbNodeException(e); } } @@ -169,4 +203,15 @@ class TbHttpClient { } } + private static void checkProxyHost(String proxyHost) throws TbNodeException { + if (StringUtils.isEmpty(proxyHost)) { + throw new TbNodeException("Proxy host can't be empty"); + } + } + + private static void checkProxyPort(int proxyPort) throws TbNodeException { + if (proxyPort < 0 || proxyPort > 65535) { + throw new TbNodeException("Proxy port out of range:" + proxyPort); + } + } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeConfiguration.java index b361c5ed8e..6e9a9af500 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeConfiguration.java @@ -18,6 +18,7 @@ package org.thingsboard.rule.engine.rest; import lombok.Data; import org.thingsboard.rule.engine.api.NodeConfiguration; +import java.net.Proxy; import java.util.Collections; import java.util.Map; @@ -33,6 +34,12 @@ public class TbRestApiCallNodeConfiguration implements NodeConfiguration
tb.rulenode.customer-name-pattern-required
tb.rulenode.customer-name-pattern-hint
{{ 'tb.rulenode.create-customer-if-not-exists' | translate }}
tb.rulenode.customer-cache-expiration-required
tb.rulenode.customer-cache-expiration-range
tb.rulenode.customer-cache-expiration-hint
"},function(e,t){e.exports='
{{scope.name | translate}}
'},function(e,t){e.exports="
tb.rulenode.select-queue-hint
"},function(e,t){e.exports="
{{ 'tb.rulenode.test-details-function' | translate }}
tb.rulenode.alarm-type-required
tb.rulenode.entity-type-pattern-hint
"},function(e,t){e.exports="
{{ 'tb.rulenode.test-details-function' | translate }}
{{ 'tb.rulenode.use-message-alarm-data' | translate }}
tb.rulenode.alarm-type-required
tb.rulenode.entity-type-pattern-hint
{{ severity.name | translate}}
tb.rulenode.alarm-severity-required
{{ 'tb.rulenode.propagate' | translate }}
tb.rulenode.relation-types-list-hint
"},function(e,t){e.exports="
{{ ('relation.search-direction.' + direction) | translate}}
tb.rulenode.entity-name-pattern-required
tb.rulenode.entity-name-pattern-hint
tb.rulenode.entity-type-pattern-required
tb.rulenode.entity-type-pattern-hint
tb.rulenode.relation-type-pattern-required
tb.rulenode.relation-type-pattern-hint
{{ 'tb.rulenode.create-entity-if-not-exists' | translate }}
tb.rulenode.create-entity-if-not-exists-hint
{{ 'tb.rulenode.remove-current-relations' | translate }}
tb.rulenode.remove-current-relations-hint
{{ 'tb.rulenode.change-originator-to-related-entity' | translate }}
tb.rulenode.change-originator-to-related-entity-hint
tb.rulenode.entity-cache-expiration-required
tb.rulenode.entity-cache-expiration-range
tb.rulenode.entity-cache-expiration-hint
"},function(e,t){e.exports="
{{ 'tb.rulenode.delete-relation-to-specific-entity' | translate }}
tb.rulenode.delete-relation-hint
{{ ('relation.search-direction.' + direction) | translate}}
tb.rulenode.entity-name-pattern-required
tb.rulenode.entity-name-pattern-hint
tb.rulenode.relation-type-pattern-required
tb.rulenode.relation-type-pattern-hint
tb.rulenode.entity-cache-expiration-required
tb.rulenode.entity-cache-expiration-range
tb.rulenode.entity-cache-expiration-hint
"},function(e,t){e.exports="
tb.rulenode.message-count-required
tb.rulenode.min-message-count-message
tb.rulenode.period-seconds-required
tb.rulenode.min-period-seconds-message
{{ 'tb.rulenode.test-generator-function' | translate }}
"},function(e,t){e.exports='
tb.rulenode.latitude-key-name-required
tb.rulenode.longitude-key-name-required
{{ \'tb.rulenode.fetch-perimeter-info-from-message-metadata\' | translate }}
{{ type.name | translate}}
tb.rulenode.circle-center-latitude-required
tb.rulenode.circle-center-longitude-required
tb.rulenode.range-required
{{ type.name | translate}}
tb.rulenode.polygon-definition-required
tb.rulenode.polygon-definition-hint
tb.rulenode.min-inside-duration-value-required
tb.rulenode.time-value-range
tb.rulenode.time-value-range
{{timeUnit.name | translate}}
tb.rulenode.min-outside-duration-value-required
tb.rulenode.time-value-range
tb.rulenode.time-value-range
{{timeUnit.name | translate}}
'},function(e,t){e.exports='
tb.rulenode.topic-pattern-required
tb.rulenode.bootstrap-servers-required
tb.rulenode.min-retries-message
tb.rulenode.min-batch-size-bytes-message
tb.rulenode.min-linger-ms-message
tb.rulenode.min-buffer-memory-bytes-message
{{ ackValue }}
tb.rulenode.key-serializer-required
tb.rulenode.value-serializer-required
{{ \'tb.rulenode.add-metadata-key-values-as-kafka-headers\' | translate }}
tb.rulenode.add-metadata-key-values-as-kafka-headers-hint
{{charset.name | translate}}
'},function(e,t){e.exports="
{{ 'tb.rulenode.test-to-string-function' | translate }}
"},function(e,t){e.exports='
tb.rulenode.topic-pattern-required
tb.rulenode.mqtt-topic-pattern-hint
tb.rulenode.host-required
tb.rulenode.port-required
tb.rulenode.port-range
tb.rulenode.port-range
tb.rulenode.connect-timeout-required
tb.rulenode.connect-timeout-range
tb.rulenode.connect-timeout-range
{{ \'tb.rulenode.clean-session\' | translate }} {{ \'tb.rulenode.enable-ssl\' | translate }}
{{ \'tb.rulenode.credentials\' | translate }}
{{ ruleNodeTypes.mqttCredentialTypes[configuration.credentials.type].name | translate }}
{{ \'tb.rulenode.credentials\' | translate }}
{{ ruleNodeTypes.mqttCredentialTypes[configuration.credentials.type].name | translate }}
{{credentialsValue.name | translate}}
tb.rulenode.credentials-type-required
tb.rulenode.username-required
tb.rulenode.password-required
'; -},function(e,t){e.exports="
tb.rulenode.interval-seconds-required
tb.rulenode.min-interval-seconds-message
tb.rulenode.output-timeseries-key-prefix-required
"},function(e,t){e.exports='
{{ \'tb.rulenode.use-metadata-period-in-seconds-patterns\' | translate }}
tb.rulenode.use-metadata-period-in-seconds-patterns-hint
tb.rulenode.period-seconds-required
tb.rulenode.min-period-0-seconds-message
tb.rulenode.period-in-seconds-pattern-required
tb.rulenode.period-in-seconds-pattern-hint
tb.rulenode.max-pending-messages-required
tb.rulenode.max-pending-messages-range
tb.rulenode.max-pending-messages-range
'},function(e,t){e.exports="
tb.rulenode.gcp-project-id-required
tb.rulenode.pubsub-topic-name-required
{{ 'action.remove' | translate }} close
tb.rulenode.message-attributes-hint
"},function(e,t){e.exports='
{{ property }}
tb.rulenode.host-required
tb.rulenode.port-required
tb.rulenode.port-range
tb.rulenode.port-range
{{ \'tb.rulenode.automatic-recovery\' | translate }}
tb.rulenode.min-connection-timeout-ms-message
tb.rulenode.min-handshake-timeout-ms-message
'},function(e,t){e.exports='
tb.rulenode.endpoint-url-pattern-required
tb.rulenode.endpoint-url-pattern-hint
{{ type }} {{ \'tb.rulenode.use-simple-client-http-factory\' | translate }}
tb.rulenode.read-timeout-hint
tb.rulenode.max-parallel-requests-count-hint
tb.rulenode.headers-hint
{{ \'tb.rulenode.use-redis-queue\' | translate }}
{{ \'tb.rulenode.trim-redis-queue\' | translate }}
'},function(e,t){e.exports="
"},function(e,t){e.exports="
tb.rulenode.timeout-required
tb.rulenode.min-timeout-message
"},function(e,t){e.exports='
tb.rulenode.custom-table-name-required
tb.rulenode.custom-table-hint
'},function(e,t){e.exports='
{{ \'tb.rulenode.use-system-smtp-settings\' | translate }}
{{smtpProtocol.toUpperCase()}}
tb.rulenode.smtp-host-required
tb.rulenode.smtp-port-required
tb.rulenode.smtp-port-range
tb.rulenode.smtp-port-range
tb.rulenode.timeout-required
tb.rulenode.min-timeout-msec-message
{{ \'tb.rulenode.enable-tls\' | translate }} {{tlsVersion}} {{ \'tb.rulenode.enable-proxy\' | translate }}
tb.rulenode.proxy-host-required
tb.rulenode.proxy-port-required
tb.rulenode.proxy-port-range
tb.rulenode.proxy-port-range
'},function(e,t){e.exports="
tb.rulenode.topic-arn-pattern-required
tb.rulenode.topic-arn-pattern-hint
tb.rulenode.aws-access-key-id-required
tb.rulenode.aws-secret-access-key-required
tb.rulenode.aws-region-required
"},function(e,t){e.exports='
{{ type.name | translate }}
tb.rulenode.queue-url-pattern-required
tb.rulenode.queue-url-pattern-hint
tb.rulenode.min-delay-seconds-message
tb.rulenode.max-delay-seconds-message
tb.rulenode.message-attributes-hint
tb.rulenode.aws-access-key-id-required
tb.rulenode.aws-secret-access-key-required
tb.rulenode.aws-region-required
'},function(e,t){e.exports="
tb.rulenode.default-ttl-required
tb.rulenode.min-default-ttl-message
"},function(e,t){e.exports="
tb.rulenode.customer-name-pattern-required
tb.rulenode.customer-name-pattern-hint
tb.rulenode.customer-cache-expiration-required
tb.rulenode.customer-cache-expiration-range
tb.rulenode.customer-cache-expiration-hint
"},function(e,t){e.exports="
{{ 'alias.last-level-relation' | translate}}
{{ ('relation.search-direction.' + direction) | translate}}
relation.relation-type
device.device-types
"},function(e,t){e.exports="
{{ 'tb.rulenode.latest-telemetry' | translate }}
"},function(e,t){e.exports='
{{ \'tb.rulenode.tell-failure-if-absent\' | translate }}
tb.rulenode.tell-failure-if-absent-hint
{{ \'tb.rulenode.get-latest-value-with-ts\' | translate }}
tb.rulenode.get-latest-value-with-ts-hint
'},function(e,t){e.exports='
{{\'tb.rulenode.entity-details-\'+item.toLowerCase() | translate}} tb.rulenode.no-entity-details-matching {{\'tb.rulenode.entity-details-\'+$chip.toLowerCase() | translate}} {{ \'tb.rulenode.add-to-metadata\' | translate }}
tb.rulenode.add-to-metadata-hint
'},function(e,t){e.exports='
{{ type }}
tb.rulenode.fetch-mode-hint
{{ type }}
tb.rulenode.order-by-hint
tb.rulenode.limit-hint
{{ \'tb.rulenode.use-metadata-interval-patterns\' | translate }}
tb.rulenode.use-metadata-interval-patterns-hint
tb.rulenode.start-interval-value-required
tb.rulenode.time-value-range
tb.rulenode.time-value-range
{{timeUnit.name | translate}}
tb.rulenode.end-interval-value-required
tb.rulenode.time-value-range
tb.rulenode.time-value-range
{{timeUnit.name | translate}}
tb.rulenode.start-interval-pattern-required
tb.rulenode.start-interval-pattern-hint
tb.rulenode.end-interval-pattern-required
tb.rulenode.end-interval-pattern-hint
'; +},function(e,t){e.exports="
tb.rulenode.interval-seconds-required
tb.rulenode.min-interval-seconds-message
tb.rulenode.output-timeseries-key-prefix-required
"},function(e,t){e.exports='
{{ \'tb.rulenode.use-metadata-period-in-seconds-patterns\' | translate }}
tb.rulenode.use-metadata-period-in-seconds-patterns-hint
tb.rulenode.period-seconds-required
tb.rulenode.min-period-0-seconds-message
tb.rulenode.period-in-seconds-pattern-required
tb.rulenode.period-in-seconds-pattern-hint
tb.rulenode.max-pending-messages-required
tb.rulenode.max-pending-messages-range
tb.rulenode.max-pending-messages-range
'},function(e,t){e.exports="
tb.rulenode.gcp-project-id-required
tb.rulenode.pubsub-topic-name-required
{{ 'action.remove' | translate }} close
tb.rulenode.message-attributes-hint
"},function(e,t){e.exports='
{{ property }}
tb.rulenode.host-required
tb.rulenode.port-required
tb.rulenode.port-range
tb.rulenode.port-range
{{ \'tb.rulenode.automatic-recovery\' | translate }}
tb.rulenode.min-connection-timeout-ms-message
tb.rulenode.min-handshake-timeout-ms-message
'},function(e,t){e.exports='
tb.rulenode.endpoint-url-pattern-required
tb.rulenode.endpoint-url-pattern-hint
{{ type }} {{ \'tb.rulenode.enable-proxy\' | translate }} {{ \'tb.rulenode.use-simple-client-http-factory\' | translate }}
tb.rulenode.proxy-host-required
tb.rulenode.proxy-port-required
tb.rulenode.proxy-port-range
tb.rulenode.proxy-port-range
tb.rulenode.read-timeout-hint
tb.rulenode.max-parallel-requests-count-hint
tb.rulenode.headers-hint
{{ \'tb.rulenode.use-redis-queue\' | translate }}
{{ \'tb.rulenode.trim-redis-queue\' | translate }}
'},function(e,t){e.exports="
"},function(e,t){e.exports="
tb.rulenode.timeout-required
tb.rulenode.min-timeout-message
"},function(e,t){e.exports='
tb.rulenode.custom-table-name-required
tb.rulenode.custom-table-hint
'},function(e,t){e.exports='
{{ \'tb.rulenode.use-system-smtp-settings\' | translate }}
{{smtpProtocol.toUpperCase()}}
tb.rulenode.smtp-host-required
tb.rulenode.smtp-port-required
tb.rulenode.smtp-port-range
tb.rulenode.smtp-port-range
tb.rulenode.timeout-required
tb.rulenode.min-timeout-msec-message
{{ \'tb.rulenode.enable-tls\' | translate }} {{tlsVersion}} {{ \'tb.rulenode.enable-proxy\' | translate }}
tb.rulenode.proxy-host-required
tb.rulenode.proxy-port-required
tb.rulenode.proxy-port-range
tb.rulenode.proxy-port-range
'},function(e,t){e.exports="
tb.rulenode.topic-arn-pattern-required
tb.rulenode.topic-arn-pattern-hint
tb.rulenode.aws-access-key-id-required
tb.rulenode.aws-secret-access-key-required
tb.rulenode.aws-region-required
"},function(e,t){e.exports='
{{ type.name | translate }}
tb.rulenode.queue-url-pattern-required
tb.rulenode.queue-url-pattern-hint
tb.rulenode.min-delay-seconds-message
tb.rulenode.max-delay-seconds-message
tb.rulenode.message-attributes-hint
tb.rulenode.aws-access-key-id-required
tb.rulenode.aws-secret-access-key-required
tb.rulenode.aws-region-required
'},function(e,t){e.exports="
tb.rulenode.default-ttl-required
tb.rulenode.min-default-ttl-message
"},function(e,t){e.exports="
tb.rulenode.customer-name-pattern-required
tb.rulenode.customer-name-pattern-hint
tb.rulenode.customer-cache-expiration-required
tb.rulenode.customer-cache-expiration-range
tb.rulenode.customer-cache-expiration-hint
"},function(e,t){e.exports="
{{ 'alias.last-level-relation' | translate}}
{{ ('relation.search-direction.' + direction) | translate}}
relation.relation-type
device.device-types
"},function(e,t){e.exports="
{{ 'tb.rulenode.latest-telemetry' | translate }}
"},function(e,t){e.exports='
{{ \'tb.rulenode.tell-failure-if-absent\' | translate }}
tb.rulenode.tell-failure-if-absent-hint
{{ \'tb.rulenode.get-latest-value-with-ts\' | translate }}
tb.rulenode.get-latest-value-with-ts-hint
'},function(e,t){e.exports='
{{\'tb.rulenode.entity-details-\'+item.toLowerCase() | translate}} tb.rulenode.no-entity-details-matching {{\'tb.rulenode.entity-details-\'+$chip.toLowerCase() | translate}} {{ \'tb.rulenode.add-to-metadata\' | translate }}
tb.rulenode.add-to-metadata-hint
'},function(e,t){e.exports='
{{ type }}
tb.rulenode.fetch-mode-hint
{{ type }}
tb.rulenode.order-by-hint
tb.rulenode.limit-hint
{{ \'tb.rulenode.use-metadata-interval-patterns\' | translate }}
tb.rulenode.use-metadata-interval-patterns-hint
tb.rulenode.start-interval-value-required
tb.rulenode.time-value-range
tb.rulenode.time-value-range
{{timeUnit.name | translate}}
tb.rulenode.end-interval-value-required
tb.rulenode.time-value-range
tb.rulenode.time-value-range
{{timeUnit.name | translate}}
tb.rulenode.start-interval-pattern-required
tb.rulenode.start-interval-pattern-hint
tb.rulenode.end-interval-pattern-required
tb.rulenode.end-interval-pattern-hint
'; },function(e,t){e.exports='
{{ \'tb.rulenode.tell-failure-if-absent\' | translate }}
tb.rulenode.tell-failure-if-absent-hint
{{ \'tb.rulenode.get-latest-value-with-ts\' | translate }}
tb.rulenode.get-latest-value-with-ts-hint
'},function(e,t){e.exports='
'},function(e,t){e.exports="
{{ 'tb.rulenode.latest-telemetry' | translate }}
"},32,function(e,t){e.exports="
{{'alarm.display-status.' + item | translate}} {{'alarm.display-status.' + $chip | translate}}
"},function(e,t){e.exports='
tb.rulenode.separator-hint
tb.rulenode.separator-hint
{{ \'tb.rulenode.check-all-keys\' | translate }}
tb.rulenode.check-all-keys-hint
'},function(e,t){e.exports="
{{ 'tb.rulenode.check-relation-to-specific-entity' | translate }}
tb.rulenode.check-relation-hint
{{ ('relation.search-direction.' + direction) | translate}}
"},function(e,t){e.exports='
tb.rulenode.latitude-key-name-required
tb.rulenode.longitude-key-name-required
{{ \'tb.rulenode.fetch-perimeter-info-from-message-metadata\' | translate }}
{{ type.name | translate}}
tb.rulenode.circle-center-latitude-required
tb.rulenode.circle-center-longitude-required
tb.rulenode.range-required
{{ type.name | translate}}
tb.rulenode.polygon-definition-required
tb.rulenode.polygon-definition-hint
'},function(e,t){e.exports='
{{item}}
tb.rulenode.no-message-types-found
tb.rulenode.no-message-type-matching tb.rulenode.create-new-message-type
{{$chip.name}}
'},function(e,t){e.exports='
'},function(e,t){e.exports="
{{ 'tb.rulenode.test-filter-function' | translate }}
"},function(e,t){e.exports="
{{ 'tb.rulenode.test-switch-function' | translate }}
"},function(e,t){e.exports='
{{ keyText }} {{ valText }}  
{{keyRequiredText}}
{{valRequiredText}}
{{ \'tb.key-val.remove-entry\' | translate }} close
{{ \'tb.key-val.add-entry\' | translate }} add {{ \'action.add\' | translate }}
'},function(e,t){e.exports="
{{ 'alias.last-level-relation' | translate}}
{{ ('relation.search-direction.' + direction) | translate}}
relation.relation-filters
"},function(e,t){e.exports='
{{ source.name | translate}}
'},function(e,t){e.exports="
{{ 'tb.rulenode.test-transformer-function' | translate }}
"},function(e,t){e.exports="
tb.rulenode.from-template-required
tb.rulenode.from-template-hint
tb.rulenode.to-template-required
tb.rulenode.mail-address-list-template-hint
tb.rulenode.mail-address-list-template-hint
tb.rulenode.mail-address-list-template-hint
tb.rulenode.subject-template-required
tb.rulenode.subject-template-hint
tb.rulenode.body-template-required
tb.rulenode.body-template-hint
"},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(6),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(7),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.serviceType="TB_RULE_ENGINE",n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(8),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t,n,a){var i=function(i,r,l,s){var d=o.default;r.html(d),i.types=n,i.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(i.configuration)}),s.$render=function(){i.configuration=s.$viewValue},i.testDetailsBuildJs=function(e){var n=angular.copy(i.configuration.alarmDetailsBuildJs);a.testNodeScript(e,n,"json",t.instant("tb.rulenode.details")+"","Details",["msg","metadata","msgType"],i.ruleNodeId).then(function(e){i.configuration.alarmDetailsBuildJs=e,s.$setDirty()})},e(r.contents())(i)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:i}}i.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(9),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t,n,a){var i=function(i,r,l,s){var d=o.default;r.html(d),i.types=n,i.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(i.configuration)}),s.$render=function(){i.configuration=s.$viewValue,i.configuration.hasOwnProperty("relationTypes")||(i.configuration.relationTypes=[])},i.testDetailsBuildJs=function(e){var n=angular.copy(i.configuration.alarmDetailsBuildJs);a.testNodeScript(e,n,"json",t.instant("tb.rulenode.details")+"","Details",["msg","metadata","msgType"],i.ruleNodeId).then(function(e){i.configuration.alarmDetailsBuildJs=e,s.$setDirty()})},e(r.contents())(i)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:i}}i.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(10),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(11),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(12),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t,n,a){var i=function(i,r,l,s){var d=o.default;r.html(d),i.types=n,i.originator=null,i.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(i.configuration)}),s.$render=function(){i.configuration=s.$viewValue,i.configuration.originatorId&&i.configuration.originatorType?i.originator={id:i.configuration.originatorId,entityType:i.configuration.originatorType}:i.originator=null,i.$watch("originator",function(e,t){angular.equals(e,t)||(i.originator?(s.$viewValue.originatorId=i.originator.id,s.$viewValue.originatorType=i.originator.entityType):(s.$viewValue.originatorId=null,s.$viewValue.originatorType=null))},!0)},i.testScript=function(e){var n=angular.copy(i.configuration.jsScript);a.testNodeScript(e,n,"generate",t.instant("tb.rulenode.generator")+"","Generate",["prevMsg","prevMetadata","prevMsgType"],i.ruleNodeId).then(function(e){i.configuration.jsScript=e,s.$setDirty()})},e(r.contents())(i)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:i}}i.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i,n(1);var r=n(13),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}i.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(14),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var i=n(77),r=a(i),o=n(54),l=a(o),s=n(60),d=a(s),u=n(57),c=a(u),m=n(56),g=a(m),p=n(64),f=a(p),b=n(71),v=a(b),y=n(72),h=a(y),q=n(70),x=a(q),k=n(63),$=a(k),T=n(75),C=a(T),w=n(76),M=a(w),N=n(69),S=a(N),_=n(65),P=a(_),F=n(74),E=a(F),A=n(67),V=a(A),I=n(66),j=a(I),O=n(53),D=a(O),L=n(78),R=a(L),K=n(59),U=a(K),z=n(58),H=a(z),B=n(73),G=a(B),Y=n(61),Q=a(Y),W=n(68),J=a(W),Z=n(55),X=a(Z);t.default=angular.module("thingsboard.ruleChain.config.action",[]).directive("tbActionNodeTimeseriesConfig",r.default).directive("tbActionNodeAttributesConfig",l.default).directive("tbActionNodeGeneratorConfig",d.default).directive("tbActionNodeCreateAlarmConfig",c.default).directive("tbActionNodeClearAlarmConfig",g.default).directive("tbActionNodeLogConfig",f.default).directive("tbActionNodeRpcReplyConfig",v.default).directive("tbActionNodeRpcRequestConfig",h.default).directive("tbActionNodeRestApiCallConfig",x.default).directive("tbActionNodeKafkaConfig",$.default).directive("tbActionNodeSnsConfig",C.default).directive("tbActionNodeSqsConfig",M.default).directive("tbActionNodeRabbitMqConfig",S.default).directive("tbActionNodeMqttConfig",P.default).directive("tbActionNodeSendEmailConfig",E.default).directive("tbActionNodeMsgDelayConfig",V.default).directive("tbActionNodeMsgCountConfig",j.default).directive("tbActionNodeAssignToCustomerConfig",D.default).directive("tbActionNodeUnAssignToCustomerConfig",R.default).directive("tbActionNodeDeleteRelationConfig",U.default).directive("tbActionNodeCreateRelationConfig",H.default).directive("tbActionNodeCustomTableConfig",G.default).directive("tbActionNodeGpsGeofencingConfig",Q.default).directive("tbActionNodePubSubConfig",J.default).directive("tbActionNodeCheckPointConfig",X.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.ackValues=["all","-1","0","1"],n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue,n.configuration.hasOwnProperty("kafkaHeadersCharset")||(n.configuration.kafkaHeadersCharset="UTF-8")},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(15),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t,n){var a=function(a,i,r,l){var s=o.default;i.html(s),a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},a.testScript=function(e){var i=angular.copy(a.configuration.jsScript);n.testNodeScript(e,i,"string",t.instant("tb.rulenode.to-string")+"","ToString",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,l.$setDirty()})},e(i.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}i.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(16),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t,n){var a=function(a,i,r,l){var s=o.default;i.html(s),a.$mdExpansionPanel=t,a.ruleNodeTypes=n,a.credentialsTypeChanged=function(){var e=a.configuration.credentials.type;a.configuration.credentials={},a.configuration.credentials.type=e,a.updateValidity()},a.certFileAdded=function(e,t){var n=new FileReader;n.onload=function(n){a.$apply(function(){if(n.target.result){l.$setDirty();var i=n.target.result;i&&i.length>0&&("caCert"==t&&(a.configuration.credentials.caCertFileName=e.name,a.configuration.credentials.caCert=i),"privateKey"==t&&(a.configuration.credentials.privateKeyFileName=e.name,a.configuration.credentials.privateKey=i),"Cert"==t&&(a.configuration.credentials.certFileName=e.name,a.configuration.credentials.cert=i)),a.updateValidity()}})},n.readAsText(e.file)},a.clearCertFile=function(e){l.$setDirty(),"caCert"==e&&(a.configuration.credentials.caCertFileName=null,a.configuration.credentials.caCert=null),"privateKey"==e&&(a.configuration.credentials.privateKeyFileName=null,a.configuration.credentials.privateKey=null),"Cert"==e&&(a.configuration.credentials.certFileName=null,a.configuration.credentials.cert=null),a.updateValidity()},a.updateValidity=function(){var e=!0,t=a.configuration.credentials;t.type==n.mqttCredentialTypes["cert.PEM"].value&&(t.caCert&&t.cert&&t.privateKey||(e=!1)),l.$setValidity("Certs",e)},a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},e(i.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:a}}i.$inject=["$compile","$mdExpansionPanel","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i,n(2);var r=n(17),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(18),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){ var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(19),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.serviceAccountFileAdded=function(e){var t=new FileReader;t.onload=function(t){n.$apply(function(){if(t.target.result){r.$setDirty();var a=t.target.result;a&&a.length>0&&(n.configuration.serviceAccountKeyFileName=e.name,n.configuration.serviceAccountKey=a),n.updateValidity()}})},t.readAsText(e.file)},n.clearServiceAccountFile=function(){r.$setDirty(),n.configuration.serviceAccountKeyFileName=null,n.configuration.serviceAccountKey=null,n.updateValidity()},n.updateValidity=function(){var e=!0,t=n.configuration;t.serviceAccountKeyFileName&&t.serviceAccountKey||(e=!1),r.$setValidity("SAKey",e)},n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}i.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(20),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.messageProperties=[null,"BASIC","TEXT_PLAIN","MINIMAL_BASIC","MINIMAL_PERSISTENT_BASIC","PERSISTENT_BASIC","PERSISTENT_TEXT_PLAIN"],t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(21),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}i.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(22),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(23),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(24),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(25),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.smtpProtocols=["smtp","smtps"],t.tlsVersions=["TLSv1","TLSv1.1","TLSv1.2","TLSv1.3"],t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(26),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(27),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}i.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(28),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(29),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(30),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||r.$setViewValue(n.query)}),r.$render=function(){n.query=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(31),o=a(r)},function(e,t){"use strict";function n(e){var t=function(t,n,a,i){n.html("
"),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}n.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(32),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(33),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),n.entityDetailsList=[];for(var s in t.entityDetails){var d=s;n.entityDetailsList.push(d)}r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(34),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t,n){var a=function(a,i,r,l){var s=o.default;i.html(s);var d=186;a.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,d],a.ruleNodeTypes=n,a.aggPeriodTimeUnits={},a.aggPeriodTimeUnits.MINUTES=n.timeUnit.MINUTES,a.aggPeriodTimeUnits.HOURS=n.timeUnit.HOURS,a.aggPeriodTimeUnits.DAYS=n.timeUnit.DAYS,a.aggPeriodTimeUnits.MILLISECONDS=n.timeUnit.MILLISECONDS,a.aggPeriodTimeUnits.SECONDS=n.timeUnit.SECONDS,a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},e(i.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{},link:a}}i.$inject=["$compile","$mdConstant","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(35),o=a(r);n(3)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var i=n(86),r=a(i),o=n(87),l=a(o),s=n(82),d=a(s),u=n(88),c=a(u),m=n(81),g=a(m),p=n(89),f=a(p),b=n(84),v=a(b),y=n(83),h=a(y);t.default=angular.module("thingsboard.ruleChain.config.enrichment",[]).directive("tbEnrichmentNodeOriginatorAttributesConfig",r.default).directive("tbEnrichmentNodeOriginatorFieldsConfig",l.default).directive("tbEnrichmentNodeDeviceAttributesConfig",d.default).directive("tbEnrichmentNodeRelatedAttributesConfig",c.default).directive("tbEnrichmentNodeCustomerAttributesConfig",g.default).directive("tbEnrichmentNodeTenantAttributesConfig",f.default).directive("tbEnrichmentNodeGetTelemetryFromDatabase",v.default).directive("tbEnrichmentNodeEntityDetailsConfig",h.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(36),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(37),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(38),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(39),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),n.alarmStatusList=[];for(var s in t.alarmStatus)n.alarmStatusList.push(t.alarmStatus[s]);r.$render=function(){n.configuration=r.$viewValue},n.getAlarmStatusList=function(){return n.alarmStatusList.filter(function(e){return n.configuration.alarmStatusList.indexOf(e)===-1})},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{required:"=ngRequired",readonly:"=ngReadonly"},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(40),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(41),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(42),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}i.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(43),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var i=n(97),r=a(i),o=n(95),l=a(o),s=n(98),d=a(s),u=n(92),c=a(u),m=n(96),g=a(m),p=n(91),f=a(p),b=n(93),v=a(b),y=n(90),h=a(y);t.default=angular.module("thingsboard.ruleChain.config.filter",[]).directive("tbFilterNodeScriptConfig",r.default).directive("tbFilterNodeMessageTypeConfig",l.default).directive("tbFilterNodeSwitchConfig",d.default).directive("tbFilterNodeCheckRelationConfig",c.default).directive("tbFilterNodeOriginatorTypeConfig",g.default).directive("tbFilterNodeCheckMessageConfig",f.default).directive("tbFilterNodeGpsGeofencingConfig",v.default).directive("tbFilterNodeCheckAlarmStatusConfig",h.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t,n){var a=function(a,i,r,l){function s(){if(l.$viewValue){for(var e=[],t=0;t-1&&t.kvList.splice(e,1)}function l(){t.kvList||(t.kvList=[]),t.kvList.push({key:"",value:""})}function s(){var e={};t.kvList.forEach(function(t){t.key&&(e[t.key]=t.value)}),i.$setViewValue(e),d()}function d(){var e=!0;t.required&&!t.kvList.length&&(e=!1),i.$setValidity("kvMap",e)}var u=o.default;n.html(u),t.ngModelCtrl=i,t.removeKeyVal=r,t.addKeyVal=l,t.kvList=[],t.$watch("query",function(e,n){angular.equals(e,n)||i.$setViewValue(t.query)}),i.$render=function(){if(i.$viewValue){var e=i.$viewValue;t.kvList.length=0;for(var n in e)t.kvList.push({key:n,value:e[n]})}t.$watch("kvList",function(e,t){angular.equals(e,t)||s()},!0),d()},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{required:"=ngRequired",disabled:"=ngDisabled",requiredText:"=",keyText:"=",keyRequiredText:"=",valText:"=",valRequiredText:"="},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(48),o=a(r);n(5)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||r.$setViewValue(n.query)}),r.$render=function(){n.query=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(49),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(50),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var i=n(101),r=a(i),o=n(103),l=a(o),s=n(104),d=a(s);t.default=angular.module("thingsboard.ruleChain.config.transform",[]).directive("tbTransformationNodeChangeOriginatorConfig",r.default).directive("tbTransformationNodeScriptConfig",l.default).directive("tbTransformationNodeToEmailConfig",d.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t,n){var a=function(a,i,r,l){var s=o.default;i.html(s),a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},a.testScript=function(e){var i=angular.copy(a.configuration.jsScript);n.testNodeScript(e,i,"update",t.instant("tb.rulenode.transformer")+"","Transform",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,l.$setDirty()})},e(i.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}i.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(51),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(52),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var i=n(108),r=a(i),o=n(94),l=a(o),s=n(85),d=a(s),u=n(102),c=a(u),m=n(62),g=a(m),p=n(80),f=a(p),b=n(100),v=a(b),y=n(79),h=a(y),q=n(99),x=a(q),k=n(107),$=a(k);t.default=angular.module("thingsboard.ruleChain.config",[r.default,l.default,d.default,c.default,g.default]).directive("tbNodeEmptyConfig",f.default).directive("tbRelationsQueryConfig",v.default).directive("tbDeviceRelationsQueryConfig",h.default).directive("tbKvMapConfig",x.default).config($.default).name},function(e,t){"use strict";function n(e){var t={tb:{rulenode:{"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-name-pattern-hint":"Name pattern, use ${metaKeyName} to substitute variables from metadata","entity-type-pattern":"Type pattern","entity-type-pattern-required":"Type pattern is required","entity-type-pattern-hint":"Type pattern, use ${metaKeyName} to substitute variables from metadata","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-name-pattern-hint":"Customer name pattern, use ${metaKeyName} to substitute variables from metadata","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.","start-interval":"Start Interval","end-interval":"End Interval","start-interval-time-unit":"Start Interval Time Unit","end-interval-time-unit":"End Interval Time Unit","fetch-mode":"Fetch mode","fetch-mode-hint":"If selected fetch mode 'ALL' you able to choose telemetry sampling order.","order-by":"Order by","order-by-hint":"Select to choose telemetry sampling order.",limit:"Limit","limit-hint":"Min limit value is 2, max - 1000. In case you want to fetch a single entry, select fetch mode 'FIRST' or 'LAST'.","time-unit-milliseconds":"Milliseconds","time-unit-seconds":"Seconds","time-unit-minutes":"Minutes","time-unit-hours":"Hours","time-unit-days":"Days","time-value-range":"Time value should be in a range from 1 to 2147483647'.","start-interval-value-required":"Start interval value is required.","end-interval-value-required":"End interval value is required.",filter:"Filter",switch:"Switch","message-type":"Message type","message-type-required":"Message type is required.","message-types-filter":"Message types filter","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","latest-timeseries":"Latest timeseries","data-keys":"Message data","metadata-keys":"Message metadata","relations-query":"Relations query","device-relations-query":"Device relations query","max-relation-level":"Max relation level","relation-type-pattern":"Relation type pattern","relation-type-pattern-hint":"Relation type pattern, use ${metaKeyName} to substitute variables from metadata","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","attr-mapping":"Attributes mapping","source-attribute":"Source attribute","source-attribute-required":"Source attribute is required.","source-telemetry":"Source telemetry","source-telemetry-required":"Source telemetry is required.","target-attribute":"Target attribute","target-attribute-required":"Target attribute is required.","attr-mapping-required":"At least one attribute mapping should be specified.","fields-mapping":"Fields mapping","fields-mapping-required":"At least one field mapping should be specified.","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","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 metadata period in seconds pattern","use-metadata-period-in-seconds-patterns-hint":"If selected, rule node use period in seconds interval pattern from message metadata assuming that intervals are in the seconds.","period-in-seconds-pattern":"Period in seconds metadata pattern","period-in-seconds-pattern-required":"Period in seconds pattern is required","period-in-seconds-pattern-hint":"Period in seconds pattern, use ${metaKeyName} to substitute variables from metadata","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","alarm-type-required":"Alarm type is required.","alarm-severity":"Alarm severity","alarm-severity-required":"Alarm severity is required","alarm-statuses-filter":"Alarm statuses filter","alarm-statuses-required":"Alarm statuses is required",propagate:"Propagate",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","from-template-hint":"From address template, use ${metaKeyName} to substitute variables from metadata","to-template":"To Template","to-template-required":"To Template is required","mail-address-list-template-hint":"Comma separated address list, use ${metaKeyName} to substitute variables from metadata","cc-template":"Cc Template","bcc-template":"Bcc Template","subject-template":"Subject Template","subject-template-required":"Subject Template is required","subject-template-hint":"Mail subject template, use ${metaKeyName} to substitute variables from metadata","body-template":"Body Template","body-template-required":"Body Template is required","body-template-hint":"Mail body template, use ${metaKeyName} to substitute variables from metadata","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", "endpoint-url-pattern-hint":"HTTP URL address pattern, use ${metaKeyName} to substitute variables from metadata","request-method":"Request method","use-simple-client-http-factory":"Use simple client HTTP factory","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 ${metaKeyName} in header/value fields to substitute variables from metadata",header:"Header","header-required":"Header is required",value:"Value","value-required":"Value is required","topic-pattern":"Topic pattern","topic-pattern-required":"Topic pattern is required","mqtt-topic-pattern-hint":"MQTT topic pattern, use ${metaKeyName} to substitute variables from metadata","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","topic-arn-pattern-hint":"Topic ARN pattern, use ${metaKeyName} to substitute variables from metadata","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","queue-url-pattern-hint":"Queue URL pattern, use ${metaKeyName} to substitute variables from metadata","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 ${metaKeyName} in name/value fields to substitute variables from metadata","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","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","username-required":"Username is required.","password-required":"Password is required.","ca-cert":"CA certificate file *","private-key":"Private key file *",cert:"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-interval-patterns":"Use metadata interval patterns","use-metadata-interval-patterns-hint":"If selected, rule node use start and end interval patterns from message metadata assuming that intervals are in the milliseconds.","use-message-alarm-data":"Use message alarm data","check-all-keys":"Check that all selected keys 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-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-pattern":"Start interval pattern","end-interval-pattern":"End interval pattern","start-interval-pattern-required":"Start interval pattern is required","end-interval-pattern-required":"End interval pattern is required","start-interval-pattern-hint":"Start interval pattern, use ${metaKeyName} to substitute variables from metadata","end-interval-pattern-hint":"End interval pattern, use ${metaKeyName} to substitute variables from metadata","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","enable-proxy":"Enable proxy","proxy-host":"Proxy host","proxy-host-required":"Proxy host is required.","proxy-port":"Proxy port","proxy-port-required":"You must supply a proxy port.","proxy-port-range":"Proxy port should be in a range from 1 to 65535.","proxy-user":"Proxy user","proxy-password":"Proxy password","tls-version":"TLS version","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":'You should press "enter" to complete field input.',"entity-details":"Select entity details:","entity-details-title":"Title","entity-details-country":"Country","entity-details-state":"State","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","add-to-metadata":"Add selected details to message metadata","add-to-metadata-hint":"If selected, adds the selected details keys to the message metadata instead of message data.","entity-details-list-empty":"No entity details 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":"You should 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-key-name":"Latitude key name","longitude-key-name":"Longitude key name","latitude-key-name-required":"Latitude key name is required.","longitude-key-name-required":"Longitude key name is required.","fetch-perimeter-info-from-message-metadata":"Fetch perimeter information from message metadata","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:"Range","range-required":"Range is required.","polygon-definition":"Polygon definition","polygon-definition-required":"Polygon definition is required.","polygon-definition-hint":"Please, 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 Latest telemetry with Timestamp","get-latest-value-with-ts-hint":'If selected, latest telemetry values will be added to the outbound message metadata with timestamp, e.g: "temp": "{\\"ts\\":1574329385897,\\"value\\":42}"',"use-redis-queue":"Use redis queue for message persistence","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."},"key-val":{key:"Key",value:"Value","remove-entry":"Remove entry","add-entry":"Add entry"}}};e.translations("en_US",t)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){(0,o.default)(e)}i.$inject=["$translateProvider"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(106),o=a(r)},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=angular.module("thingsboard.ruleChain.config.types",[]).constant("ruleNodeTypes",{originatorSource:{CUSTOMER:{name:"tb.rulenode.originator-customer",value:"CUSTOMER"},TENANT:{name:"tb.rulenode.originator-tenant",value:"TENANT"},RELATED:{name:"tb.rulenode.originator-related",value:"RELATED"},ALARM_ORIGINATOR:{name:"tb.rulenode.originator-alarm-originator",value:"ALARM_ORIGINATOR"}},fetchModeType:["FIRST","LAST","ALL"],samplingOrder:["ASC","DESC"],httpRequestType:["GET","POST","PUT","DELETE"],entityDetails:{TITLE:{name:"tb.rulenode.entity-details-title",value:"TITLE"},COUNTRY:{name:"tb.rulenode.entity-details-country",value:"COUNTRY"},STATE:{name:"tb.rulenode.entity-details-state",value:"STATE"},ZIP:{name:"tb.rulenode.entity-details-zip",value:"ZIP"},ADDRESS:{name:"tb.rulenode.entity-details-address",value:"ADDRESS"},ADDRESS2:{name:"tb.rulenode.entity-details-address2",value:"ADDRESS2"},PHONE:{name:"tb.rulenode.entity-details-phone",value:"PHONE"},EMAIL:{name:"tb.rulenode.entity-details-email",value:"EMAIL"},ADDITIONAL_INFO:{name:"tb.rulenode.entity-details-additional_info",value:"ADDITIONAL_INFO"}},sqsQueueType:{STANDARD:{name:"tb.rulenode.sqs-queue-standard",value:"STANDARD"},FIFO:{name:"tb.rulenode.sqs-queue-fifo",value:"FIFO"}},perimeterType:{CIRCLE:{name:"tb.rulenode.perimeter-circle",value:"CIRCLE"},POLYGON:{name:"tb.rulenode.perimeter-polygon",value:"POLYGON"}},timeUnit:{MILLISECONDS:{value:"MILLISECONDS",name:"tb.rulenode.time-unit-milliseconds"},SECONDS:{value:"SECONDS",name:"tb.rulenode.time-unit-seconds"},MINUTES:{value:"MINUTES",name:"tb.rulenode.time-unit-minutes"},HOURS:{value:"HOURS",name:"tb.rulenode.time-unit-hours"},DAYS:{value:"DAYS",name:"tb.rulenode.time-unit-days"}},rangeUnit:{METER:{value:"METER",name:"tb.rulenode.range-unit-meter"},KILOMETER:{value:"KILOMETER",name:"tb.rulenode.range-unit-kilometer"},FOOT:{value:"FOOT",name:"tb.rulenode.range-unit-foot"},MILE:{value:"MILE",name:"tb.rulenode.range-unit-mile"},NAUTICAL_MILE:{value:"NAUTICAL_MILE",name:"tb.rulenode.range-unit-nautical-mile"}},mqttCredentialTypes:{anonymous:{value:"anonymous",name:"tb.rulenode.credentials-anonymous"},basic:{value:"basic",name:"tb.rulenode.credentials-basic"},"cert.PEM":{value:"cert.PEM",name:"tb.rulenode.credentials-pem"}},toBytesStandartCharsetTypes:{"US-ASCII":{value:"US-ASCII",name:"tb.rulenode.charset-us-ascii"},"ISO-8859-1":{value:"ISO-8859-1",name:"tb.rulenode.charset-iso-8859-1"},"UTF-8":{value:"UTF-8",name:"tb.rulenode.charset-utf-8"},"UTF-16BE":{value:"UTF-16BE",name:"tb.rulenode.charset-utf-16be"},"UTF-16LE":{value:"UTF-16LE",name:"tb.rulenode.charset-utf-16le"},"UTF-16":{value:"UTF-16",name:"tb.rulenode.charset-utf-16"}}}).name}])); From 306674511cd6462461db2b41d74f8291d99cd35e Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Mon, 15 Jun 2020 13:19:42 +0300 Subject: [PATCH 47/52] fix bug in DefaultTbQueueRequestTemplate when responses is empty tickTs did not update --- .../server/queue/common/DefaultTbQueueRequestTemplate.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java index f02ae63441..ae7760638f 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java @@ -91,8 +91,6 @@ public class DefaultTbQueueRequestTemplate responses = responseTemplate.poll(pollInterval); if (responses.size() > 0) { log.trace("Polling responses completed, consumer records count [{}]", responses.size()); - } else { - continue; } responses.forEach(response -> { byte[] requestIdHeader = response.getHeaders().get(REQUEST_ID_HEADER); From cd2908fb59ec4628429adae6684809d3f40d9de1 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Tue, 16 Jun 2020 16:49:27 +0300 Subject: [PATCH 48/52] Refactoring notification engine --- .../server/actors/ActorSystemContext.java | 6 + .../actors/ruleChain/DefaultTbContext.java | 6 + .../RuleChainActorMessageProcessor.java | 32 +-- .../ruleChain/RuleChainManagerActor.java | 4 +- .../server/actors/tenant/TenantActor.java | 2 +- .../server/controller/AlarmController.java | 4 + .../server/controller/AssetController.java | 9 + .../server/controller/BaseController.java | 60 ++++-- .../controller/DashboardController.java | 8 + .../server/controller/DeviceController.java | 12 ++ .../server/controller/EdgeController.java | 7 +- .../controller/EntityRelationController.java | 9 + .../controller/EntityViewController.java | 10 + .../controller/RuleChainController.java | 32 ++- .../AnnotationComponentDiscoveryService.java | 4 +- .../edge/DefaultEdgeNotificationService.java | 183 ++++++++++-------- .../service/edge/EdgeNotificationService.java | 18 +- .../service/edge/rpc/EdgeGrpcSession.java | 100 +++++----- .../install/SqlDatabaseUpgradeService.java | 2 +- .../server/dao/edge/EdgeEventService.java | 35 ++++ .../server/dao/edge/EdgeService.java | 2 +- .../server/common/data/DataConstants.java | 3 - .../server/common/data/audit/ActionType.java | 1 + .../server/common/data/edge/EdgeEvent.java | 50 +++++ ...ueueEntityType.java => EdgeEventType.java} | 2 +- .../server/common/data/id/EdgeEventId.java | 35 ++++ .../common/data/rule/RuleChainType.java | 2 +- common/edge-api/pom.xml | 4 + common/edge-api/src/main/proto/edge.proto | 9 +- common/queue/src/main/proto/queue.proto | 8 + .../server/dao/edge/BaseEdgeEventService.java | 85 ++++++++ .../server/dao/edge/EdgeEventDao.java | 51 +++++ .../server/dao/edge/EdgeServiceImpl.java | 2 +- .../server/dao/model/ModelConstants.java | 10 + .../dao/model/nosql/EdgeEventEntity.java | 135 +++++++++++++ .../server/dao/model/sql/EdgeEventEntity.java | 121 ++++++++++++ .../dao/model/type/EdgeEventTypeCodec.java | 27 +++ .../server/dao/rule/BaseRuleChainService.java | 6 +- .../dao/sql/edge/EdgeEventRepository.java | 26 +++ .../dao/sql/edge/JpaBaseEdgeEventDao.java | 112 +++++++++++ .../resources/sql/schema-entities-hsql.sql | 11 ++ .../main/resources/sql/schema-entities.sql | 12 ++ .../dao/service/AbstractServiceTest.java | 4 + .../dao/service/BaseEdgeEventServiceTest.java | 108 +++++++++++ ...ImplTest.java => BaseEdgeServiceTest.java} | 2 +- .../dao/service/BaseRuleChainServiceTest.java | 12 +- .../nosql/EdgeEventServiceNoSqlTest.java | 23 +++ .../service/nosql/EdgeServiceNoSqlTest.java | 4 +- .../service/sql/EdgeEventServiceSqlTest.java | 12 +- .../dao/service/sql/EdgeServiceSqlTest.java | 4 +- .../resources/sql/hsql/drop-all-tables.sql | 3 +- .../thingsboard/rule/engine/api/RuleNode.java | 2 +- .../rule/engine/api/TbContext.java | 3 + .../engine/action/TbAssignToCustomerNode.java | 2 +- .../rule/engine/action/TbClearAlarmNode.java | 2 +- .../TbCopyAttributesToEntityViewNode.java | 2 +- .../rule/engine/action/TbCreateAlarmNode.java | 2 +- .../engine/action/TbCreateRelationNode.java | 2 +- .../engine/action/TbDeleteRelationNode.java | 2 +- .../rule/engine/action/TbLogNode.java | 2 +- .../rule/engine/action/TbMsgCountNode.java | 2 +- .../action/TbUnassignFromCustomerNode.java | 2 +- .../rule/engine/aws/sns/TbSnsNode.java | 2 +- .../rule/engine/aws/sqs/TbSqsNode.java | 2 +- .../rule/engine/debug/TbMsgGeneratorNode.java | 2 +- .../rule/engine/delay/TbMsgDelayNode.java | 2 +- .../engine/edge/PushToEdgeNodeCallback.java | 41 ---- .../rule/engine/edge/TbMsgPushToEdgeNode.java | 97 ++++++++-- .../engine/filter/TbCheckMessageNode.java | 2 +- .../engine/filter/TbCheckRelationNode.java | 2 +- .../rule/engine/filter/TbJsFilterNode.java | 2 +- .../rule/engine/filter/TbJsSwitchNode.java | 2 +- .../engine/filter/TbMsgTypeFilterNode.java | 2 +- .../engine/filter/TbMsgTypeSwitchNode.java | 2 +- .../filter/TbOriginatorTypeFilterNode.java | 2 +- .../filter/TbOriginatorTypeSwitchNode.java | 2 +- .../rule/engine/gcp/pubsub/TbPubSubNode.java | 2 +- .../engine/geo/TbGpsGeofencingActionNode.java | 2 +- .../engine/geo/TbGpsGeofencingFilterNode.java | 2 +- .../rule/engine/kafka/TbKafkaNode.java | 2 +- .../rule/engine/mail/TbMsgToEmailNode.java | 2 +- .../rule/engine/mail/TbSendEmailNode.java | 2 +- .../engine/metadata/TbGetAttributesNode.java | 2 +- .../metadata/TbGetCustomerAttributeNode.java | 2 +- .../metadata/TbGetCustomerDetailsNode.java | 2 +- .../engine/metadata/TbGetDeviceAttrNode.java | 2 +- .../metadata/TbGetOriginatorFieldsNode.java | 2 +- .../metadata/TbGetRelatedAttributeNode.java | 2 +- .../engine/metadata/TbGetTelemetryNode.java | 2 +- .../metadata/TbGetTenantAttributeNode.java | 2 +- .../metadata/TbGetTenantDetailsNode.java | 2 +- .../rule/engine/mqtt/TbMqttNode.java | 2 +- .../rule/engine/rabbitmq/TbRabbitMqNode.java | 2 +- .../rule/engine/rest/TbRestApiCallNode.java | 2 +- .../rule/engine/rpc/TbSendRPCReplyNode.java | 2 +- .../rule/engine/rpc/TbSendRPCRequestNode.java | 2 +- .../engine/telemetry/TbMsgAttributesNode.java | 2 +- .../engine/telemetry/TbMsgTimeseriesNode.java | 2 +- .../TbSynchronizationBeginNode.java | 2 +- .../transaction/TbSynchronizationEndNode.java | 2 +- .../transform/TbChangeOriginatorNode.java | 2 +- .../engine/transform/TbTransformMsgNode.java | 2 +- ui/src/app/common/types.constant.js | 2 +- ui/src/app/locale/locale.constant-de_DE.json | 2 +- ui/src/app/locale/locale.constant-en_US.json | 2 +- ui/src/app/locale/locale.constant-es_ES.json | 2 +- ui/src/app/locale/locale.constant-fr_FR.json | 2 +- ui/src/app/rulechain/rulechain.controller.js | 2 +- ui/src/app/rulechain/rulechain.routes.js | 10 +- ui/src/app/rulechain/rulechains.controller.js | 2 +- ui/src/app/services/menu.service.js | 8 +- 111 files changed, 1353 insertions(+), 345 deletions(-) create mode 100644 common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeEventService.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEvent.java rename common/data/src/main/java/org/thingsboard/server/common/data/edge/{EdgeQueueEntityType.java => EdgeEventType.java} (95%) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/id/EdgeEventId.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeEventService.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/edge/EdgeEventDao.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/model/nosql/EdgeEventEntity.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/model/sql/EdgeEventEntity.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/model/type/EdgeEventTypeCodec.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/edge/EdgeEventRepository.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaBaseEdgeEventDao.java create mode 100644 dao/src/test/java/org/thingsboard/server/dao/service/BaseEdgeEventServiceTest.java rename dao/src/test/java/org/thingsboard/server/dao/service/{EdgeServiceImplTest.java => BaseEdgeServiceTest.java} (99%) create mode 100644 dao/src/test/java/org/thingsboard/server/dao/service/nosql/EdgeEventServiceNoSqlTest.java rename common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeQueueEntry.java => dao/src/test/java/org/thingsboard/server/dao/service/sql/EdgeEventServiceSqlTest.java (70%) delete mode 100644 rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/edge/PushToEdgeNodeCallback.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 cf26a5aff8..e0c4e502e1 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java @@ -58,6 +58,7 @@ import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.device.ClaimDevicesService; import org.thingsboard.server.dao.device.DeviceService; +import org.thingsboard.server.dao.edge.EdgeEventService; import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.event.EventService; @@ -258,6 +259,11 @@ public class ActorSystemContext { @Getter private EdgeService edgeService; + @Lazy + @Autowired + @Getter + private EdgeEventService edgeEventService; + @Value("${actors.session.max_concurrent_sessions_per_device:1}") @Getter private long maxConcurrentSessionsPerDevice; 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 f222c4d862..55229694a7 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 @@ -51,6 +51,7 @@ import org.thingsboard.server.dao.cassandra.CassandraCluster; import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.device.DeviceService; +import org.thingsboard.server.dao.edge.EdgeEventService; import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.nosql.CassandraStatementTask; @@ -406,6 +407,11 @@ class DefaultTbContext implements TbContext { return mainCtx.getEdgeService(); } + @Override + public EdgeEventService getEdgeEventService() { + return mainCtx.getEdgeEventService(); + } + @Override public EventLoopGroup getSharedEventLoop() { return mainCtx.getSharedEventLoopGroupService().getSharedEventLoopGroup(); diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java index 30e4e56ff3..d583eb6b0a 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java @@ -18,14 +18,11 @@ package org.thingsboard.server.actors.ruleChain; import akka.actor.ActorContext; import akka.actor.ActorRef; import akka.actor.Props; -import com.google.common.util.concurrent.FutureCallback; -import com.sun.istack.Nullable; import lombok.extern.slf4j.Slf4j; import org.thingsboard.rule.engine.api.TbRelationTypes; import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.service.DefaultActorService; import org.thingsboard.server.actors.shared.ComponentMsgProcessor; -import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.RuleChainId; @@ -102,7 +99,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor ruleNodeList = service.getRuleChainNodes(tenantId, entityId); log.trace("[{}][{}] Starting rule chain with {} nodes", tenantId, entityId, ruleNodeList.size()); // Creating and starting the actors; @@ -124,7 +121,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor ruleNodeList = service.getRuleChainNodes(tenantId, entityId); log.trace("[{}][{}] Updating rule chain with {} nodes", tenantId, entityId, ruleNodeList.size()); @@ -224,7 +221,6 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor() { - @Override - public void onSuccess(@Nullable Void aVoid) { - log.debug("Event saved successfully!"); - } - - @Override - public void onFailure(Throwable t) { - log.debug("Failure during event save", t); - } - }); - } - - } - @Override protected RuleNodeException getInactiveException() { RuleNode firstRuleNode = firstNode != null ? firstNode.getSelf() : null; diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainManagerActor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainManagerActor.java index 6a08a3e1ed..f591c394a3 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainManagerActor.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainManagerActor.java @@ -56,7 +56,7 @@ public abstract class RuleChainManagerActor extends ContextAwareActor { } protected void initRuleChains() { - for (RuleChain ruleChain : new PageDataIterable<>(link -> ruleChainService.findTenantRuleChainsByType(tenantId, RuleChainType.SYSTEM, link), ContextAwareActor.ENTITY_PACK_LIMIT)) { + for (RuleChain ruleChain : new PageDataIterable<>(link -> ruleChainService.findTenantRuleChainsByType(tenantId, RuleChainType.CORE, link), ContextAwareActor.ENTITY_PACK_LIMIT)) { RuleChainId ruleChainId = ruleChain.getId(); log.debug("[{}|{}] Creating rule chain actor", ruleChainId.getEntityType(), ruleChain.getId()); //TODO: remove this cast making UUIDBased subclass of EntityId an interface and vice versa. @@ -67,7 +67,7 @@ public abstract class RuleChainManagerActor extends ContextAwareActor { } protected void visit(RuleChain entity, ActorRef actorRef) { - if (entity != null && entity.isRoot() && entity.getType().equals(RuleChainType.SYSTEM)) { + if (entity != null && entity.isRoot() && entity.getType().equals(RuleChainType.CORE)) { rootChain = entity; rootChainActor = actorRef; } 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 900a3a88c3..246b5ad1e5 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 @@ -206,7 +206,7 @@ public class TenantActor extends RuleChainManagerActor { if (msg.getEntityId().getEntityType() == EntityType.RULE_CHAIN) { RuleChain ruleChain = systemContext.getRuleChainService(). findRuleChainById(tenantId, new RuleChainId(msg.getEntityId().getId())); - if (ruleChain != null && ruleChain.getType().equals(RuleChainType.SYSTEM)) { + if (ruleChain != null && ruleChain.getType().equals(RuleChainType.CORE)) { visit(ruleChain, target); } } diff --git a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java index 7553448c09..e56b4b34ef 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java @@ -125,6 +125,8 @@ public class AlarmController extends BaseController { alarmService.ackAlarm(getCurrentUser().getTenantId(), alarmId, ackTs).get(); alarm.setAckTs(ackTs); logEntityAction(alarmId, alarm, getCurrentUser().getCustomerId(), ActionType.ALARM_ACK, null); + + sendNotificationMsgToEdgeService(getTenantId(), alarmId, ActionType.ALARM_ACK); } catch (Exception e) { throw handleException(e); } @@ -142,6 +144,8 @@ public class AlarmController extends BaseController { alarmService.clearAlarm(getCurrentUser().getTenantId(), alarmId, null, clearTs).get(); alarm.setClearTs(clearTs); logEntityAction(alarmId, alarm, getCurrentUser().getCustomerId(), ActionType.ALARM_CLEAR, null); + + sendNotificationMsgToEdgeService(getTenantId(), alarmId, ActionType.ALARM_CLEAR); } catch (Exception e) { throw handleException(e); } diff --git a/application/src/main/java/org/thingsboard/server/controller/AssetController.java b/application/src/main/java/org/thingsboard/server/controller/AssetController.java index dcd6bca3a2..31f256c03a 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AssetController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AssetController.java @@ -27,12 +27,14 @@ import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.asset.AssetSearchQuery; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.edge.EdgeEventType; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.CustomerId; @@ -86,6 +88,8 @@ public class AssetController extends BaseController { Asset savedAsset = checkNotNull(assetService.saveAsset(asset)); + sendNotificationMsgToEdgeService(savedAsset.getTenantId(), savedAsset.getId(), EdgeEventType.ASSET, asset.getId() == null ? ActionType.ADDED : ActionType.UPDATED); + logEntityAction(savedAsset.getId(), savedAsset, savedAsset.getCustomerId(), asset.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); @@ -112,6 +116,7 @@ public class AssetController extends BaseController { asset.getCustomerId(), ActionType.DELETED, null, strAssetId); + sendNotificationMsgToEdgeService(getTenantId(), assetId, EdgeEventType.ASSET, ActionType.DELETED); } catch (Exception e) { logEntityAction(emptyId(EntityType.ASSET), null, @@ -354,6 +359,8 @@ public class AssetController extends BaseController { savedAsset.getCustomerId(), ActionType.ASSIGNED_TO_EDGE, null, strAssetId, strEdgeId, edge.getName()); + sendNotificationMsgToEdgeService(getTenantId(), savedAsset.getId(), EdgeEventType.ASSET, ActionType.ASSIGNED_TO_EDGE); + return savedAsset; } catch (Exception e) { @@ -385,6 +392,8 @@ public class AssetController extends BaseController { asset.getCustomerId(), ActionType.UNASSIGNED_FROM_EDGE, null, strAssetId, edge.getId().toString(), edge.getName()); + sendNotificationMsgToEdgeService(getTenantId(), savedAsset.getId(), EdgeEventType.ASSET, ActionType.UNASSIGNED_FROM_EDGE); + return savedAsset; } catch (Exception e) { 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 e3f0c381d1..77f066795c 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -39,6 +39,7 @@ import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.common.data.edge.EdgeEventType; import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.asset.Asset; @@ -66,6 +67,7 @@ import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.plugin.ComponentDescriptor; import org.thingsboard.server.common.data.plugin.ComponentType; +import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.data.rule.RuleNode; @@ -83,6 +85,7 @@ import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.device.ClaimDevicesService; import org.thingsboard.server.dao.device.DeviceCredentialsService; import org.thingsboard.server.dao.device.DeviceService; +import org.thingsboard.server.dao.edge.EdgeEventService; import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.exception.DataValidationException; @@ -95,10 +98,12 @@ import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.dao.widget.WidgetTypeService; import org.thingsboard.server.dao.widget.WidgetsBundleService; import org.thingsboard.server.exception.ThingsboardErrorResponseHandler; +import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.provider.TbQueueProducerProvider; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.component.ComponentDiscoveryService; +import org.thingsboard.server.service.edge.EdgeNotificationService; import org.thingsboard.server.service.queue.TbClusterService; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.AccessControlService; @@ -108,6 +113,7 @@ import org.thingsboard.server.service.state.DeviceStateService; import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; import javax.mail.MessagingException; +import javax.management.relation.Relation; import javax.servlet.http.HttpServletResponse; import java.util.List; import java.util.Optional; @@ -200,6 +206,12 @@ public abstract class BaseController { @Autowired protected EdgeService edgeService; + @Autowired + protected EdgeNotificationService edgeNotificationService; + + @Autowired + protected EdgeEventService edgeEventService; + @Value("${server.log_controller_error_stack_trace}") @Getter private boolean logControllerErrorStackTrace; @@ -559,7 +571,6 @@ public abstract class BaseController { } if (e == null) { pushEntityActionToRuleEngine(entityId, entity, user, customerId, actionType, additionalInfo); - // TODO: voba - refactor to push events to edge queue directly, instead of the rule engine flow } auditLogService.logEntityAction(user.getTenantId(), customerId, user.getId(), user.getName(), entityId, entity, actionType, e, additionalInfo); } @@ -600,16 +611,6 @@ public abstract class BaseController { case ALARM_CLEAR: msgType = DataConstants.ALARM_CLEAR; break; - case ASSIGNED_TO_EDGE: - msgType = DataConstants.ENTITY_ASSIGNED_TO_EDGE; - break; - case UNASSIGNED_FROM_EDGE: - msgType = DataConstants.ENTITY_UNASSIGNED_FROM_EDGE; - break; - case CREDENTIALS_UPDATED: - //TODO: voba - this is not efficient way to do this. Refactor on later stages - msgType = DataConstants.ENTITY_UPDATED; - break; } if (!StringUtils.isEmpty(msgType)) { try { @@ -698,5 +699,42 @@ public abstract class BaseController { return result; } + protected void sendNotificationMsgToEdgeService(TenantId tenantId, EntityRelation relation, ActionType edgeEventAction) { + try { + sendNotificationMsgToEdgeService(tenantId, null, json.writeValueAsString(relation), EdgeEventType.RELATION, edgeEventAction); + } catch (Exception e) { + log.warn("Failed to push relation to core: {}", relation, e); + } + } + + protected void sendNotificationMsgToEdgeService(TenantId tenantId, EntityId entityId, ActionType edgeEventAction) { + EdgeEventType edgeEventType = edgeEventService.getEdgeEventTypeByEntityType(entityId.getEntityType()); + if (edgeEventType != null) { + sendNotificationMsgToEdgeService(tenantId, entityId, null, edgeEventType, edgeEventAction); + } + } + + protected void sendNotificationMsgToEdgeService(TenantId tenantId, EntityId entityId, EdgeEventType edgeEventType, ActionType edgeEventAction) { + sendNotificationMsgToEdgeService(tenantId, entityId, null, edgeEventType, edgeEventAction); + } + + private void sendNotificationMsgToEdgeService(TenantId tenantId, EntityId entityId, String entityBody, EdgeEventType edgeEventType, ActionType edgeEventAction) { + TransportProtos.EdgeNotificationMsgProto.Builder builder = TransportProtos.EdgeNotificationMsgProto.newBuilder(); + builder.setTenantIdMSB(tenantId.getId().getMostSignificantBits()); + builder.setTenantIdLSB(tenantId.getId().getLeastSignificantBits()); + builder.setEdgeEventType(edgeEventType.name()); + builder.setEdgeEventAction(edgeEventAction.name()); + if (entityId != null) { + builder.setEntityIdMSB(entityId.getId().getMostSignificantBits()); + builder.setEntityIdLSB(entityId.getId().getLeastSignificantBits()); + builder.setEntityType(entityId.getEntityType().name()); + } + if (entityBody != null) { + builder.setEntityBody(entityBody); + } + TransportProtos.EdgeNotificationMsgProto msg = builder.build(); + tbClusterService.pushMsgToCore(tenantId, entityId != null ? entityId : tenantId, + TransportProtos.ToCoreMsg.newBuilder().setEdgeNotificationMsg(msg).build(), null); + } } diff --git a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java index 5012e775ec..90d991a400 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java @@ -34,6 +34,7 @@ import org.thingsboard.server.common.data.ShortCustomerInfo; import org.thingsboard.server.common.data.ShortEdgeInfo; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.edge.EdgeEventType; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DashboardId; @@ -116,6 +117,9 @@ public class DashboardController extends BaseController { null, dashboard.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); + sendNotificationMsgToEdgeService(savedDashboard.getTenantId(), savedDashboard.getId(), + EdgeEventType.DASHBOARD, savedDashboard.getId() == null ? ActionType.ADDED : ActionType.UPDATED); + return savedDashboard; } catch (Exception e) { logEntityAction(emptyId(EntityType.DASHBOARD), dashboard, @@ -139,6 +143,7 @@ public class DashboardController extends BaseController { null, ActionType.DELETED, null, strDashboardId); + sendNotificationMsgToEdgeService(getTenantId(), dashboardId, EdgeEventType.DASHBOARD, ActionType.DELETED); } catch (Exception e) { logEntityAction(emptyId(EntityType.DASHBOARD), @@ -495,6 +500,7 @@ public class DashboardController extends BaseController { null, ActionType.ASSIGNED_TO_EDGE, null, strDashboardId, strEdgeId, edge.getName()); + sendNotificationMsgToEdgeService(getTenantId(), savedDashboard.getId(), EdgeEventType.DASHBOARD, ActionType.ASSIGNED_TO_EDGE); return savedDashboard; } catch (Exception e) { @@ -526,6 +532,8 @@ public class DashboardController extends BaseController { null, ActionType.UNASSIGNED_FROM_EDGE, null, strDashboardId, edge.getId().toString(), edge.getName()); + sendNotificationMsgToEdgeService(getTenantId(), savedDashboard.getId(), EdgeEventType.DASHBOARD, ActionType.UNASSIGNED_FROM_EDGE); + return savedDashboard; } catch (Exception e) { diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java index 0218ebf47e..0f24507cbc 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java @@ -42,6 +42,7 @@ import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.device.DeviceSearchQuery; import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.edge.EdgeEventType; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; @@ -57,6 +58,7 @@ import org.thingsboard.server.dao.device.claim.ClaimResponse; import org.thingsboard.server.dao.device.claim.ClaimResult; import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.model.ModelConstants; +import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; @@ -106,6 +108,8 @@ public class DeviceController extends BaseController { tbClusterService.pushMsgToCore(new DeviceNameOrTypeUpdateMsg(savedDevice.getTenantId(), savedDevice.getId(), savedDevice.getName(), savedDevice.getType()), null); + sendNotificationMsgToEdgeService(savedDevice.getTenantId(), savedDevice.getId(), EdgeEventType.DEVICE, device.getId() == null ? ActionType.ADDED : ActionType.UPDATED); + logEntityAction(savedDevice.getId(), savedDevice, savedDevice.getCustomerId(), device.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); @@ -137,6 +141,8 @@ public class DeviceController extends BaseController { device.getCustomerId(), ActionType.DELETED, null, strDeviceId); + sendNotificationMsgToEdgeService(getTenantId(), deviceId, EdgeEventType.DEVICE, ActionType.DELETED); + deviceStateService.onDeviceDeleted(device); } catch (Exception e) { logEntityAction(emptyId(EntityType.DEVICE), @@ -260,6 +266,8 @@ public class DeviceController extends BaseController { tbClusterService.pushMsgToCore(new DeviceCredentialsUpdateNotificationMsg(getCurrentUser().getTenantId(), deviceCredentials.getDeviceId()), null); + sendNotificationMsgToEdgeService(getTenantId(), device.getId(), EdgeEventType.DEVICE, ActionType.CREDENTIALS_UPDATED); + logEntityAction(device.getId(), device, device.getCustomerId(), ActionType.CREDENTIALS_UPDATED, null, deviceCredentials); @@ -509,6 +517,8 @@ public class DeviceController extends BaseController { savedDevice.getCustomerId(), ActionType.ASSIGNED_TO_EDGE, null, strDeviceId, strEdgeId, edge.getName()); + sendNotificationMsgToEdgeService(getTenantId(), savedDevice.getId(), EdgeEventType.DEVICE, ActionType.ASSIGNED_TO_EDGE); + return savedDevice; } catch (Exception e) { logEntityAction(emptyId(EntityType.DEVICE), null, @@ -538,6 +548,8 @@ public class DeviceController extends BaseController { device.getCustomerId(), ActionType.UNASSIGNED_FROM_EDGE, null, strDeviceId, edge.getId().toString(), edge.getName()); + sendNotificationMsgToEdgeService(getTenantId(), savedDevice.getId(), EdgeEventType.DEVICE, ActionType.UNASSIGNED_FROM_EDGE); + return savedDevice; } catch (Exception e) { logEntityAction(emptyId(EntityType.DEVICE), null, diff --git a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java b/application/src/main/java/org/thingsboard/server/controller/EdgeController.java index d263497527..b6f2c31561 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EdgeController.java @@ -97,7 +97,7 @@ public class EdgeController extends BaseController { if (created) { ruleChainService.assignRuleChainToEdge(tenantId, defaultRootEdgeRuleChain.getId(), savedEdge.getId()); - edgeService.setEdgeRootRuleChain(tenantId, savedEdge, defaultRootEdgeRuleChain.getId()); + edgeNotificationService.setEdgeRootRuleChain(tenantId, savedEdge, defaultRootEdgeRuleChain.getId()); edgeService.assignDefaultRuleChainsToEdge(tenantId, savedEdge.getId()); } @@ -257,8 +257,7 @@ public class EdgeController extends BaseController { @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/tenant/edges", params = {"edgeName"}, method = RequestMethod.GET) @ResponseBody - public Edge getTenantEdge( - @RequestParam String edgeName) throws ThingsboardException { + public Edge getTenantEdge(@RequestParam String edgeName) throws ThingsboardException { try { TenantId tenantId = getCurrentUser().getTenantId(); return checkNotNull(edgeService.findEdgeByTenantIdAndName(tenantId, edgeName)); @@ -283,7 +282,7 @@ public class EdgeController extends BaseController { accessControlService.checkPermission(getCurrentUser(), Resource.EDGE, Operation.WRITE, edge.getId(), edge); - Edge updatedEdge = edgeService.setEdgeRootRuleChain(getTenantId(), edge, ruleChainId); + Edge updatedEdge = edgeNotificationService.setEdgeRootRuleChain(getTenantId(), edge, ruleChainId); logEntityAction(updatedEdge.getId(), updatedEdge, null, ActionType.UPDATED, null); diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java b/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java index 88aa9ce850..38060c7731 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java @@ -24,7 +24,9 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; +import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.edge.EdgeEventType; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.EntityId; @@ -63,10 +65,13 @@ public class EntityRelationController extends BaseController { relation.setTypeGroup(RelationTypeGroup.COMMON); } relationService.saveRelation(getTenantId(), relation); + logEntityAction(relation.getFrom(), null, getCurrentUser().getCustomerId(), ActionType.RELATION_ADD_OR_UPDATE, null, relation); logEntityAction(relation.getTo(), null, getCurrentUser().getCustomerId(), ActionType.RELATION_ADD_OR_UPDATE, null, relation); + + sendNotificationMsgToEdgeService(getTenantId(), relation, ActionType.RELATION_ADD_OR_UPDATE); } catch (Exception e) { logEntityAction(relation.getFrom(), null, getCurrentUser().getCustomerId(), ActionType.RELATION_ADD_OR_UPDATE, e, relation); @@ -104,6 +109,8 @@ public class EntityRelationController extends BaseController { ActionType.RELATION_DELETED, null, relation); logEntityAction(relation.getTo(), null, getCurrentUser().getCustomerId(), ActionType.RELATION_DELETED, null, relation); + + sendNotificationMsgToEdgeService(getTenantId(), relation, ActionType.RELATION_DELETED); } catch (Exception e) { logEntityAction(relation.getFrom(), null, getCurrentUser().getCustomerId(), ActionType.RELATION_DELETED, e, relation); @@ -125,6 +132,8 @@ public class EntityRelationController extends BaseController { try { relationService.deleteEntityRelations(getTenantId(), entityId); logEntityAction(entityId, null, getCurrentUser().getCustomerId(), ActionType.RELATIONS_DELETED, null); + + sendNotificationMsgToEdgeService(getTenantId(), entityId, ActionType.RELATIONS_DELETED); } catch (Exception e) { logEntityAction(entityId, null, getCurrentUser().getCustomerId(), ActionType.RELATIONS_DELETED, e); throw handleException(e); diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java index 3318ff8e79..970cd98db3 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java @@ -37,6 +37,7 @@ import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.edge.EdgeEventType; import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.CustomerId; @@ -116,6 +117,8 @@ public class EntityViewController extends BaseController { logEntityAction(savedEntityView.getId(), savedEntityView, null, entityView.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); + + sendNotificationMsgToEdgeService(getTenantId(), savedEntityView.getId(), EdgeEventType.ENTITY_VIEW, entityView.getId() == null ? ActionType.ADDED : ActionType.UPDATED); return savedEntityView; } catch (Exception e) { logEntityAction(emptyId(EntityType.ENTITY_VIEW), entityView, null, @@ -185,6 +188,8 @@ public class EntityViewController extends BaseController { entityViewService.deleteEntityView(getTenantId(), entityViewId); logEntityAction(entityViewId, entityView, entityView.getCustomerId(), ActionType.DELETED, null, strEntityViewId); + + sendNotificationMsgToEdgeService(getTenantId(), entityViewId, EdgeEventType.ENTITY_VIEW, ActionType.DELETED); } catch (Exception e) { logEntityAction(emptyId(EntityType.ENTITY_VIEW), null, @@ -389,6 +394,9 @@ public class EntityViewController extends BaseController { logEntityAction(entityViewId, savedEntityView, savedEntityView.getCustomerId(), ActionType.ASSIGNED_TO_EDGE, null, strEntityViewId, strEdgeId, edge.getName()); + + sendNotificationMsgToEdgeService(getTenantId(), savedEntityView.getId(), EdgeEventType.ENTITY_VIEW, ActionType.ASSIGNED_TO_EDGE); + return savedEntityView; } catch (Exception e) { logEntityAction(emptyId(EntityType.ENTITY_VIEW), null, @@ -417,6 +425,8 @@ public class EntityViewController extends BaseController { entityView.getCustomerId(), ActionType.UNASSIGNED_FROM_EDGE, null, strEntityViewId, edge.getId().toString(), edge.getName()); + sendNotificationMsgToEdgeService(getTenantId(), savedEntityView.getId(), EdgeEventType.ENTITY_VIEW, ActionType.UNASSIGNED_FROM_EDGE); + return savedEntityView; } catch (Exception e) { logEntityAction(emptyId(EntityType.ENTITY_VIEW), null, diff --git a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java index 604d3b62dd..ce4a96c2c9 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java +++ b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java @@ -41,6 +41,7 @@ import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.Event; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.edge.EdgeEventType; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.RuleChainId; @@ -134,7 +135,7 @@ public class RuleChainController extends BaseController { RuleChain savedRuleChain = checkNotNull(ruleChainService.saveRuleChain(ruleChain)); - if (RuleChainType.SYSTEM.equals(savedRuleChain.getType())) { + if (RuleChainType.CORE.equals(savedRuleChain.getType())) { tbClusterService.onEntityStateChange(ruleChain.getTenantId(), savedRuleChain.getId(), created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); } @@ -143,6 +144,12 @@ public class RuleChainController extends BaseController { null, created ? ActionType.ADDED : ActionType.UPDATED, null); + if (RuleChainType.EDGE.equals(savedRuleChain.getType())) { + sendNotificationMsgToEdgeService(savedRuleChain.getTenantId(), + savedRuleChain.getId(), EdgeEventType.RULE_CHAIN, + savedRuleChain.getId() == null ? ActionType.ADDED : ActionType.UPDATED); + } + return savedRuleChain; } catch (Exception e) { @@ -209,7 +216,7 @@ public class RuleChainController extends BaseController { RuleChain ruleChain = checkRuleChain(ruleChainMetaData.getRuleChainId(), Operation.WRITE); RuleChainMetaData savedRuleChainMetaData = checkNotNull(ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData)); - if (RuleChainType.SYSTEM.equals(ruleChain.getType())) { + if (RuleChainType.CORE.equals(ruleChain.getType())) { tbClusterService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.UPDATED); } @@ -217,6 +224,12 @@ public class RuleChainController extends BaseController { null, ActionType.UPDATED, null, ruleChainMetaData); + if (RuleChainType.EDGE.equals(ruleChain.getType())) { + sendNotificationMsgToEdgeService(ruleChain.getTenantId(), + ruleChain.getId(), EdgeEventType.RULE_CHAIN, + ActionType.UPDATED); + } + return savedRuleChainMetaData; } catch (Exception e) { @@ -243,7 +256,7 @@ public class RuleChainController extends BaseController { RuleChainType type = RuleChainType.valueOf(typeStr); return checkNotNull(ruleChainService.findTenantRuleChainsByType(tenantId, type, pageLink)); } else { - return checkNotNull(ruleChainService.findTenantRuleChainsByType(tenantId, RuleChainType.SYSTEM, pageLink)); + return checkNotNull(ruleChainService.findTenantRuleChainsByType(tenantId, RuleChainType.CORE, pageLink)); } } catch (Exception e) { throw handleException(e); @@ -267,7 +280,7 @@ public class RuleChainController extends BaseController { referencingRuleChainIds.remove(ruleChain.getId()); - if (RuleChainType.SYSTEM.equals(ruleChain.getType())) { + if (RuleChainType.CORE.equals(ruleChain.getType())) { referencingRuleChainIds.forEach(referencingRuleChainId -> tbClusterService.onEntityStateChange(ruleChain.getTenantId(), referencingRuleChainId, ComponentLifecycleEvent.UPDATED)); @@ -278,6 +291,12 @@ public class RuleChainController extends BaseController { null, ActionType.DELETED, null, strRuleChainId); + if (RuleChainType.EDGE.equals(ruleChain.getType())) { + sendNotificationMsgToEdgeService(ruleChain.getTenantId(), + ruleChain.getId(), EdgeEventType.RULE_CHAIN, + ActionType.DELETED); + } + } catch (Exception e) { logEntityAction(emptyId(EntityType.RULE_CHAIN), null, @@ -407,6 +426,8 @@ public class RuleChainController extends BaseController { null, ActionType.ASSIGNED_TO_EDGE, null, strRuleChainId, strEdgeId, edge.getName()); + sendNotificationMsgToEdgeService(getTenantId(), savedRuleChain.getId(), + EdgeEventType.RULE_CHAIN, ActionType.ASSIGNED_TO_EDGE); return savedRuleChain; } catch (Exception e) { @@ -438,6 +459,9 @@ public class RuleChainController extends BaseController { null, ActionType.UNASSIGNED_FROM_EDGE, null, strRuleChainId, edge.getId().toString(), edge.getName()); + sendNotificationMsgToEdgeService(getTenantId(), savedRuleChain.getId(), + EdgeEventType.RULE_CHAIN, ActionType.UNASSIGNED_FROM_EDGE); + return savedRuleChain; } catch (Exception e) { diff --git a/application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java b/application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java index 2203fb461b..43231768f9 100644 --- a/application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java +++ b/application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java @@ -116,7 +116,7 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe } private void putComponentIntoMaps(ComponentType type, RuleNode ruleNodeAnnotation, ComponentDescriptor component) { - if (ruleChainTypeContainsArray(RuleChainType.SYSTEM, ruleNodeAnnotation.ruleChainTypes())) { + if (ruleChainTypeContainsArray(RuleChainType.CORE, ruleNodeAnnotation.ruleChainTypes())) { systemComponentsMap.computeIfAbsent(type, k -> new ArrayList<>()).add(component); } if (ruleChainTypeContainsArray(RuleChainType.EDGE, ruleNodeAnnotation.ruleChainTypes())) { @@ -225,7 +225,7 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe @Override public List getComponents(Set types, RuleChainType ruleChainType) { - if (RuleChainType.SYSTEM.equals(ruleChainType)) { + if (RuleChainType.CORE.equals(ruleChainType)) { return getComponents(types, systemComponentsMap); } else if (RuleChainType.EDGE.equals(ruleChainType)) { return getComponents(types, edgeComponentsMap); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java b/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java index a65e95d0ff..96f127d9be 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java @@ -1,3 +1,18 @@ +/** + * Copyright © 2016-2020 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; import com.fasterxml.jackson.databind.ObjectMapper; @@ -17,9 +32,10 @@ import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.Event; import org.thingsboard.server.common.data.alarm.Alarm; 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.edge.EdgeQueueEntityType; -import org.thingsboard.server.common.data.edge.EdgeQueueEntry; +import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.edge.EdgeEventType; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.RuleChainId; @@ -36,6 +52,7 @@ import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.common.msg.session.SessionMsgType; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.device.DeviceService; +import org.thingsboard.server.dao.edge.EdgeEventService; import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.event.EventService; @@ -79,7 +96,7 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { private RelationService relationService; @Autowired - private EventService eventService; + private EdgeEventService edgeEventService; private ExecutorService tsCallBackExecutor; @@ -96,8 +113,8 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { } @Override - public TimePageData findQueueEvents(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink) { - return eventService.findEvents(tenantId, edgeId, DataConstants.EDGE_QUEUE_EVENT_TYPE, pageLink); + public TimePageData findEdgeEvents(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink) { + return edgeEventService.findEdgeEvents(tenantId, edgeId, pageLink); } @Override @@ -105,7 +122,7 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { edge.setRootRuleChainId(ruleChainId); Edge savedEdge = edgeService.saveEdge(edge); RuleChain ruleChain = ruleChainService.findRuleChainById(tenantId, ruleChainId); - saveEventToEdgeQueue(tenantId, edge.getId(), EdgeQueueEntityType.RULE_CHAIN, DataConstants.ENTITY_UPDATED, mapper.writeValueAsString(ruleChain), new FutureCallback() { + saveEventToEdgeQueue(tenantId, edge.getId(), EdgeEventType.RULE_CHAIN, DataConstants.ENTITY_UPDATED, mapper.writeValueAsString(ruleChain), new FutureCallback() { @Override public void onSuccess(@Nullable Void aVoid) { log.debug("Event saved successfully!"); @@ -119,28 +136,28 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { return savedEdge; } - private void saveEventToEdgeQueue(TenantId tenantId, EdgeId edgeId, EdgeQueueEntityType entityType, String type, String data, FutureCallback callback) throws IOException { + private void saveEventToEdgeQueue(TenantId tenantId, EdgeId edgeId, EdgeEventType entityType, String type, String data, FutureCallback callback) throws IOException { log.debug("Pushing single event to edge queue. tenantId [{}], edgeId [{}], entityType [{}], type[{}], data [{}]", tenantId, edgeId, entityType, type, data); - EdgeQueueEntry queueEntry = new EdgeQueueEntry(); - queueEntry.setEntityType(entityType); - queueEntry.setType(type); - queueEntry.setData(data); +// EdgeEQueueEntry queueEntry = new EdgeQueueEntry(); +// queueEntry.setEntityType(entityType); +// queueEntry.setType(type); +// queueEntry.setData(data); - Event event = new Event(); - event.setEntityId(edgeId); - event.setTenantId(tenantId); - event.setType(DataConstants.EDGE_QUEUE_EVENT_TYPE); - event.setBody(mapper.valueToTree(queueEntry)); - ListenableFuture saveFuture = eventService.saveAsync(event); + EdgeEvent edgeEvent = new EdgeEvent(); + edgeEvent.setEdgeId(edgeId); + edgeEvent.setTenantId(tenantId); +// event.setType(DataConstants.EDGE_QUEUE_EVENT_TYPE); +// event.setBody(mapper.valueToTree(queueEntry)); + ListenableFuture saveFuture = edgeEventService.saveAsync(edgeEvent); addMainCallback(saveFuture, callback); } - private void addMainCallback(ListenableFuture saveFuture, final FutureCallback callback) { - Futures.addCallback(saveFuture, new FutureCallback() { + private void addMainCallback(ListenableFuture saveFuture, final FutureCallback callback) { + Futures.addCallback(saveFuture, new FutureCallback() { @Override - public void onSuccess(@Nullable Event result) { + public void onSuccess(@Nullable EdgeEvent result) { callback.onSuccess(null); } @@ -153,52 +170,52 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { @Override public void pushNotificationToEdge(TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg, TbCallback callback) { - if (tbMsg.getType().equals(SessionMsgType.POST_TELEMETRY_REQUEST.name()) || - tbMsg.getType().equals(SessionMsgType.POST_ATTRIBUTES_REQUEST.name()) || - tbMsg.getType().equals(DataConstants.ATTRIBUTES_UPDATED) || - tbMsg.getType().equals(DataConstants.ATTRIBUTES_DELETED)) { - processCustomTbMsg(tenantId, tbMsg, callback); - } else { - try { - switch (tbMsg.getOriginator().getEntityType()) { - case EDGE: - processEdge(tenantId, tbMsg, callback); - break; - case ASSET: - processAsset(tenantId, tbMsg, callback); - break; - case DEVICE: - processDevice(tenantId, tbMsg, callback); - break; - case DASHBOARD: - processDashboard(tenantId, tbMsg, callback); - break; - case RULE_CHAIN: - processRuleChain(tenantId, tbMsg, callback); - break; - case ENTITY_VIEW: - processEntityView(tenantId, tbMsg, callback); - break; - case ALARM: - processAlarm(tenantId, tbMsg, callback); - break; - default: - log.debug("Entity type [{}] is not designed to be pushed to edge", tbMsg.getOriginator().getEntityType()); - } - } catch (IOException e) { - log.error("Can't push to edge updates, entity type [{}], data [{}]", tbMsg.getOriginator().getEntityType(), tbMsg.getData(), e); - } - } +// if (tbMsg.getType().equals(SessionMsgType.POST_TELEMETRY_REQUEST.name()) || +// tbMsg.getType().equals(SessionMsgType.POST_ATTRIBUTES_REQUEST.name()) || +// tbMsg.getType().equals(DataConstants.ATTRIBUTES_UPDATED) || +// tbMsg.getType().equals(DataConstants.ATTRIBUTES_DELETED)) { +// processCustomTbMsg(tenantId, tbMsg, callback); +// } else { +// try { +// switch (tbMsg.getOriginator().getEntityType()) { +// case EDGE: +// processEdge(tenantId, tbMsg, callback); +// break; +// case ASSET: +// processAsset(tenantId, tbMsg, callback); +// break; +// case DEVICE: +// processDevice(tenantId, tbMsg, callback); +// break; +// case DASHBOARD: +// processDashboard(tenantId, tbMsg, callback); +// break; +// case RULE_CHAIN: +// processRuleChain(tenantId, tbMsg, callback); +// break; +// case ENTITY_VIEW: +// processEntityView(tenantId, tbMsg, callback); +// break; +// case ALARM: +// processAlarm(tenantId, tbMsg, callback); +// break; +// default: +// log.debug("Entity type [{}] is not designed to be pushed to edge", tbMsg.getOriginator().getEntityType()); +// } +// } catch (IOException e) { +// log.error("Can't push to edge updates, entity type [{}], data [{}]", tbMsg.getOriginator().getEntityType(), tbMsg.getData(), e); +// } +// } } private void processCustomTbMsg(TenantId tenantId, TbMsg tbMsg, FutureCallback callback) { ListenableFuture edgeIdFuture = getEdgeIdByOriginatorId(tenantId, tbMsg.getOriginator()); Futures.transform(edgeIdFuture, edgeId -> { - EdgeQueueEntityType edgeQueueEntityType = getEdgeQueueTypeByEntityType(tbMsg.getOriginator().getEntityType()); - if (edgeId != null && edgeQueueEntityType != null) { + EdgeEventType edgeEventType = getEdgeQueueTypeByEntityType(tbMsg.getOriginator().getEntityType()); + if (edgeId != null && edgeEventType != null) { try { - saveEventToEdgeQueue(tenantId, edgeId, edgeQueueEntityType, tbMsg.getType(), Base64.encodeBase64String(TbMsg.toByteArray(tbMsg)), callback); + saveEventToEdgeQueue(tenantId, edgeId, edgeEventType, tbMsg.getType(), Base64.encodeBase64String(TbMsg.toByteArray(tbMsg)), callback); } catch (IOException e) { log.error("Error while saving custom tbMsg into Edge Queue", e); } @@ -211,13 +228,13 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { switch (tbMsg.getType()) { case DataConstants.ENTITY_ASSIGNED_TO_EDGE: case DataConstants.ENTITY_UNASSIGNED_FROM_EDGE: - processAssignedEntity(tenantId, tbMsg, EdgeQueueEntityType.DEVICE, callback); + processAssignedEntity(tenantId, tbMsg, EdgeEventType.DEVICE, callback); break; case DataConstants.ENTITY_DELETED: case DataConstants.ENTITY_CREATED: case DataConstants.ENTITY_UPDATED: Device device = mapper.readValue(tbMsg.getData(), Device.class); - pushEventToEdge(tenantId, device.getId(), EdgeQueueEntityType.DEVICE, tbMsg, callback); + pushEventToEdge(tenantId, device.getId(), EdgeEventType.DEVICE, tbMsg, callback); break; default: log.warn("Unsupported msgType [{}], tbMsg [{}]", tbMsg.getType(), tbMsg); @@ -240,13 +257,13 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { switch (tbMsg.getType()) { case DataConstants.ENTITY_ASSIGNED_TO_EDGE: case DataConstants.ENTITY_UNASSIGNED_FROM_EDGE: - processAssignedEntity(tenantId, tbMsg, EdgeQueueEntityType.ASSET, callback); + processAssignedEntity(tenantId, tbMsg, EdgeEventType.ASSET, callback); break; case DataConstants.ENTITY_DELETED: case DataConstants.ENTITY_CREATED: case DataConstants.ENTITY_UPDATED: Asset asset = mapper.readValue(tbMsg.getData(), Asset.class); - pushEventToEdge(tenantId, asset.getId(), EdgeQueueEntityType.ASSET, tbMsg, callback); + pushEventToEdge(tenantId, asset.getId(), EdgeEventType.ASSET, tbMsg, callback); break; default: log.warn("Unsupported msgType [{}], tbMsg [{}]", tbMsg.getType(), tbMsg); @@ -257,13 +274,13 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { switch (tbMsg.getType()) { case DataConstants.ENTITY_ASSIGNED_TO_EDGE: case DataConstants.ENTITY_UNASSIGNED_FROM_EDGE: - processAssignedEntity(tenantId, tbMsg, EdgeQueueEntityType.ENTITY_VIEW, callback); + processAssignedEntity(tenantId, tbMsg, EdgeEventType.ENTITY_VIEW, callback); break; case DataConstants.ENTITY_DELETED: case DataConstants.ENTITY_CREATED: case DataConstants.ENTITY_UPDATED: EntityView entityView = mapper.readValue(tbMsg.getData(), EntityView.class); - pushEventToEdge(tenantId, entityView.getId(), EdgeQueueEntityType.ENTITY_VIEW, tbMsg, callback); + pushEventToEdge(tenantId, entityView.getId(), EdgeEventType.ENTITY_VIEW, tbMsg, callback); break; default: log.warn("Unsupported msgType [{}], tbMsg [{}]", tbMsg.getType(), tbMsg); @@ -278,9 +295,9 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { case DataConstants.ALARM_ACK: case DataConstants.ALARM_CLEAR: Alarm alarm = mapper.readValue(tbMsg.getData(), Alarm.class); - EdgeQueueEntityType edgeQueueEntityType = getEdgeQueueTypeByEntityType(alarm.getOriginator().getEntityType()); - if (edgeQueueEntityType != null) { - pushEventToEdge(tenantId, alarm.getOriginator(), EdgeQueueEntityType.ALARM, tbMsg, callback); + EdgeEventType edgeEventType = getEdgeQueueTypeByEntityType(alarm.getOriginator().getEntityType()); + if (edgeEventType != null) { + pushEventToEdge(tenantId, alarm.getOriginator(), EdgeEventType.ALARM, tbMsg, callback); } break; default: @@ -292,7 +309,7 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { switch (tbMsg.getType()) { case DataConstants.ENTITY_ASSIGNED_TO_EDGE: case DataConstants.ENTITY_UNASSIGNED_FROM_EDGE: - processAssignedEntity(tenantId, tbMsg, EdgeQueueEntityType.DASHBOARD, callback); + processAssignedEntity(tenantId, tbMsg, EdgeEventType.DASHBOARD, callback); break; case DataConstants.ENTITY_DELETED: case DataConstants.ENTITY_CREATED: @@ -303,7 +320,7 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { if (edges != null && edges.getData() != null && !edges.getData().isEmpty()) { try { for (Edge edge : edges.getData()) { - pushEventToEdge(tenantId, edge.getId(), EdgeQueueEntityType.DASHBOARD, tbMsg, callback); + pushEventToEdge(tenantId, edge.getId(), EdgeEventType.DASHBOARD, tbMsg, callback); } } catch (IOException e) { log.error("Can't push event to edge", e); @@ -321,7 +338,7 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { switch (tbMsg.getType()) { case DataConstants.ENTITY_ASSIGNED_TO_EDGE: case DataConstants.ENTITY_UNASSIGNED_FROM_EDGE: - processAssignedEntity(tenantId, tbMsg, EdgeQueueEntityType.RULE_CHAIN, callback); + processAssignedEntity(tenantId, tbMsg, EdgeEventType.RULE_CHAIN, callback); break; case DataConstants.ENTITY_DELETED: case DataConstants.ENTITY_CREATED: @@ -333,7 +350,7 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { if (edges != null && edges.getData() != null && !edges.getData().isEmpty()) { try { for (Edge edge : edges.getData()) { - pushEventToEdge(tenantId, edge.getId(), EdgeQueueEntityType.RULE_CHAIN, tbMsg, callback); + pushEventToEdge(tenantId, edge.getId(), EdgeEventType.RULE_CHAIN, tbMsg, callback); } } catch (IOException e) { log.error("Can't push event to edge", e); @@ -349,7 +366,7 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { } - private void processAssignedEntity(TenantId tenantId, TbMsg tbMsg, EdgeQueueEntityType entityType, FutureCallback callback) throws IOException { + private void processAssignedEntity(TenantId tenantId, TbMsg tbMsg, EdgeEventType entityType, FutureCallback callback) throws IOException { EdgeId edgeId; switch (tbMsg.getType()) { case DataConstants.ENTITY_ASSIGNED_TO_EDGE: @@ -364,13 +381,12 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { } } - - private void pushEventToEdge(TenantId tenantId, EntityId originatorId, EdgeQueueEntityType edgeQueueEntityType, TbMsg tbMsg, FutureCallback callback) { + private void pushEventToEdge(TenantId tenantId, EntityId originatorId, EdgeEventType edgeEventType, TbMsg tbMsg, FutureCallback callback) { ListenableFuture edgeIdFuture = getEdgeIdByOriginatorId(tenantId, originatorId); Futures.transform(edgeIdFuture, edgeId -> { if (edgeId != null) { try { - pushEventToEdge(tenantId, edgeId, edgeQueueEntityType, tbMsg, callback); + pushEventToEdge(tenantId, edgeId, edgeEventType, tbMsg, callback); } catch (Exception e) { log.error("Failed to push event to edge, edgeId [{}], tbMsg [{}]", edgeId, tbMsg, e); } @@ -380,7 +396,6 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { MoreExecutors.directExecutor()); } - private ListenableFuture getEdgeIdByOriginatorId(TenantId tenantId, EntityId originatorId) { List originatorEdgeRelations = relationService.findByToAndType(tenantId, originatorId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE); if (originatorEdgeRelations != null && originatorEdgeRelations.size() > 0) { @@ -391,12 +406,12 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { } - private void pushEventToEdge(TenantId tenantId, EdgeId edgeId, EdgeQueueEntityType entityType, TbMsg tbMsg, FutureCallback callback) throws IOException { + private void pushEventToEdge(TenantId tenantId, EdgeId edgeId, EdgeEventType entityType, TbMsg tbMsg, FutureCallback callback) throws IOException { log.debug("Pushing event(s) to edge queue. tenantId [{}], edgeId [{}], entityType [{}], tbMsg [{}]", tenantId, edgeId, entityType, tbMsg); saveEventToEdgeQueue(tenantId, edgeId, entityType, tbMsg.getType(), tbMsg.getData(), callback); - if (entityType.equals(EdgeQueueEntityType.RULE_CHAIN)) { + if (entityType.equals(EdgeEventType.RULE_CHAIN)) { pushRuleChainMetadataToEdge(tenantId, edgeId, tbMsg, callback); } } @@ -408,7 +423,7 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { case DataConstants.ENTITY_UNASSIGNED_FROM_EDGE: case DataConstants.ENTITY_UPDATED: RuleChainMetaData ruleChainMetaData = ruleChainService.loadRuleChainMetaData(tenantId, ruleChain.getId()); - saveEventToEdgeQueue(tenantId, edgeId, EdgeQueueEntityType.RULE_CHAIN_METADATA, tbMsg.getType(), mapper.writeValueAsString(ruleChainMetaData), callback); + saveEventToEdgeQueue(tenantId, edgeId, EdgeEventType.RULE_CHAIN_METADATA, tbMsg.getType(), mapper.writeValueAsString(ruleChainMetaData), callback); break; default: log.warn("Unsupported msgType [{}], tbMsg [{}]", tbMsg.getType(), tbMsg); @@ -416,14 +431,14 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { } - private EdgeQueueEntityType getEdgeQueueTypeByEntityType(EntityType entityType) { + private EdgeEventType getEdgeQueueTypeByEntityType(EntityType entityType) { switch (entityType) { case DEVICE: - return EdgeQueueEntityType.DEVICE; + return EdgeEventType.DEVICE; case ASSET: - return EdgeQueueEntityType.ASSET; + return EdgeEventType.ASSET; case ENTITY_VIEW: - return EdgeQueueEntityType.ENTITY_VIEW; + return EdgeEventType.ENTITY_VIEW; default: log.info("Unsupported entity type: [{}]", entityType); return null; diff --git a/application/src/main/java/org/thingsboard/server/service/edge/EdgeNotificationService.java b/application/src/main/java/org/thingsboard/server/service/edge/EdgeNotificationService.java index f4de0f974e..6f35d4c32c 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/EdgeNotificationService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/EdgeNotificationService.java @@ -1,7 +1,23 @@ +/** + * Copyright © 2016-2020 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; import org.thingsboard.server.common.data.Event; import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; @@ -14,7 +30,7 @@ import java.io.IOException; public interface EdgeNotificationService { - TimePageData findQueueEvents(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink); + TimePageData findEdgeEvents(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink); Edge setEdgeRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) throws IOException; 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 1e77f438c5..aa48f0a370 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 @@ -33,14 +33,14 @@ import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityView; -import org.thingsboard.server.common.data.Event; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.alarm.AlarmStatus; 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.edge.EdgeQueueEntry; +import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EdgeId; @@ -63,7 +63,6 @@ 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.queue.TbMsgCallback; -import org.thingsboard.server.common.msg.session.SessionMsgType; import org.thingsboard.server.gen.edge.AlarmUpdateMsg; import org.thingsboard.server.gen.edge.ConnectRequestMsg; import org.thingsboard.server.gen.edge.ConnectResponseCode; @@ -169,36 +168,28 @@ public final class EdgeGrpcSession implements Closeable { void processHandleMessages() throws ExecutionException, InterruptedException { Long queueStartTs = getQueueStartTs().get(); TimePageLink pageLink = new TimePageLink(ctx.getEdgeEventStorageSettings().getMaxReadRecordsCount(), queueStartTs, null, true); - TimePageData pageData; + TimePageData pageData; UUID ifOffset = null; do { - pageData = ctx.getEdgeNotificationService().findQueueEvents(edge.getTenantId(), edge.getId(), pageLink); + pageData = ctx.getEdgeNotificationService().findEdgeEvents(edge.getTenantId(), edge.getId(), pageLink); if (isConnected() && !pageData.getData().isEmpty()) { log.trace("[{}] [{}] event(s) are going to be processed.", this.sessionId, pageData.getData().size()); - for (Event event : pageData.getData()) { - log.trace("[{}] Processing event [{}]", this.sessionId, event); + for (EdgeEvent edgeEvent : pageData.getData()) { + log.trace("[{}] Processing edge event [{}]", this.sessionId, edgeEvent); try { - EdgeQueueEntry entry = objectMapper.treeToValue(event.getBody(), EdgeQueueEntry.class); - UpdateMsgType msgType = getResponseMsgType(entry.getType()); - switch (msgType) { - case ENTITY_DELETED_RPC_MESSAGE: - case ENTITY_UPDATED_RPC_MESSAGE: - case ENTITY_CREATED_RPC_MESSAGE: - case ALARM_ACK_RPC_MESSAGE: - case ALARM_CLEAR_RPC_MESSAGE: - processEntityCRUDMessage(entry, msgType); - break; - case RULE_CHAIN_CUSTOM_MESSAGE: - processCustomDownlinkMessage(entry); - break; + UpdateMsgType msgType = getResponseMsgType(ActionType.valueOf(edgeEvent.getEdgeEventAction())); + if (msgType == null) { + processTelemetryMessage(edgeEvent); + } else { + processEntityCRUDMessage(edgeEvent, msgType); } if (ENTITY_CREATED_RPC_MESSAGE.equals(msgType)) { - pushEntityAttributesToEdge(entry); + pushEntityAttributesToEdge(edgeEvent); } } catch (Exception e) { log.error("Exception during processing records from queue", e); } - ifOffset = event.getUuidId(); + ifOffset = edgeEvent.getUuidId(); } } if (isConnected() && pageData.hasNext()) { @@ -222,10 +213,10 @@ public final class EdgeGrpcSession implements Closeable { } } - private void pushEntityAttributesToEdge(EdgeQueueEntry entry) throws IOException { + private void pushEntityAttributesToEdge(EdgeEvent edgeEvent) throws IOException { EntityId entityId = null; String entityName = null; - switch (entry.getEntityType()) { + switch (edgeEvent.getEdgeEventType()) { case EDGE: Edge edge = objectMapper.readValue(entry.getData(), Edge.class); entityId = edge.getId(); @@ -277,7 +268,7 @@ public final class EdgeGrpcSession implements Closeable { , objectMapper.writeValueAsString(entityNode)); log.debug("Sending donwlink entity data msg, entityName [{}], tbMsg [{}]", finalEntityName, tbMsg); outputStream.onNext(ResponseMsg.newBuilder() - .setDownlinkMsg(constructDownlinkEntityDataMsg(finalEntityName, finalEntityId, tbMsg)) + .setDownlinkMsg(constructEntityDataProtoMsg(finalEntityName, finalEntityId, tbMsg)) .build()); } catch (Exception e) { log.error("[{}] Failed to send attribute updates to the edge", edge.getName(), e); @@ -290,12 +281,12 @@ public final class EdgeGrpcSession implements Closeable { } } - private void processCustomDownlinkMessage(EdgeQueueEntry entry) throws IOException { - log.trace("Executing processCustomDownlinkMessage, entry [{}]", entry); + private void processTelemetryMessage(EdgeEvent edgeEvent) throws IOException { + log.trace("Executing processTelemetryMessage, edgeEvent [{}]", edgeEvent); TbMsg tbMsg = TbMsg.fromBytes(Base64.decodeBase64(entry.getData()), TbMsgCallback.EMPTY); String entityName = null; EntityId entityId = null; - switch (entry.getEntityType()) { + switch (edgeEvent.getEdgeEventType()) { case DEVICE: Device device = ctx.getDeviceService().findDeviceById(edge.getTenantId(), new DeviceId(tbMsg.getOriginator().getId())); entityName = device.getName(); @@ -316,14 +307,14 @@ public final class EdgeGrpcSession implements Closeable { if (entityName != null && entityId != null) { log.debug("Sending downlink entity data msg, entityName [{}], tbMsg [{}]", entityName, tbMsg); outputStream.onNext(ResponseMsg.newBuilder() - .setDownlinkMsg(constructDownlinkEntityDataMsg(entityName, entityId, tbMsg)) + .setDownlinkMsg(constructEntityDataProtoMsg(entityName, entityId, tbMsg)) .build()); } } - private void processEntityCRUDMessage(EdgeQueueEntry entry, UpdateMsgType msgType) throws java.io.IOException { - log.trace("Executing processEntityCRUDMessage, entry [{}], msgType [{}]", entry, msgType); - switch (entry.getEntityType()) { + private void processEntityCRUDMessage(EdgeEvent edgeEvent, UpdateMsgType msgType) throws java.io.IOException { + log.trace("Executing processEntityCRUDMessage, edgeEvent [{}], msgType [{}]", edgeEvent, msgType); + switch (edgeEvent.getEdgeEventType()) { case EDGE: Edge edge = objectMapper.readValue(entry.getData(), Edge.class); onEdgeUpdated(msgType, edge); @@ -476,33 +467,30 @@ public final class EdgeGrpcSession implements Closeable { .build()); } - private UpdateMsgType getResponseMsgType(String msgType) { - if (msgType.equals(SessionMsgType.POST_TELEMETRY_REQUEST.name()) || - msgType.equals(SessionMsgType.POST_ATTRIBUTES_REQUEST.name()) || - msgType.equals(DataConstants.ATTRIBUTES_UPDATED) || - msgType.equals(DataConstants.ATTRIBUTES_DELETED)) { - return UpdateMsgType.RULE_CHAIN_CUSTOM_MESSAGE; - } else { - switch (msgType) { - case DataConstants.ENTITY_UPDATED: - return UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE; - case DataConstants.ENTITY_CREATED: - case DataConstants.ENTITY_ASSIGNED_TO_EDGE: - return ENTITY_CREATED_RPC_MESSAGE; - case DataConstants.ENTITY_DELETED: - case DataConstants.ENTITY_UNASSIGNED_FROM_EDGE: - return UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE; - case DataConstants.ALARM_ACK: - return UpdateMsgType.ALARM_ACK_RPC_MESSAGE; - case DataConstants.ALARM_CLEAR: - return UpdateMsgType.ALARM_CLEAR_RPC_MESSAGE; - default: - throw new RuntimeException("Unsupported msgType [" + msgType + "]"); - } + private UpdateMsgType getResponseMsgType(ActionType actionType) { + switch (actionType) { + case ADDED: + return UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE; + case UPDATED: + case ASSIGNED_TO_EDGE: + return ENTITY_CREATED_RPC_MESSAGE; + case DELETED: + case UNASSIGNED_FROM_EDGE: + return UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE; + case ALARM_ACK: + return UpdateMsgType.ALARM_ACK_RPC_MESSAGE; + case ALARM_CLEAR: + return UpdateMsgType.ALARM_CLEAR_RPC_MESSAGE; + case ATTRIBUTES_UPDATED: + case ATTRIBUTES_DELETED: + case TIMESERIES_DELETED: + return null; + default: + throw new RuntimeException("Unsupported actionType [" + actionType + "]"); } } - private DownlinkMsg constructDownlinkEntityDataMsg(String entityName, EntityId entityId, TbMsg tbMsg) { + private DownlinkMsg constructEntityDataProtoMsg(String entityName, EntityId entityId, TbMsg tbMsg) { EntityDataProto entityData = EntityDataProto.newBuilder() .setEntityName(entityName) .setTbMsg(ByteString.copyFrom(TbMsg.toByteArray(tbMsg))) 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 f9c5e53fd5..eda9cf88cb 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 @@ -239,7 +239,7 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.6.0", SCHEMA_UPDATE_SQL); loadSql(schemaUpdateFile, conn); try { - conn.createStatement().execute("ALTER TABLE rule_chain ADD type varchar(255) DEFAULT 'SYSTEM'"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script + conn.createStatement().execute("ALTER TABLE rule_chain ADD type varchar(255) DEFAULT 'CORE'"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script } catch (Exception e) {} log.info("Schema updated."); } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeEventService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeEventService.java new file mode 100644 index 0000000000..6c3e189d41 --- /dev/null +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeEventService.java @@ -0,0 +1,35 @@ +/** + * Copyright © 2016-2020 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.edge; + +import com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.edge.EdgeEventType; +import org.thingsboard.server.common.data.id.EdgeId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.TimePageData; +import org.thingsboard.server.common.data.page.TimePageLink; + +public interface EdgeEventService { + + EdgeEventType getEdgeEventTypeByEntityType(EntityType entityType); + + ListenableFuture saveAsync(EdgeEvent edgeEvent); + + TimePageData findEdgeEvents(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink); + +} diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java index 496d6a2425..60c6b24918 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.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/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 4dd9bb1076..01b9e86d25 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 @@ -65,7 +65,4 @@ public class DataConstants { public static final String DEFAULT_SECRET_KEY = ""; public static final String SECRET_KEY_FIELD_NAME = "secretKey"; public static final String DURATION_MS_FIELD_NAME = "durationMs"; - - public static final String EDGE_QUEUE_EVENT_TYPE = "EDGE_QUEUE"; - } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/audit/ActionType.java b/common/data/src/main/java/org/thingsboard/server/common/data/audit/ActionType.java index 7e78a03bb4..48de5fedb6 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/audit/ActionType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/audit/ActionType.java @@ -24,6 +24,7 @@ public enum ActionType { UPDATED(false), // log entity ATTRIBUTES_UPDATED(false), // log attributes/values ATTRIBUTES_DELETED(false), // log attributes + TIMESERIES_UPDATED(false), // log timeseries TIMESERIES_DELETED(false), // log timeseries RPC_CALL(false), // log method and params CREDENTIALS_UPDATED(false), // log new credentials 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 new file mode 100644 index 0000000000..1c9ea9343c --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEvent.java @@ -0,0 +1,50 @@ +/** + * Copyright © 2016-2020 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.edge; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Data; +import org.thingsboard.server.common.data.BaseData; +import org.thingsboard.server.common.data.id.EdgeEventId; +import org.thingsboard.server.common.data.id.EdgeId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; + +import java.util.UUID; + +@Data +public class EdgeEvent extends BaseData { + + private TenantId tenantId; + private EdgeId edgeId; + private String edgeEventAction; + private UUID entityId; + private EdgeEventType edgeEventType; + private transient JsonNode entityBody; + + public EdgeEvent() { + super(); + } + + public EdgeEvent(EdgeEventId id) { + super(id); + } + + public EdgeEvent(EdgeEvent event) { + super(event); + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeQueueEntityType.java b/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEventType.java similarity index 95% rename from common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeQueueEntityType.java rename to common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEventType.java index 7ba316a529..ada50c9998 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeQueueEntityType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEventType.java @@ -15,6 +15,6 @@ */ package org.thingsboard.server.common.data.edge; -public enum EdgeQueueEntityType { +public enum EdgeEventType { DASHBOARD, ASSET, DEVICE, ENTITY_VIEW, ALARM, RULE_CHAIN, RULE_CHAIN_METADATA, EDGE, USER, CUSTOMER, RELATION } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/EdgeEventId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/EdgeEventId.java new file mode 100644 index 0000000000..705c46e2fa --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/EdgeEventId.java @@ -0,0 +1,35 @@ +/** + * Copyright © 2016-2020 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.id; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.UUID; + +public class EdgeEventId extends UUIDBased { + + private static final long serialVersionUID = 1L; + + @JsonCreator + public EdgeEventId(@JsonProperty("id") UUID id) { + super(id); + } + + public static EdgeEventId fromString(String edgeEventId) { + return new EdgeEventId(UUID.fromString(edgeEventId)); + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChainType.java b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChainType.java index d6a806f95f..9dc3dc4e5e 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChainType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChainType.java @@ -16,5 +16,5 @@ package org.thingsboard.server.common.data.rule; public enum RuleChainType { - SYSTEM, EDGE + CORE, EDGE } diff --git a/common/edge-api/pom.xml b/common/edge-api/pom.xml index dfd42240c4..53b7d62472 100644 --- a/common/edge-api/pom.xml +++ b/common/edge-api/pom.xml @@ -40,6 +40,10 @@ org.thingsboard.common data
+ + org.thingsboard.common + queue + org.thingsboard.common message diff --git a/common/edge-api/src/main/proto/edge.proto b/common/edge-api/src/main/proto/edge.proto index 4a941b279e..787de743be 100644 --- a/common/edge-api/src/main/proto/edge.proto +++ b/common/edge-api/src/main/proto/edge.proto @@ -19,6 +19,8 @@ option java_package = "org.thingsboard.server.gen.edge"; option java_multiple_files = true; option java_outer_classname = "EdgeProtos"; +import "queue.proto"; + package edge; // Interface exported by the ThingsBoard Edge Transport. @@ -94,15 +96,16 @@ enum UpdateMsgType { ENTITY_DELETED_RPC_MESSAGE = 2; ALARM_ACK_RPC_MESSAGE = 3; ALARM_CLEAR_RPC_MESSAGE = 4; - RULE_CHAIN_CUSTOM_MESSAGE = 5; - DEVICE_CONFLICT_RPC_MESSAGE = 6; + DEVICE_CONFLICT_RPC_MESSAGE = 5; } message EntityDataProto { string entityName = 1; int64 entityIdMSB = 2; int64 entityIdLSB = 3; - bytes tbMsg = 4; + transport.PostTelemetryMsg postTelemetryMsg = 4; + transport.PostAttributeMsg postAttributesMsg = 5; + // transport.ToDeviceRpcRequestMsg ??? } message RuleChainUpdateMsg { diff --git a/common/queue/src/main/proto/queue.proto b/common/queue/src/main/proto/queue.proto index ab0193318a..54a8148fbe 100644 --- a/common/queue/src/main/proto/queue.proto +++ b/common/queue/src/main/proto/queue.proto @@ -356,6 +356,14 @@ message FromDeviceRPCResponseProto { message EdgeNotificationMsgProto { int64 tenantIdMSB = 1; int64 tenantIdLSB = 2; + string edgeEventType = 3; + string edgeEventAction = 4; + int64 entityIdMSB = 5; + int64 entityIdLSB = 6; + string entityType = 7; + string entityBody = 8; + PostTelemetryMsg postTelemetryMsg = 9; + PostAttributeMsg postAttributesMsg = 10; } /** diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeEventService.java b/dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeEventService.java new file mode 100644 index 0000000000..134ffe3b06 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeEventService.java @@ -0,0 +1,85 @@ +/** + * Copyright © 2016-2020 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.edge; + +import com.google.common.util.concurrent.ListenableFuture; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.edge.EdgeEventType; +import org.thingsboard.server.common.data.id.EdgeId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.TimePageData; +import org.thingsboard.server.common.data.page.TimePageLink; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.service.DataValidator; + +import java.util.List; + +@Service +@Slf4j +public class BaseEdgeEventService implements EdgeEventService { + + @Autowired + public EdgeEventDao edgeEventDao; + + @Override + public EdgeEventType getEdgeEventTypeByEntityType(EntityType entityType) { + switch (entityType) { + case DEVICE: + return EdgeEventType.DEVICE; + case ASSET: + return EdgeEventType.ASSET; + case ENTITY_VIEW: + return EdgeEventType.ENTITY_VIEW; + case DASHBOARD: + return EdgeEventType.DASHBOARD; + case USER: + return EdgeEventType.USER; + default: + log.warn("Failed to push notification to edge service. Unsupported entity type [{}]", entityType); + return null; + } + } + + @Override + public ListenableFuture saveAsync(EdgeEvent edgeEvent) { + edgeEventValidator.validate(edgeEvent, EdgeEvent::getTenantId); + return edgeEventDao.saveAsync(edgeEvent); + } + + @Override + public TimePageData findEdgeEvents(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink) { + List events = edgeEventDao.findEdgeEvents(tenantId.getId(), edgeId, pageLink); + return new TimePageData<>(events, pageLink); + } + + private DataValidator edgeEventValidator = + new DataValidator() { + @Override + protected void validateDataImpl(TenantId tenantId, EdgeEvent edgeEvent) { + if (edgeEvent.getEdgeId() == null) { + throw new DataValidationException("Edge id should be specified!"); + } + if (StringUtils.isEmpty(edgeEvent.getEdgeEventAction())) { + throw new DataValidationException("Edge Event action should be specified!"); + } + } + }; +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeEventDao.java b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeEventDao.java new file mode 100644 index 0000000000..426bada645 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeEventDao.java @@ -0,0 +1,51 @@ +/** + * Copyright © 2016-2020 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.edge; + +import com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.id.EdgeId; +import org.thingsboard.server.common.data.page.TimePageLink; +import org.thingsboard.server.dao.Dao; + +import java.util.List; +import java.util.UUID; + +/** + * The Interface EdgeEventDao. + */ +public interface EdgeEventDao extends Dao { + + /** + * Save or update edge event object async + * + * @param edgeEvent the event object + * @return saved edge event object future + */ + ListenableFuture saveAsync(EdgeEvent edgeEvent); + + + /** + * Find edge events by tenantId, edgeId and pageLink. + * + * @param tenantId the tenantId + * @param edgeId the edgeId + * @param pageLink the pageLink + * @return the event list + */ + List findEdgeEvents(UUID tenantId, EdgeId edgeId, TimePageLink pageLink); + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java index 48e8912225..39e0513637 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.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/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index dcdf9460bf..5a7b5ce45f 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 @@ -372,6 +372,16 @@ public class ModelConstants { public static final String EDGE_ROUTING_KEY_PROPERTY = "routing_key"; public static final String EDGE_SECRET_PROPERTY = "secret"; + /** + * Cassandra edge queue constants. + */ + public static final String EDGE_EVENT_COLUMN_FAMILY_NAME = "edge_event"; + public static final String EDGE_EVENT_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; + public static final String EDGE_EVENT_EDGE_ID_PROPERTY = "edge_id"; + public static final String EDGE_EVENT_TYPE_PROPERTY = "edge_event_type"; + public static final String EDGE_EVENT_ACTION_PROPERTY = "edge_event_action"; + public static final String EDGE_EVENT_ENTITY_ID_PROPERTY = "entity_id"; + public static final String EDGE_EVENT_ENTITY_BODY_PROPERTY = "entity_body"; /** * Cassandra attributes and timeseries constants. diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/EdgeEventEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/EdgeEventEntity.java new file mode 100644 index 0000000000..8a3ed1b437 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/EdgeEventEntity.java @@ -0,0 +1,135 @@ +/** + * Copyright © 2016-2020 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.model.nosql; + +import com.datastax.driver.core.utils.UUIDs; +import com.datastax.driver.mapping.annotations.ClusteringColumn; +import com.datastax.driver.mapping.annotations.Column; +import com.datastax.driver.mapping.annotations.PartitionKey; +import com.datastax.driver.mapping.annotations.Table; +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.Event; +import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.edge.EdgeEventType; +import org.thingsboard.server.common.data.id.EdgeEventId; +import org.thingsboard.server.common.data.id.EdgeId; +import org.thingsboard.server.common.data.id.EntityIdFactory; +import org.thingsboard.server.common.data.id.EventId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.model.BaseEntity; +import org.thingsboard.server.dao.model.type.EdgeEventTypeCodec; +import org.thingsboard.server.dao.model.type.EntityTypeCodec; +import org.thingsboard.server.dao.model.type.JsonCodec; + +import java.util.UUID; + +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_EVENT_ACTION_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_EVENT_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_EVENT_EDGE_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_EVENT_ENTITY_BODY_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_EVENT_ENTITY_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_EVENT_TENANT_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_EVENT_TYPE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.EVENT_BODY_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.EVENT_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.EVENT_ENTITY_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.EVENT_ENTITY_TYPE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.EVENT_TENANT_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.EVENT_TYPE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.EVENT_UID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY; + +@Data +@NoArgsConstructor +@Table(name = EDGE_EVENT_COLUMN_FAMILY_NAME) +public class EdgeEventEntity implements BaseEntity { + + @Column(name = ID_PROPERTY) + private UUID id; + + @PartitionKey() + @Column(name = EDGE_EVENT_TENANT_ID_PROPERTY) + private UUID tenantId; + + @PartitionKey(value = 1) + @Column(name = EDGE_EVENT_EDGE_ID_PROPERTY) + private UUID edgeId; + + @PartitionKey(value = 2) + @Column(name = EDGE_EVENT_TYPE_PROPERTY, codec = EdgeEventTypeCodec.class) + private EdgeEventType edgeEventType; + + @PartitionKey(value = 3) + @Column(name = EDGE_EVENT_ENTITY_ID_PROPERTY) + private UUID entityId; + + @ClusteringColumn() + @Column(name = EDGE_EVENT_ACTION_PROPERTY) + private String edgeEventAction; + + // TODO + @ClusteringColumn(value = 1) + @Column(name = EVENT_UID_PROPERTY) + private String eventUid; + + @Column(name = EDGE_EVENT_ENTITY_BODY_PROPERTY, codec = JsonCodec.class) + private JsonNode entityBody; + + public EdgeEventEntity(EdgeEvent edgeEvent) { + if (edgeEvent.getId() != null) { + this.id = edgeEvent.getId().getId(); + } + if (edgeEvent.getTenantId() != null) { + this.tenantId = edgeEvent.getTenantId().getId(); + } + if (edgeEvent.getEdgeId() != null) { + this.edgeId = edgeEvent.getEdgeId().getId(); + } +// if (event.getEntityId() != null) { +// this.entityType = event.getEntityId().getEntityType(); +// this.entityId = event.getEntityId().getId(); +// } +// this.edgeEventType = edgeEvent.getEdgeEventType(); +// this.edgeEventAction = edgeEvent.getEdgeEventAction(); +// this.entityBody = edgeEvent.getEntityBody(); + } + + @Override + public UUID getUuid() { + return id; + } + + @Override + public void setUuid(UUID id) { + this.id = id; + } + + @Override + public EdgeEvent toData() { + EdgeEvent edgeEvent = new EdgeEvent(new EdgeEventId(id)); +// edgeEvent.setCreatedTime(UUIDs.unixTimestamp(id)); +// edgeEvent.setTenantId(new TenantId(tenantId)); +// edgeEvent.setEdgeId(new EdgeId(edgeId)); +// edgeEvent.setEntityId(entityId); +// event.setBody(body); +// event.setType(eventType); +// event.setUid(eventUid); + return edgeEvent; + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/EdgeEventEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/EdgeEventEntity.java new file mode 100644 index 0000000000..9c6dca59ef --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/EdgeEventEntity.java @@ -0,0 +1,121 @@ +/** + * Copyright © 2016-2020 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.model.sql; + +import com.datastax.driver.core.utils.UUIDs; +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.Type; +import org.hibernate.annotations.TypeDef; +import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.edge.EdgeEventType; +import org.thingsboard.server.common.data.id.EdgeEventId; +import org.thingsboard.server.common.data.id.EdgeId; +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.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.Table; +import java.util.UUID; + +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_EVENT_ACTION_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_EVENT_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_EVENT_EDGE_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_EVENT_ENTITY_BODY_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_EVENT_ENTITY_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_EVENT_TENANT_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_EVENT_TYPE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.EPOCH_DIFF; +import static org.thingsboard.server.dao.model.ModelConstants.TS_COLUMN; + +@Data +@EqualsAndHashCode(callSuper = true) +@Entity +@TypeDef(name = "json", typeClass = JsonStringType.class) +@Table(name = EDGE_EVENT_COLUMN_FAMILY_NAME) +@NoArgsConstructor +public class EdgeEventEntity extends BaseSqlEntity implements BaseEntity { + + @Column(name = EDGE_EVENT_TENANT_ID_PROPERTY) + private String tenantId; + + @Column(name = EDGE_EVENT_EDGE_ID_PROPERTY) + private String edgeId; + + @Column(name = EDGE_EVENT_ENTITY_ID_PROPERTY) + private String entityId; + + @Enumerated(EnumType.STRING) + @Column(name = EDGE_EVENT_TYPE_PROPERTY) + private EdgeEventType edgeEventType; + + @Column(name = EDGE_EVENT_ACTION_PROPERTY) + private String edgeEventAction; + + @Type(type = "json") + @Column(name = EDGE_EVENT_ENTITY_BODY_PROPERTY) + private JsonNode entityBody; + + @Column(name = TS_COLUMN) + private long ts; + + public EdgeEventEntity(EdgeEvent edgeEvent) { + if (edgeEvent.getId() != null) { + this.setUuid(edgeEvent.getId().getId()); + this.ts = getTs(edgeEvent.getId().getId()); + } else { + this.ts = System.currentTimeMillis(); + } + if (edgeEvent.getTenantId() != null) { + this.tenantId = toString(edgeEvent.getTenantId().getId()); + } + if (edgeEvent.getEdgeId() != null) { + this.edgeId = toString(edgeEvent.getEdgeId().getId()); + } + if (edgeEvent.getEntityId() != null) { + this.entityId = toString(edgeEvent.getEntityId()); + } + this.edgeEventType = edgeEvent.getEdgeEventType(); + this.edgeEventAction = edgeEvent.getEdgeEventAction(); + this.entityBody = edgeEvent.getEntityBody(); + } + + @Override + public EdgeEvent toData() { + EdgeEvent edgeEvent = new EdgeEvent(new EdgeEventId(this.getUuid())); + edgeEvent.setCreatedTime(UUIDs.unixTimestamp(this.getUuid())); + edgeEvent.setTenantId(new TenantId(toUUID(tenantId))); + edgeEvent.setEdgeId(new EdgeId(toUUID(edgeId))); + if (entityId != null) { + edgeEvent.setEntityId(toUUID(entityId)); + } + edgeEvent.setEdgeEventType(edgeEventType); + edgeEvent.setEdgeEventAction(edgeEventAction); + edgeEvent.setEntityBody(entityBody); + return edgeEvent; + } + + private static long getTs(UUID uuid) { + return (uuid.timestamp() - EPOCH_DIFF) / 10000; + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/type/EdgeEventTypeCodec.java b/dao/src/main/java/org/thingsboard/server/dao/model/type/EdgeEventTypeCodec.java new file mode 100644 index 0000000000..4a32c3a9ab --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/type/EdgeEventTypeCodec.java @@ -0,0 +1,27 @@ +/** + * Copyright © 2016-2020 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.model.type; + +import com.datastax.driver.extras.codecs.enums.EnumNameCodec; +import org.thingsboard.server.common.data.edge.EdgeEventType; + +public class EdgeEventTypeCodec extends EnumNameCodec { + + public EdgeEventTypeCodec() { + super(EdgeEventType.class); + } + +} 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 d0c37903a8..592f20f4f2 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 @@ -289,7 +289,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC @Override public RuleChain getRootTenantRuleChain(TenantId tenantId) { - return getRootRuleChainByType(tenantId, RuleChainType.SYSTEM); + return getRootRuleChainByType(tenantId, RuleChainType.CORE); } private RuleChain getRootRuleChainByType(TenantId tenantId, RuleChainType type) { @@ -566,7 +566,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC throw new DataValidationException("Rule chain name should be specified!"); } if (ruleChain.getType() == null) { - ruleChain.setType(RuleChainType.SYSTEM); + ruleChain.setType(RuleChainType.CORE); } if (ruleChain.getTenantId() == null || ruleChain.getTenantId().isNullUid()) { throw new DataValidationException("Rule chain should be assigned to tenant!"); @@ -575,7 +575,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC if (tenant == null) { throw new DataValidationException("Rule chain is referencing to non-existent tenant!"); } - if (ruleChain.isRoot() && RuleChainType.SYSTEM.equals(ruleChain.getType())) { + if (ruleChain.isRoot() && RuleChainType.CORE.equals(ruleChain.getType())) { RuleChain rootRuleChain = getRootTenantRuleChain(ruleChain.getTenantId()); if (rootRuleChain != null && !rootRuleChain.getId().equals(ruleChain.getId())) { throw new DataValidationException("Another root rule chain is present in scope of current tenant!"); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/EdgeEventRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/EdgeEventRepository.java new file mode 100644 index 0000000000..9a19cfd4b8 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/EdgeEventRepository.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2020 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.sql.edge; + +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.repository.CrudRepository; +import org.thingsboard.server.dao.model.sql.EdgeEventEntity; +import org.thingsboard.server.dao.util.SqlDao; + +@SqlDao +public interface EdgeEventRepository extends CrudRepository, JpaSpecificationExecutor { + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaBaseEdgeEventDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaBaseEdgeEventDao.java new file mode 100644 index 0000000000..11a4a1dc64 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaBaseEdgeEventDao.java @@ -0,0 +1,112 @@ +/** + * Copyright © 2016-2020 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.sql.edge; + +import com.datastax.driver.core.utils.UUIDs; +import com.google.common.util.concurrent.ListenableFuture; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.UUIDConverter; +import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.id.EdgeEventId; +import org.thingsboard.server.common.data.id.EdgeId; +import org.thingsboard.server.common.data.page.TimePageLink; +import org.thingsboard.server.dao.DaoUtil; +import org.thingsboard.server.dao.edge.EdgeEventDao; +import org.thingsboard.server.dao.model.sql.EdgeEventEntity; +import org.thingsboard.server.dao.sql.JpaAbstractSearchTimeDao; +import org.thingsboard.server.dao.util.SqlDao; + +import javax.persistence.criteria.Predicate; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; + +@Slf4j +@Component +@SqlDao +public class JpaBaseEdgeEventDao extends JpaAbstractSearchTimeDao implements EdgeEventDao { + + private final UUID systemTenantId = NULL_UUID; + + @Autowired + private EdgeEventRepository edgeEventRepository; + + @Override + protected Class getEntityClass() { + return EdgeEventEntity.class; + } + + @Override + protected CrudRepository getCrudRepository() { + return edgeEventRepository; + } + + @Override + public ListenableFuture saveAsync(EdgeEvent edgeEvent) { + log.debug("Save edge event [{}] ", edgeEvent); + if (edgeEvent.getId() == null) { + edgeEvent.setId(new EdgeEventId(UUIDs.timeBased())); + } + return service.submit(() -> save(new EdgeEventEntity(edgeEvent)).orElse(null)); + } + + @Override + public List findEdgeEvents(UUID tenantId, EdgeId edgeId, TimePageLink pageLink) { + Specification timeSearchSpec = JpaAbstractSearchTimeDao.getTimeSearchPageSpec(pageLink, "id"); + Specification fieldsSpec = getEntityFieldsSpec(tenantId, edgeId); + Sort.Direction sortDirection = pageLink.isAscOrder() ? Sort.Direction.ASC : Sort.Direction.DESC; + Pageable pageable = PageRequest.of(0, pageLink.getLimit(), sortDirection, ID_PROPERTY); + return DaoUtil.convertDataList(edgeEventRepository.findAll(Specification.where(timeSearchSpec).and(fieldsSpec), pageable).getContent()); + } + + public Optional save(EdgeEventEntity entity) { + log.debug("Save edge event [{}] ", entity); + if (entity.getTenantId() == null) { + log.trace("Save system edge event with predefined id {}", systemTenantId); + entity.setTenantId(UUIDConverter.fromTimeUUID(systemTenantId)); + } + if (entity.getUuid() == null) { + entity.setUuid(UUIDs.timeBased()); + } + return Optional.of(DaoUtil.getData(edgeEventRepository.save(entity))); + } + + private Specification getEntityFieldsSpec(UUID tenantId, EdgeId edgeId) { + return (root, criteriaQuery, criteriaBuilder) -> { + List predicates = new ArrayList<>(); + if (tenantId != null) { + Predicate tenantIdPredicate = criteriaBuilder.equal(root.get("tenantId"), UUIDConverter.fromTimeUUID(tenantId)); + predicates.add(tenantIdPredicate); + } + if (edgeId != null) { + Predicate entityIdPredicate = criteriaBuilder.equal(root.get("edgeId"), UUIDConverter.fromTimeUUID(edgeId.getId())); + predicates.add(entityIdPredicate); + } + return criteriaBuilder.and(predicates.toArray(new Predicate[]{})); + }; + } +} diff --git a/dao/src/main/resources/sql/schema-entities-hsql.sql b/dao/src/main/resources/sql/schema-entities-hsql.sql index cd8eae9e34..53042215cd 100644 --- a/dao/src/main/resources/sql/schema-entities-hsql.sql +++ b/dao/src/main/resources/sql/schema-entities-hsql.sql @@ -270,3 +270,14 @@ CREATE TABLE IF NOT EXISTS edge ( CONSTRAINT edge_name_unq_key UNIQUE (tenant_id, name), CONSTRAINT edge_routing_key_unq_key UNIQUE (routing_key) ); + +CREATE TABLE IF NOT EXISTS edge_event ( + id varchar(31) NOT NULL CONSTRAINT edge_event_pkey PRIMARY KEY, + edge_id varchar(31), + edge_event_type varchar(255), + entity_id varchar(31), + edge_event_action varchar(255), + entity_body varchar(10000000), + tenant_id varchar(31), + ts bigint NOT NULL +); \ No newline at end of file diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index de526788d3..606dd0c8a7 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -271,6 +271,18 @@ CREATE TABLE IF NOT EXISTS edge ( CONSTRAINT edge_routing_key_unq_key UNIQUE (routing_key) ); +CREATE TABLE IF NOT EXISTS edge_event ( + id varchar(31) NOT NULL CONSTRAINT edge_event_pkey PRIMARY KEY, + edge_id varchar(31), + edge_event_type varchar(255), + entity_id varchar(31), + edge_event_action varchar(255), + entity_body varchar(10000000), + tenant_id varchar(31), + ts bigint NOT NULL +); + + CREATE OR REPLACE PROCEDURE cleanup_events_by_ttl(IN ttl bigint, IN debug_ttl bigint, INOUT deleted bigint) LANGUAGE plpgsql AS $$ diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java index 3311b49388..adb986cb03 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java @@ -45,6 +45,7 @@ import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.device.DeviceCredentialsService; import org.thingsboard.server.dao.device.DeviceService; +import org.thingsboard.server.dao.edge.EdgeEventService; import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.event.EventService; @@ -125,6 +126,9 @@ public abstract class AbstractServiceTest { @Autowired protected EdgeService edgeService; + @Autowired + protected EdgeEventService edgeEventService; + @Autowired private ComponentDescriptorService componentDescriptorService; diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseEdgeEventServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseEdgeEventServiceTest.java new file mode 100644 index 0000000000..f4d622daa5 --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseEdgeEventServiceTest.java @@ -0,0 +1,108 @@ +/** + * Copyright © 2016-2020 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.service; + +import com.datastax.driver.core.utils.UUIDs; +import org.junit.Assert; +import org.junit.Test; +import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.edge.EdgeEventType; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.EdgeEventId; +import org.thingsboard.server.common.data.id.EdgeId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.TimePageData; +import org.thingsboard.server.common.data.page.TimePageLink; + +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.Month; +import java.time.ZoneOffset; + +public abstract class BaseEdgeEventServiceTest extends AbstractServiceTest { + + @Test + public void saveEdgeEvent() throws Exception { + EdgeId edgeId = new EdgeId(UUIDs.timeBased()); + DeviceId deviceId = new DeviceId(UUIDs.timeBased()); + EdgeEvent edgeEvent = generateEdgeEvent(null, edgeId, deviceId, DataConstants.ENTITY_CREATED); + EdgeEvent saved = edgeEventService.saveAsync(edgeEvent).get(); + Assert.assertEquals(saved.getTenantId(), edgeEvent.getTenantId()); + Assert.assertEquals(saved.getEdgeId(), edgeEvent.getEdgeId()); + Assert.assertEquals(saved.getEntityId(), edgeEvent.getEntityId()); + Assert.assertEquals(saved.getEdgeEventType(), edgeEvent.getEdgeEventType()); + Assert.assertEquals(saved.getEdgeEventAction(), edgeEvent.getEdgeEventAction()); + Assert.assertEquals(saved.getEntityBody(), edgeEvent.getEntityBody()); + } + + protected EdgeEvent generateEdgeEvent(TenantId tenantId, EdgeId edgeId, EntityId entityId, String edgeEventAction) throws IOException { + if (tenantId == null) { + tenantId = new TenantId(UUIDs.timeBased()); + } + EdgeEvent edgeEvent = new EdgeEvent(); + edgeEvent.setTenantId(tenantId); + edgeEvent.setEdgeId(edgeId); + edgeEvent.setEntityId(entityId.getId()); + edgeEvent.setEdgeEventType(EdgeEventType.DEVICE); + edgeEvent.setEdgeEventAction(edgeEventAction); + edgeEvent.setEntityBody(readFromResource("TestJsonData.json")); + return edgeEvent; + } + + + @Test + public void findEdgeEventsByTimeDescOrder() throws Exception { + long timeBeforeStartTime = LocalDateTime.of(2020, Month.NOVEMBER, 1, 11, 30).toEpochSecond(ZoneOffset.UTC); + long startTime = LocalDateTime.of(2020, Month.NOVEMBER, 1, 12, 0).toEpochSecond(ZoneOffset.UTC); + long eventTime = LocalDateTime.of(2020, Month.NOVEMBER, 1, 12, 30).toEpochSecond(ZoneOffset.UTC); + long endTime = LocalDateTime.of(2020, Month.NOVEMBER, 1, 13, 0).toEpochSecond(ZoneOffset.UTC); + long timeAfterEndTime = LocalDateTime.of(2020, Month.NOVEMBER, 1, 13, 30).toEpochSecond(ZoneOffset.UTC); + + EdgeId edgeId = new EdgeId(UUIDs.timeBased()); + DeviceId deviceId = new DeviceId(UUIDs.timeBased()); + TenantId tenantId = new TenantId(UUIDs.timeBased()); + saveEdgeEventWithProvidedTime(timeBeforeStartTime, edgeId, deviceId, tenantId); + EdgeEvent savedEdgeEvent = saveEdgeEventWithProvidedTime(eventTime, edgeId, deviceId, tenantId); + EdgeEvent savedEdgeEvent2 = saveEdgeEventWithProvidedTime(eventTime + 1, edgeId, deviceId, tenantId); + EdgeEvent savedEdgeEvent3 = saveEdgeEventWithProvidedTime(eventTime + 2, edgeId, deviceId, tenantId); + saveEdgeEventWithProvidedTime(timeAfterEndTime, edgeId, deviceId, tenantId); + + TimePageData edgeEvents = edgeEventService.findEdgeEvents(tenantId, edgeId, new TimePageLink(2, startTime, endTime, false)); + + Assert.assertNotNull(edgeEvents.getData()); + Assert.assertTrue(edgeEvents.getData().size() == 2); + Assert.assertTrue(edgeEvents.getData().get(0).getUuidId().equals(savedEdgeEvent3.getUuidId())); + Assert.assertTrue(edgeEvents.getData().get(1).getUuidId().equals(savedEdgeEvent2.getUuidId())); + Assert.assertTrue(edgeEvents.hasNext()); + Assert.assertNotNull(edgeEvents.getNextPageLink()); + + edgeEvents = edgeEventService.findEdgeEvents(tenantId, edgeId, edgeEvents.getNextPageLink()); + + Assert.assertNotNull(edgeEvents.getData()); + Assert.assertTrue(edgeEvents.getData().size() == 1); + Assert.assertTrue(edgeEvents.getData().get(0).getUuidId().equals(savedEdgeEvent.getUuidId())); + Assert.assertFalse(edgeEvents.hasNext()); + Assert.assertNull(edgeEvents.getNextPageLink()); + } + + private EdgeEvent saveEdgeEventWithProvidedTime(long time, EdgeId edgeId, EntityId entityId, TenantId tenantId) throws Exception { + EdgeEvent edgeEvent = generateEdgeEvent(tenantId, edgeId, entityId, DataConstants.ENTITY_CREATED); + edgeEvent.setId(new EdgeEventId(UUIDs.startOf(time))); + return edgeEventService.saveAsync(edgeEvent).get(); + } +} \ No newline at end of file diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/EdgeServiceImplTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseEdgeServiceTest.java similarity index 99% rename from dao/src/test/java/org/thingsboard/server/dao/service/EdgeServiceImplTest.java rename to dao/src/test/java/org/thingsboard/server/dao/service/BaseEdgeServiceTest.java index 7dbedce770..7d31ff4236 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/EdgeServiceImplTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseEdgeServiceTest.java @@ -37,7 +37,7 @@ import java.util.List; import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; -public abstract class EdgeServiceImplTest extends AbstractServiceTest { +public abstract class BaseEdgeServiceTest extends AbstractServiceTest { private IdComparator idComparator = new IdComparator<>(); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseRuleChainServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseRuleChainServiceTest.java index b7ecc1f784..2ab0fa4067 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseRuleChainServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseRuleChainServiceTest.java @@ -145,7 +145,7 @@ public abstract class BaseRuleChainServiceTest extends AbstractServiceTest { TextPageLink pageLink = new TextPageLink(16); TextPageData pageData = null; do { - pageData = ruleChainService.findTenantRuleChainsByType(tenantId, RuleChainType.SYSTEM, pageLink); + pageData = ruleChainService.findTenantRuleChainsByType(tenantId, RuleChainType.CORE, pageLink); loadedRuleChains.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageData.getNextPageLink(); @@ -160,7 +160,7 @@ public abstract class BaseRuleChainServiceTest extends AbstractServiceTest { ruleChainService.deleteRuleChainsByTenantId(tenantId); pageLink = new TextPageLink(31); - pageData = ruleChainService.findTenantRuleChainsByType(tenantId, RuleChainType.SYSTEM, pageLink); + pageData = ruleChainService.findTenantRuleChainsByType(tenantId, RuleChainType.CORE, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertTrue(pageData.getData().isEmpty()); @@ -196,7 +196,7 @@ public abstract class BaseRuleChainServiceTest extends AbstractServiceTest { TextPageLink pageLink = new TextPageLink(19, name1); TextPageData pageData = null; do { - pageData = ruleChainService.findTenantRuleChainsByType(tenantId, RuleChainType.SYSTEM, pageLink); + pageData = ruleChainService.findTenantRuleChainsByType(tenantId, RuleChainType.CORE, pageLink); loadedRuleChainsName1.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageData.getNextPageLink(); @@ -211,7 +211,7 @@ public abstract class BaseRuleChainServiceTest extends AbstractServiceTest { List loadedRuleChainsName2 = new ArrayList<>(); pageLink = new TextPageLink(4, name2); do { - pageData = ruleChainService.findTenantRuleChainsByType(tenantId, RuleChainType.SYSTEM, pageLink); + pageData = ruleChainService.findTenantRuleChainsByType(tenantId, RuleChainType.CORE, pageLink); loadedRuleChainsName2.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageData.getNextPageLink(); @@ -228,7 +228,7 @@ public abstract class BaseRuleChainServiceTest extends AbstractServiceTest { } pageLink = new TextPageLink(4, name1); - pageData = ruleChainService.findTenantRuleChainsByType(tenantId, RuleChainType.SYSTEM, pageLink); + pageData = ruleChainService.findTenantRuleChainsByType(tenantId, RuleChainType.CORE, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); @@ -237,7 +237,7 @@ public abstract class BaseRuleChainServiceTest extends AbstractServiceTest { } pageLink = new TextPageLink(4, name2); - pageData = ruleChainService.findTenantRuleChainsByType(tenantId, RuleChainType.SYSTEM, pageLink); + pageData = ruleChainService.findTenantRuleChainsByType(tenantId, RuleChainType.CORE, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/nosql/EdgeEventServiceNoSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/nosql/EdgeEventServiceNoSqlTest.java new file mode 100644 index 0000000000..fa6a25f6da --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/service/nosql/EdgeEventServiceNoSqlTest.java @@ -0,0 +1,23 @@ +/** + * Copyright © 2016-2020 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.service.nosql; + +import org.thingsboard.server.dao.service.BaseEdgeEventServiceTest; +import org.thingsboard.server.dao.service.DaoNoSqlTest; + +@DaoNoSqlTest +public class EdgeEventServiceNoSqlTest extends BaseEdgeEventServiceTest { +} diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/nosql/EdgeServiceNoSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/nosql/EdgeServiceNoSqlTest.java index cf365464f8..4b91ee9a6e 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/nosql/EdgeServiceNoSqlTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/nosql/EdgeServiceNoSqlTest.java @@ -15,9 +15,9 @@ */ package org.thingsboard.server.dao.service.nosql; -import org.thingsboard.server.dao.service.EdgeServiceImplTest; +import org.thingsboard.server.dao.service.BaseEdgeServiceTest; import org.thingsboard.server.dao.service.DaoNoSqlTest; @DaoNoSqlTest -public class EdgeServiceNoSqlTest extends EdgeServiceImplTest { +public class EdgeServiceNoSqlTest extends BaseEdgeServiceTest { } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeQueueEntry.java b/dao/src/test/java/org/thingsboard/server/dao/service/sql/EdgeEventServiceSqlTest.java similarity index 70% rename from common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeQueueEntry.java rename to dao/src/test/java/org/thingsboard/server/dao/service/sql/EdgeEventServiceSqlTest.java index 3299ab07be..58b7a3014e 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeQueueEntry.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/sql/EdgeEventServiceSqlTest.java @@ -13,13 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.edge; +package org.thingsboard.server.dao.service.sql; -import lombok.Data; +import org.thingsboard.server.dao.service.BaseEdgeEventServiceTest; +import org.thingsboard.server.dao.service.DaoSqlTest; -@Data -public class EdgeQueueEntry { - private String type; - private EdgeQueueEntityType entityType; - private String data; +@DaoSqlTest +public class EdgeEventServiceSqlTest extends BaseEdgeEventServiceTest { } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/sql/EdgeServiceSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/sql/EdgeServiceSqlTest.java index 445a9718ec..989b8f3148 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/sql/EdgeServiceSqlTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/sql/EdgeServiceSqlTest.java @@ -15,9 +15,9 @@ */ package org.thingsboard.server.dao.service.sql; -import org.thingsboard.server.dao.service.EdgeServiceImplTest; +import org.thingsboard.server.dao.service.BaseEdgeServiceTest; import org.thingsboard.server.dao.service.DaoSqlTest; @DaoSqlTest -public class EdgeServiceSqlTest extends EdgeServiceImplTest { +public class EdgeServiceSqlTest extends BaseEdgeServiceTest { } diff --git a/dao/src/test/resources/sql/hsql/drop-all-tables.sql b/dao/src/test/resources/sql/hsql/drop-all-tables.sql index 8946f77d6c..5b1234483f 100644 --- a/dao/src/test/resources/sql/hsql/drop-all-tables.sql +++ b/dao/src/test/resources/sql/hsql/drop-all-tables.sql @@ -20,4 +20,5 @@ DROP TABLE IF EXISTS widgets_bundle; DROP TABLE IF EXISTS rule_node; DROP TABLE IF EXISTS rule_chain; DROP TABLE IF EXISTS entity_view; -DROP TABLE IF EXISTS edge; \ No newline at end of file +DROP TABLE IF EXISTS edge; +DROP TABLE IF EXISTS edge_event; \ No newline at end of file diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleNode.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleNode.java index 96b45c2cbb..1efe0081d7 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleNode.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleNode.java @@ -58,6 +58,6 @@ public @interface RuleNode { boolean customRelations() default false; - RuleChainType[] ruleChainTypes() default RuleChainType.SYSTEM; + RuleChainType[] ruleChainTypes() default RuleChainType.CORE; } 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 bd26d59f6d..3761933c96 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 @@ -35,6 +35,7 @@ import org.thingsboard.server.dao.cassandra.CassandraCluster; import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.device.DeviceService; +import org.thingsboard.server.dao.edge.EdgeEventService; import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.nosql.CassandraStatementTask; @@ -190,6 +191,8 @@ public interface TbContext { EdgeService getEdgeService(); + EdgeEventService getEdgeEventService(); + ListeningExecutor getJsExecutor(); ListeningExecutor getMailExecutor(); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAssignToCustomerNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAssignToCustomerNode.java index 07a8744873..5d0fe87fa9 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAssignToCustomerNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAssignToCustomerNode.java @@ -38,7 +38,7 @@ import org.thingsboard.server.common.msg.TbMsg; uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeAssignToCustomerConfig", icon = "add_circle", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbAssignToCustomerNode extends TbAbstractCustomerActionNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbClearAlarmNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbClearAlarmNode.java index b8e328344a..b7f7b8ca04 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbClearAlarmNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbClearAlarmNode.java @@ -46,7 +46,7 @@ import org.thingsboard.server.common.msg.TbMsg; uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeClearAlarmConfig", icon = "notifications_off", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbClearAlarmNode extends TbAbstractAlarmNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java index 954e1bed27..0d256b302c 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java @@ -58,7 +58,7 @@ import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbNodeEmptyConfig", icon = "content_copy", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbCopyAttributesToEntityViewNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNode.java index 4644b7820b..4bd56be6be 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNode.java @@ -52,7 +52,7 @@ import java.util.List; uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeCreateAlarmConfig", icon = "notifications_active", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbCreateAlarmNode extends TbAbstractAlarmNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateRelationNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateRelationNode.java index 6404246ed0..c9c0edd96f 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateRelationNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateRelationNode.java @@ -55,7 +55,7 @@ import java.util.List; uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeCreateRelationConfig", icon = "add_circle", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbCreateRelationNode extends TbAbstractRelationActionNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbDeleteRelationNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbDeleteRelationNode.java index cf53645e84..2ea2264dad 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbDeleteRelationNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbDeleteRelationNode.java @@ -45,7 +45,7 @@ import java.util.List; uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeDeleteRelationConfig", icon = "remove_circle", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbDeleteRelationNode extends TbAbstractRelationActionNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbLogNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbLogNode.java index d84a6f4e2e..393cc72b0b 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbLogNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbLogNode.java @@ -38,7 +38,7 @@ import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeLogConfig", icon = "menu", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbLogNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbMsgCountNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbMsgCountNode.java index e909045755..4aeee24e1b 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbMsgCountNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbMsgCountNode.java @@ -44,7 +44,7 @@ import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; icon = "functions", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeMsgCountConfig", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbMsgCountNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbUnassignFromCustomerNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbUnassignFromCustomerNode.java index 73f9e2da08..79513c728d 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbUnassignFromCustomerNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbUnassignFromCustomerNode.java @@ -35,7 +35,7 @@ import org.thingsboard.server.common.msg.TbMsg; uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeUnAssignToCustomerConfig", icon = "remove_circle", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbUnassignFromCustomerNode extends TbAbstractCustomerActionNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/sns/TbSnsNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/sns/TbSnsNode.java index 697c139588..bfc9a0c865 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/sns/TbSnsNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/sns/TbSnsNode.java @@ -47,7 +47,7 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback; uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeSnsConfig", iconUrl = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjQ4IiBoZWlnaHQ9IjQ4Ij48cGF0aCBkPSJNMTMuMjMgMTAuNTZWMTBjLTEuOTQgMC0zLjk5LjM5LTMuOTkgMi42NyAwIDEuMTYuNjEgMS45NSAxLjYzIDEuOTUuNzYgMCAxLjQzLS40NyAxLjg2LTEuMjIuNTItLjkzLjUtMS44LjUtMi44NG0yLjcgNi41M2MtLjE4LjE2LS40My4xNy0uNjMuMDYtLjg5LS43NC0xLjA1LTEuMDgtMS41NC0xLjc5LTEuNDcgMS41LTIuNTEgMS45NS00LjQyIDEuOTUtMi4yNSAwLTQuMDEtMS4zOS00LjAxLTQuMTcgMC0yLjE4IDEuMTctMy42NCAyLjg2LTQuMzggMS40Ni0uNjQgMy40OS0uNzYgNS4wNC0uOTNWNy41YzAtLjY2LjA1LTEuNDEtLjMzLTEuOTYtLjMyLS40OS0uOTUtLjctMS41LS43LTEuMDIgMC0xLjkzLjUzLTIuMTUgMS42MS0uMDUuMjQtLjI1LjQ4LS40Ny40OWwtMi42LS4yOGMtLjIyLS4wNS0uNDYtLjIyLS40LS41Ni42LTMuMTUgMy40NS00LjEgNi00LjEgMS4zIDAgMyAuMzUgNC4wMyAxLjMzQzE3LjExIDQuNTUgMTcgNi4xOCAxNyA3Ljk1djQuMTdjMCAxLjI1LjUgMS44MSAxIDIuNDguMTcuMjUuMjEuNTQgMCAuNzFsLTIuMDYgMS43OGgtLjAxIj48L3BhdGg+PHBhdGggZD0iTTIwLjE2IDE5LjU0QzE4IDIxLjE0IDE0LjgyIDIyIDEyLjEgMjJjLTMuODEgMC03LjI1LTEuNDEtOS44NS0zLjc2LS4yLS4xOC0uMDItLjQzLjI1LS4yOSAyLjc4IDEuNjMgNi4yNSAyLjYxIDkuODMgMi42MSAyLjQxIDAgNS4wNy0uNSA3LjUxLTEuNTMuMzctLjE2LjY2LjI0LjMyLjUxIj48L3BhdGg+PHBhdGggZD0iTTIxLjA3IDE4LjVjLS4yOC0uMzYtMS44NS0uMTctMi41Ny0uMDgtLjE5LjAyLS4yMi0uMTYtLjAzLS4zIDEuMjQtLjg4IDMuMjktLjYyIDMuNTMtLjMzLjI0LjMtLjA3IDIuMzUtMS4yNCAzLjMyLS4xOC4xNi0uMzUuMDctLjI2LS4xMS4yNi0uNjcuODUtMi4xNC41Ny0yLjV6Ij48L3BhdGg+PC9zdmc+", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbSnsNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/sqs/TbSqsNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/sqs/TbSqsNode.java index cb39979369..27359dc8f3 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/sqs/TbSqsNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/sqs/TbSqsNode.java @@ -52,7 +52,7 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback; uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeSqsConfig", iconUrl = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjQ4IiBoZWlnaHQ9IjQ4Ij48cGF0aCBkPSJNMTMuMjMgMTAuNTZWMTBjLTEuOTQgMC0zLjk5LjM5LTMuOTkgMi42NyAwIDEuMTYuNjEgMS45NSAxLjYzIDEuOTUuNzYgMCAxLjQzLS40NyAxLjg2LTEuMjIuNTItLjkzLjUtMS44LjUtMi44NG0yLjcgNi41M2MtLjE4LjE2LS40My4xNy0uNjMuMDYtLjg5LS43NC0xLjA1LTEuMDgtMS41NC0xLjc5LTEuNDcgMS41LTIuNTEgMS45NS00LjQyIDEuOTUtMi4yNSAwLTQuMDEtMS4zOS00LjAxLTQuMTcgMC0yLjE4IDEuMTctMy42NCAyLjg2LTQuMzggMS40Ni0uNjQgMy40OS0uNzYgNS4wNC0uOTNWNy41YzAtLjY2LjA1LTEuNDEtLjMzLTEuOTYtLjMyLS40OS0uOTUtLjctMS41LS43LTEuMDIgMC0xLjkzLjUzLTIuMTUgMS42MS0uMDUuMjQtLjI1LjQ4LS40Ny40OWwtMi42LS4yOGMtLjIyLS4wNS0uNDYtLjIyLS40LS41Ni42LTMuMTUgMy40NS00LjEgNi00LjEgMS4zIDAgMyAuMzUgNC4wMyAxLjMzQzE3LjExIDQuNTUgMTcgNi4xOCAxNyA3Ljk1djQuMTdjMCAxLjI1LjUgMS44MSAxIDIuNDguMTcuMjUuMjEuNTQgMCAuNzFsLTIuMDYgMS43OGgtLjAxIj48L3BhdGg+PHBhdGggZD0iTTIwLjE2IDE5LjU0QzE4IDIxLjE0IDE0LjgyIDIyIDEyLjEgMjJjLTMuODEgMC03LjI1LTEuNDEtOS44NS0zLjc2LS4yLS4xOC0uMDItLjQzLjI1LS4yOSAyLjc4IDEuNjMgNi4yNSAyLjYxIDkuODMgMi42MSAyLjQxIDAgNS4wNy0uNSA3LjUxLTEuNTMuMzctLjE2LjY2LjI0LjMyLjUxIj48L3BhdGg+PHBhdGggZD0iTTIxLjA3IDE4LjVjLS4yOC0uMzYtMS44NS0uMTctMi41Ny0uMDgtLjE5LjAyLS4yMi0uMTYtLjAzLS4zIDEuMjQtLjg4IDMuMjktLjYyIDMuNTMtLjMzLjI0LjMtLjA3IDIuMzUtMS4yNCAzLjMyLS4xOC4xNi0uMzUuMDctLjI2LS4xMS4yNi0uNjcuODUtMi4xNC41Ny0yLjV6Ij48L3BhdGg+PC9zdmc+", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbSqsNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java index ca89d6d2a1..c646b1ca36 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java @@ -45,7 +45,7 @@ import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"}, configDirective = "tbActionNodeGeneratorConfig", icon = "repeat", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbMsgGeneratorNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/delay/TbMsgDelayNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/delay/TbMsgDelayNode.java index e3b1e71ecc..d7ceefda36 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/delay/TbMsgDelayNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/delay/TbMsgDelayNode.java @@ -46,7 +46,7 @@ import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; icon = "pause", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeMsgDelayConfig", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbMsgDelayNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/edge/PushToEdgeNodeCallback.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/edge/PushToEdgeNodeCallback.java deleted file mode 100644 index 77c6a64194..0000000000 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/edge/PushToEdgeNodeCallback.java +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright © 2016-2020 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.rule.engine.edge; - -import com.google.common.util.concurrent.FutureCallback; -import lombok.Data; -import org.thingsboard.rule.engine.api.TbContext; -import org.thingsboard.server.common.msg.TbMsg; - -import javax.annotation.Nullable; - -import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; - -@Data -class PushToEdgeNodeCallback implements FutureCallback { - private final TbContext ctx; - private final TbMsg msg; - - @Override - public void onSuccess(@Nullable Void result) { - ctx.tellNext(msg, SUCCESS); - } - - @Override - public void onFailure(Throwable t) { - ctx.tellFailure(msg, t); - } -} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/edge/TbMsgPushToEdgeNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/edge/TbMsgPushToEdgeNode.java index 92a4103867..1e42224c70 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/edge/TbMsgPushToEdgeNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/edge/TbMsgPushToEdgeNode.java @@ -15,6 +15,10 @@ */ package org.thingsboard.rule.engine.edge; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.thingsboard.rule.engine.api.EmptyNodeConfiguration; import org.thingsboard.rule.engine.api.RuleNode; @@ -23,10 +27,25 @@ import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.util.TbNodeUtils; +import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.edge.EdgeEventType; +import org.thingsboard.server.common.data.id.EdgeId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.plugin.ComponentType; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.session.SessionMsgType; +import javax.annotation.Nullable; +import java.util.List; + +import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; + @Slf4j @RuleNode( type = ComponentType.ACTION, @@ -40,13 +59,10 @@ import org.thingsboard.server.common.msg.session.SessionMsgType; ) public class TbMsgPushToEdgeNode implements TbNode { - private static final String CLOUD_MSG_SOURCE = "cloud"; - private static final String EDGE_MSG_SOURCE = "edge"; - private static final String MSG_SOURCE_KEY = "source"; - private static final String TS_METADATA_KEY = "ts"; - private EmptyNodeConfiguration config; + private static final ObjectMapper json = new ObjectMapper(); + @Override public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { this.config = TbNodeUtils.convert(configuration, EmptyNodeConfiguration.class); @@ -54,14 +70,71 @@ public class TbMsgPushToEdgeNode implements TbNode { @Override public void onMsg(TbContext ctx, TbMsg msg) { - if (EDGE_MSG_SOURCE.equalsIgnoreCase(msg.getMetaData().getValue(MSG_SOURCE_KEY))) { - return; - } - if (msg.getType().equals(SessionMsgType.POST_TELEMETRY_REQUEST.name())) { - msg.getMetaData().putValue(TS_METADATA_KEY, Long.toString(System.currentTimeMillis())); + if (EntityType.DEVICE.equals(msg.getOriginator().getEntityType()) || + EntityType.DEVICE.equals(msg.getOriginator().getEntityType()) || + EntityType.DEVICE.equals(msg.getOriginator().getEntityType()) || + EntityType.DEVICE.equals(msg.getOriginator().getEntityType())) { + if (SessionMsgType.POST_TELEMETRY_REQUEST.name().equals(msg.getType()) || + SessionMsgType.POST_ATTRIBUTES_REQUEST.name().equals(msg.getType()) || + DataConstants.ATTRIBUTES_UPDATED.equals(msg.getType()) || + DataConstants.ATTRIBUTES_DELETED.equals(msg.getType())) { + ListenableFuture getEdgeIdFuture = getEdgeIdByOriginatorId(ctx, ctx.getTenantId(), msg.getOriginator()); + Futures.transform(getEdgeIdFuture, edgeId -> { + EdgeEventType edgeEventTypeByEntityType = ctx.getEdgeEventService().getEdgeEventTypeByEntityType(msg.getOriginator().getEntityType()); + if (edgeEventTypeByEntityType == null) { + log.debug("Edge event type is null. Entity Type {}", msg.getOriginator().getEntityType()); + ctx.tellFailure(msg, new RuntimeException("Edge event type is null. Entity Type '"+ msg.getOriginator().getEntityType() + "'")); + } + ActionType actionType; + if (SessionMsgType.POST_TELEMETRY_REQUEST.name().equals(msg.getType())) { + actionType = ActionType.TIMESERIES_UPDATED; + } else if (SessionMsgType.POST_ATTRIBUTES_REQUEST.name().equals(msg.getType()) || + DataConstants.ATTRIBUTES_UPDATED.equals(msg.getType())) { + actionType = ActionType.ATTRIBUTES_UPDATED; + } else { + actionType = ActionType.ATTRIBUTES_DELETED; + } + EdgeEvent edgeEvent = new EdgeEvent(); + edgeEvent.setTenantId(ctx.getTenantId()); + edgeEvent.setEdgeId(edgeId); + edgeEvent.setEdgeEventAction(actionType.name()); + edgeEvent.setEntityId(msg.getOriginator().getId()); + edgeEvent.setEdgeEventType(edgeEventTypeByEntityType); + edgeEvent.setEntityBody(json.valueToTree(msg.getData())); + ListenableFuture saveFuture = ctx.getEdgeEventService().saveAsync(edgeEvent); + Futures.addCallback(saveFuture, new FutureCallback() { + @Override + public void onSuccess(@Nullable EdgeEvent event) { + ctx.tellNext(msg, SUCCESS); + } + + @Override + public void onFailure(Throwable th) { + log.error("Could not save edge event", th); + ctx.tellFailure(msg, th); + } + }, ctx.getDbCallbackExecutor()); + return null; + }, ctx.getDbCallbackExecutor()); + } else { + log.debug("Unsupported msg type {}", msg.getType()); + ctx.tellFailure(msg, new RuntimeException("Unsupported msg type '" + msg.getType() + "'")); + } + } else { + log.debug("Unsupported originator type {}", msg.getOriginator().getEntityType()); + ctx.tellFailure(msg, new RuntimeException("Unsupported originator type '" + msg.getOriginator().getEntityType() + "'")); } - msg.getMetaData().putValue(MSG_SOURCE_KEY, CLOUD_MSG_SOURCE); - ctx.getEdgeService().pushEventToEdge(ctx.getTenantId(), msg, new PushToEdgeNodeCallback(ctx, msg)); + } + + private ListenableFuture getEdgeIdByOriginatorId(TbContext ctx, TenantId tenantId, EntityId originatorId) { + ListenableFuture> future = ctx.getRelationService().findByToAndTypeAsync(tenantId, originatorId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE); + return Futures.transform(future, relations -> { + if (relations != null && relations.size() > 0) { + return new EdgeId(relations.get(0).getFrom().getId()); + } else { + return null; + } + }, ctx.getDbCallbackExecutor()); } @Override diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckMessageNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckMessageNode.java index 44175abac6..e9d54de5de 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckMessageNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckMessageNode.java @@ -41,7 +41,7 @@ import java.util.Map; "Else if the checkbox is not selected, and at least one of the keys from data or metadata of the message exists - send Message via True chain, otherwise, False chain is used. ", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbFilterNodeCheckMessageConfig", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbCheckMessageNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckRelationNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckRelationNode.java index 01ac3db853..f5912d1e5b 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckRelationNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckRelationNode.java @@ -53,7 +53,7 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback; nodeDetails = "If at least one relation exists - send Message via True chain, otherwise False chain is used.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbFilterNodeCheckRelationConfig", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbCheckRelationNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsFilterNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsFilterNode.java index 3d1aa94b9b..997a872a4a 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsFilterNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsFilterNode.java @@ -38,7 +38,7 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback; "Message type can be accessed via msgType property.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbFilterNodeScriptConfig", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbJsFilterNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsSwitchNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsSwitchNode.java index 207c029a5d..fa8f332d69 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsSwitchNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsSwitchNode.java @@ -41,7 +41,7 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback; "Message type can be accessed via msgType property.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbFilterNodeSwitchConfig", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbJsSwitchNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeFilterNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeFilterNode.java index c11a75a313..f0dac68130 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeFilterNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeFilterNode.java @@ -35,7 +35,7 @@ import org.thingsboard.server.common.msg.TbMsg; nodeDetails = "If incoming MessageType is expected - send Message via True chain, otherwise False chain is used.", uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"}, configDirective = "tbFilterNodeMessageTypeConfig", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbMsgTypeFilterNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNode.java index bbb7b5a113..0973b49a97 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNode.java @@ -36,7 +36,7 @@ import org.thingsboard.server.common.msg.session.SessionMsgType; nodeDetails = "Sends messages with message types \"Post attributes\", \"Post telemetry\", \"RPC Request\" etc. via corresponding chain, otherwise Other chain is used.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbNodeEmptyConfig", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbMsgTypeSwitchNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeFilterNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeFilterNode.java index 2ba96e1ed8..6687ae461e 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeFilterNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeFilterNode.java @@ -33,7 +33,7 @@ import org.thingsboard.server.common.msg.TbMsg; nodeDetails = "If Originator Type of incoming message is expected - send Message via True chain, otherwise False chain is used.", uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"}, configDirective = "tbFilterNodeOriginatorTypeConfig", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbOriginatorTypeFilterNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeSwitchNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeSwitchNode.java index efedadf9ee..9bd2109746 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeSwitchNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeSwitchNode.java @@ -33,7 +33,7 @@ import org.thingsboard.server.common.msg.TbMsg; nodeDetails = "Routes messages to chain according to the originator type ('Device', 'Asset', etc.).", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbNodeEmptyConfig", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbOriginatorTypeSwitchNode implements TbNode { 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 681fac3ef1..036bfbd762 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 @@ -51,7 +51,7 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback; uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodePubSubConfig", iconUrl = "data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjgiIGhlaWdodD0iMTI4IiB2aWV3Qm94PSIwIDAgMTI4IDEyOCI+Cjx0aXRsZT5DbG91ZCBQdWJTdWI8L3RpdGxlPgo8Zz4KPHBhdGggZD0iTTEyNi40Nyw1OC4xMmwtMjYuMy00NS43NEExMS41NiwxMS41NiwwLDAsMCw5MC4zMSw2LjVIMzcuN2ExMS41NSwxMS41NSwwLDAsMC05Ljg2LDUuODhMMS41Myw1OGExMS40OCwxMS40OCwwLDAsMCwwLDExLjQ0bDI2LjMsNDZhMTEuNzcsMTEuNzcsMCwwLDAsOS44Niw2LjA5SDkwLjNhMTEuNzMsMTEuNzMsMCwwLDAsOS44Ny02LjA2bDI2LjMtNDUuNzRBMTEuNzMsMTEuNzMsMCwwLDAsMTI2LjQ3LDU4LjEyWiIgc3R5bGU9ImZpbGw6ICM3MzViMmYiLz4KPHBhdGggZD0iTTg5LjIyLDQ3Ljc0LDgzLjM2LDQ5bC0xNC42LTE0LjZMNjQuMDksNDMuMSw2MS41NSw1My4ybDQuMjksNC4yOUw1Ny42LDU5LjE4LDQ2LjMsNDcuODhsLTcuNjcsNy4zOEw1Mi43Niw2OS4zN2wtMTUsMTEuOUw3OCwxMjEuNUg5MC4zYTExLjczLDExLjczLDAsMCwwLDkuODctNi4wNmwyMC43Mi0zNloiIHN0eWxlPSJvcGFjaXR5OiAwLjA3MDAwMDAwMDI5ODAyMztpc29sYXRpb246IGlzb2xhdGUiLz4KPHBhdGggZD0iTTgyLjg2LDQ3YTUuMzIsNS4zMiwwLDEsMS0xLjk1LDcuMjdBNS4zMiw1LjMyLDAsMCwxLDgyLjg2LDQ3IiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8cGF0aCBkPSJNMzkuODIsNTYuMThhNS4zMiw1LjMyLDAsMSwxLDcuMjctMS45NSw1LjMyLDUuMzIsMCwwLDEtNy4yNywxLjk1IiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8cGF0aCBkPSJNNjkuMzIsODguODVBNS4zMiw1LjMyLDAsMSwxLDY0LDgzLjUyYTUuMzIsNS4zMiwwLDAsMSw1LjMyLDUuMzIiIHN0eWxlPSJmaWxsOiAjZmZmIi8+CjxnPgo8cGF0aCBkPSJNNjQsNTIuOTRhMTEuMDYsMTEuMDYsMCwwLDEsMi40Ni4yOFYzOS4xNUg2MS41NFY1My4yMkExMS4wNiwxMS4wNiwwLDAsMSw2NCw1Mi45NFoiIHN0eWxlPSJmaWxsOiAjZmZmIi8+CjxwYXRoIGQ9Ik03NC41Nyw2Ny4yNmExMSwxMSwwLDAsMS0yLjQ3LDQuMjVsMTIuMTksNywyLjQ2LTQuMjZaIiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8cGF0aCBkPSJNNTMuNDMsNjcuMjZsLTEyLjE4LDcsMi40Niw0LjI2LDEyLjE5LTdBMTEsMTEsMCwwLDEsNTMuNDMsNjcuMjZaIiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8L2c+CjxwYXRoIGQ9Ik03Mi42LDY0QTguNiw4LjYsMCwxLDEsNjQsNTUuNCw4LjYsOC42LDAsMCwxLDcyLjYsNjQiIHN0eWxlPSJmaWxsOiAjZmZmIi8+CjxwYXRoIGQ9Ik0zOS4xLDcwLjU3YTYuNzYsNi43NiwwLDEsMS0yLjQ3LDkuMjMsNi43Niw2Ljc2LDAsMCwxLDIuNDctOS4yMyIgc3R5bGU9ImZpbGw6ICNmZmYiLz4KPHBhdGggZD0iTTgyLjE0LDgyLjI3YTYuNzYsNi43NiwwLDEsMSw5LjIzLTIuNDcsNi43NSw2Ljc1LDAsMCwxLTkuMjMsMi40NyIgc3R5bGU9ImZpbGw6ICNmZmYiLz4KPHBhdGggZD0iTTcwLjc2LDM5LjE1QTYuNzYsNi43NiwwLDEsMSw2NCwzMi4zOWE2Ljc2LDYuNzYsMCwwLDEsNi43Niw2Ljc2IiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8L2c+Cjwvc3ZnPgo=", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbPubSubNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNode.java index 0a177cdce6..bb2e0bbca9 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNode.java @@ -53,7 +53,7 @@ import java.util.concurrent.TimeoutException; nodeDetails = "Extracts latitude and longitude parameters from incoming message and returns different events based on configuration parameters", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeGpsGeofencingConfig", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbGpsGeofencingActionNode extends AbstractGeofencingNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNode.java index 260c630bd9..a40f416f92 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNode.java @@ -54,7 +54,7 @@ import java.util.List; nodeDetails = "Extracts latitude and longitude parameters from incoming message and returns 'True' if they are inside configured perimeters, 'False' otherwise.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbFilterNodeGpsGeofencingConfig", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbGpsGeofencingFilterNode extends AbstractGeofencingNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/kafka/TbKafkaNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/kafka/TbKafkaNode.java index 485505e2d5..3504f7bd85 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/kafka/TbKafkaNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/kafka/TbKafkaNode.java @@ -53,7 +53,7 @@ import java.util.Properties; uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeKafkaConfig", iconUrl = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTUzOCIgaGVpZ2h0PSIyNTAwIiB2aWV3Qm94PSIwIDAgMjU2IDQxNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCI+PHBhdGggZD0iTTIwMS44MTYgMjMwLjIxNmMtMTYuMTg2IDAtMzAuNjk3IDcuMTcxLTQwLjYzNCAxOC40NjFsLTI1LjQ2My0xOC4wMjZjMi43MDMtNy40NDIgNC4yNTUtMTUuNDMzIDQuMjU1LTIzLjc5NyAwLTguMjE5LTEuNDk4LTE2LjA3Ni00LjExMi0yMy40MDhsMjUuNDA2LTE3LjgzNWM5LjkzNiAxMS4yMzMgMjQuNDA5IDE4LjM2NSA0MC41NDggMTguMzY1IDI5Ljg3NSAwIDU0LjE4NC0yNC4zMDUgNTQuMTg0LTU0LjE4NCAwLTI5Ljg3OS0yNC4zMDktNTQuMTg0LTU0LjE4NC01NC4xODQtMjkuODc1IDAtNTQuMTg0IDI0LjMwNS01NC4xODQgNTQuMTg0IDAgNS4zNDguODA4IDEwLjUwNSAyLjI1OCAxNS4zODlsLTI1LjQyMyAxNy44NDRjLTEwLjYyLTEzLjE3NS0yNS45MTEtMjIuMzc0LTQzLjMzMy0yNS4xODJ2LTMwLjY0YzI0LjU0NC01LjE1NSA0My4wMzctMjYuOTYyIDQzLjAzNy01My4wMTlDMTI0LjE3MSAyNC4zMDUgOTkuODYyIDAgNjkuOTg3IDAgNDAuMTEyIDAgMTUuODAzIDI0LjMwNSAxNS44MDMgNTQuMTg0YzAgMjUuNzA4IDE4LjAxNCA0Ny4yNDYgNDIuMDY3IDUyLjc2OXYzMS4wMzhDMjUuMDQ0IDE0My43NTMgMCAxNzIuNDAxIDAgMjA2Ljg1NGMwIDM0LjYyMSAyNS4yOTIgNjMuMzc0IDU4LjM1NSA2OC45NHYzMi43NzRjLTI0LjI5OSA1LjM0MS00Mi41NTIgMjcuMDExLTQyLjU1MiA1Mi44OTQgMCAyOS44NzkgMjQuMzA5IDU0LjE4NCA1NC4xODQgNTQuMTg0IDI5Ljg3NSAwIDU0LjE4NC0yNC4zMDUgNTQuMTg0LTU0LjE4NCAwLTI1Ljg4My0xOC4yNTMtNDcuNTUzLTQyLjU1Mi01Mi44OTR2LTMyLjc3NWE2OS45NjUgNjkuOTY1IDAgMCAwIDQyLjYtMjQuNzc2bDI1LjYzMyAxOC4xNDNjLTEuNDIzIDQuODQtMi4yMiA5Ljk0Ni0yLjIyIDE1LjI0IDAgMjkuODc5IDI0LjMwOSA1NC4xODQgNTQuMTg0IDU0LjE4NCAyOS44NzUgMCA1NC4xODQtMjQuMzA1IDU0LjE4NC01NC4xODQgMC0yOS44NzktMjQuMzA5LTU0LjE4NC01NC4xODQtNTQuMTg0em0wLTEyNi42OTVjMTQuNDg3IDAgMjYuMjcgMTEuNzg4IDI2LjI3IDI2LjI3MXMtMTEuNzgzIDI2LjI3LTI2LjI3IDI2LjI3LTI2LjI3LTExLjc4Ny0yNi4yNy0yNi4yN2MwLTE0LjQ4MyAxMS43ODMtMjYuMjcxIDI2LjI3LTI2LjI3MXptLTE1OC4xLTQ5LjMzN2MwLTE0LjQ4MyAxMS43ODQtMjYuMjcgMjYuMjcxLTI2LjI3czI2LjI3IDExLjc4NyAyNi4yNyAyNi4yN2MwIDE0LjQ4My0xMS43ODMgMjYuMjctMjYuMjcgMjYuMjdzLTI2LjI3MS0xMS43ODctMjYuMjcxLTI2LjI3em01Mi41NDEgMzA3LjI3OGMwIDE0LjQ4My0xMS43ODMgMjYuMjctMjYuMjcgMjYuMjdzLTI2LjI3MS0xMS43ODctMjYuMjcxLTI2LjI3YzAtMTQuNDgzIDExLjc4NC0yNi4yNyAyNi4yNzEtMjYuMjdzMjYuMjcgMTEuNzg3IDI2LjI3IDI2LjI3em0tMjYuMjcyLTExNy45N2MtMjAuMjA1IDAtMzYuNjQyLTE2LjQzNC0zNi42NDItMzYuNjM4IDAtMjAuMjA1IDE2LjQzNy0zNi42NDIgMzYuNjQyLTM2LjY0MiAyMC4yMDQgMCAzNi42NDEgMTYuNDM3IDM2LjY0MSAzNi42NDIgMCAyMC4yMDQtMTYuNDM3IDM2LjYzOC0zNi42NDEgMzYuNjM4em0xMzEuODMxIDY3LjE3OWMtMTQuNDg3IDAtMjYuMjctMTEuNzg4LTI2LjI3LTI2LjI3MXMxMS43ODMtMjYuMjcgMjYuMjctMjYuMjcgMjYuMjcgMTEuNzg3IDI2LjI3IDI2LjI3YzAgMTQuNDgzLTExLjc4MyAyNi4yNzEtMjYuMjcgMjYuMjcxeiIvPjwvc3ZnPg==", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbKafkaNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNode.java index 2133314e61..1a4fb244b3 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNode.java @@ -42,7 +42,7 @@ import static org.thingsboard.rule.engine.mail.TbSendEmailNode.SEND_EMAIL_TYPE; uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbTransformationNodeToEmailConfig", icon = "email", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbMsgToEmailNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbSendEmailNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbSendEmailNode.java index 237a56a36c..043b5008c4 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbSendEmailNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbSendEmailNode.java @@ -48,7 +48,7 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback; uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeSendEmailConfig", icon = "send", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbSendEmailNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNode.java index cbe0054864..92aca60332 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNode.java @@ -42,7 +42,7 @@ import org.thingsboard.server.common.msg.TbMsg; "metadata.cs_temperature or metadata.shared_limit ", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbEnrichmentNodeOriginatorAttributesConfig", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbGetAttributesNode extends TbAbstractGetAttributesNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNode.java index 25979097ac..1b0e0b7efb 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNode.java @@ -35,7 +35,7 @@ import org.thingsboard.server.common.data.rule.RuleChainType; "metadata.temperature.", uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"}, configDirective = "tbEnrichmentNodeCustomerAttributesConfig", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbGetCustomerAttributeNode extends TbEntityGetAttrNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNode.java index 84d94eb614..93a03498d7 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNode.java @@ -44,7 +44,7 @@ import org.thingsboard.server.common.msg.TbMsg; "If the originator of the message is not assigned to Customer, or originator type is not supported - Message will be forwarded to Failure chain, otherwise, Success chain will be used.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbEnrichmentNodeEntityDetailsConfig", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbGetCustomerDetailsNode extends TbAbstractGetEntityDetailsNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetDeviceAttrNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetDeviceAttrNode.java index 48649aab58..af6cbf9265 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetDeviceAttrNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetDeviceAttrNode.java @@ -40,7 +40,7 @@ import org.thingsboard.server.common.msg.TbMsg; "metadata.cs_temperature or metadata.shared_limit ", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbEnrichmentNodeDeviceAttributesConfig", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbGetDeviceAttrNode extends TbAbstractGetAttributesNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNode.java index 6885b1362d..6f240d2df6 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNode.java @@ -45,7 +45,7 @@ import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; nodeDetails = "Will fetch fields values specified in mapping. If specified field is not part of originator fields it will be ignored.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbEnrichmentNodeOriginatorFieldsConfig", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbGetOriginatorFieldsNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNode.java index 0d2dc5c82a..44cf94163e 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNode.java @@ -37,7 +37,7 @@ import org.thingsboard.server.common.data.rule.RuleChainType; "metadata.temperature.", uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"}, configDirective = "tbEnrichmentNodeRelatedAttributesConfig", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbGetRelatedAttributeNode extends TbEntityGetAttrNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNode.java index a438208bcb..009870e404 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNode.java @@ -68,7 +68,7 @@ import static org.thingsboard.server.common.data.kv.Aggregation.NONE; "Note: The maximum size of the fetched array is 1000 records.\n ", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbEnrichmentNodeGetTelemetryFromDatabase", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbGetTelemetryNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNode.java index 659217eaa2..1d31a6bc0d 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNode.java @@ -37,7 +37,7 @@ import org.thingsboard.server.common.data.rule.RuleChainType; "metadata.temperature.", uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"}, configDirective = "tbEnrichmentNodeTenantAttributesConfig", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbGetTenantAttributeNode extends TbEntityGetAttrNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNode.java index 434200228f..cf4bcaf064 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNode.java @@ -39,7 +39,7 @@ import org.thingsboard.server.common.msg.TbMsg; "If the originator of the message is not assigned to Tenant, or originator type is not supported - Message will be forwarded to Failure chain, otherwise, Success chain will be used.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbEnrichmentNodeEntityDetailsConfig", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbGetTenantDetailsNode extends TbAbstractGetEntityDetailsNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java index 4badd94bc1..b7d76f8968 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java @@ -51,7 +51,7 @@ import java.util.concurrent.TimeoutException; uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"}, configDirective = "tbActionNodeMqttConfig", icon = "call_split", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbMqttNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNode.java index 7756dd71c4..78ac75a642 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNode.java @@ -41,7 +41,7 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback; uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeRabbitMqConfig", iconUrl = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbDpzcGFjZT0icHJlc2VydmUiIHZlcnNpb249IjEuMSIgeT0iMHB4IiB4PSIwcHgiIHZpZXdCb3g9IjAgMCAxMDAwIDEwMDAiPjxwYXRoIHN0cm9rZS13aWR0aD0iLjg0OTU2IiBkPSJtODYwLjQ3IDQxNi4zMmgtMjYyLjAxYy0xMi45MTMgMC0yMy42MTgtMTAuNzA0LTIzLjYxOC0yMy42MTh2LTI3Mi43MWMwLTIwLjMwNS0xNi4yMjctMzYuMjc2LTM2LjI3Ni0zNi4yNzZoLTkzLjc5MmMtMjAuMzA1IDAtMzYuMjc2IDE2LjIyNy0zNi4yNzYgMzYuMjc2djI3MC44NGMtMC4yNTQ4NyAxNC4xMDMtMTEuNDY5IDI1LjU3Mi0yNS43NDIgMjUuNTcybC04NS42MzYgMC42Nzk2NWMtMTQuMTAzIDAtMjUuNTcyLTExLjQ2OS0yNS41NzItMjUuNTcybDAuNjc5NjUtMjcxLjUyYzAtMjAuMzA1LTE2LjIyNy0zNi4yNzYtMzYuMjc2LTM2LjI3NmgtOTMuNTM3Yy0yMC4zMDUgMC0zNi4yNzYgMTYuMjI3LTM2LjI3NiAzNi4yNzZ2NzYzLjg0YzAgMTguMDk2IDE0Ljc4MiAzMi40NTMgMzIuNDUzIDMyLjQ1M2g3MjIuODFjMTguMDk2IDAgMzIuNDUzLTE0Ljc4MiAzMi40NTMtMzIuNDUzdi00MzUuMzFjLTEuMTg5NC0xOC4xODEtMTUuMjkyLTMyLjE5OC0zMy4zODgtMzIuMTk4em0tMTIyLjY4IDI4Ny4wN2MwIDIzLjYxOC0xOC44NiA0Mi40NzgtNDIuNDc4IDQyLjQ3OGgtNzMuOTk3Yy0yMy42MTggMC00Mi40NzgtMTguODYtNDIuNDc4LTQyLjQ3OHYtNzQuMjUyYzAtMjMuNjE4IDE4Ljg2LTQyLjQ3OCA0Mi40NzgtNDIuNDc4aDczLjk5N2MyMy42MTggMCA0Mi40NzggMTguODYgNDIuNDc4IDQyLjQ3OHoiLz48L3N2Zz4=", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbRabbitMqNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNode.java index 930d145259..0ee938f7b7 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNode.java @@ -41,7 +41,7 @@ import org.thingsboard.server.common.msg.TbMsg; uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeRestApiCallConfig", iconUrl = "data:image/svg+xml;base64,PHN2ZyBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbDpzcGFjZT0icHJlc2VydmUiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiB2ZXJzaW9uPSIxLjEiIHk9IjBweCIgeD0iMHB4Ij48ZyB0cmFuc2Zvcm09Im1hdHJpeCguOTQ5NzUgMCAwIC45NDk3NSAxNy4xMiAyNi40OTIpIj48cGF0aCBkPSJtMTY5LjExIDEwOC41NGMtOS45MDY2IDAuMDczNC0xOS4wMTQgNi41NzI0LTIyLjAxNCAxNi40NjlsLTY5Ljk5MyAyMzEuMDhjLTMuNjkwNCAxMi4xODEgMy4yODkyIDI1LjIyIDE1LjQ2OSAyOC45MSAyLjIyNTkgMC42NzQ4MSA0LjQ5NjkgMSA2LjcyODUgMSA5Ljk3MjEgMCAxOS4xNjUtNi41MTUzIDIyLjE4Mi0xNi40NjdhNi41MjI0IDYuNTIyNCAwIDAgMCAwLjAwMiAtMC4wMDJsNjkuOTktMjMxLjA3YTYuNTIyNCA2LjUyMjQgMCAwIDAgMCAtMC4wMDJjMy42ODU1LTEyLjE4MS0zLjI4Ny0yNS4yMjUtMTUuNDcxLTI4LjkxMi0yLjI4MjUtMC42OTE0NS00LjYxMTYtMS4wMTY5LTYuODk4NC0xem04NC45ODggMGMtOS45MDQ4IDAuMDczNC0xOS4wMTggNi41Njc1LTIyLjAxOCAxNi40NjlsLTY5Ljk4NiAyMzEuMDhjLTMuNjg5OCAxMi4xNzkgMy4yODUzIDI1LjIxNyAxNS40NjUgMjguOTA4IDIuMjI5NyAwLjY3NjQ3IDQuNTAwOCAxLjAwMiA2LjczMjQgMS4wMDIgOS45NzIxIDAgMTkuMTY1LTYuNTE1MyAyMi4xODItMTYuNDY3YTYuNTIyNCA2LjUyMjQgMCAwIDAgMC4wMDIgLTAuMDAybDY5Ljk4OC0yMzEuMDdjMy42OTA4LTEyLjE4MS0zLjI4NTItMjUuMjIzLTE1LjQ2Ny0yOC45MTItMi4yODE0LTAuNjkyMzEtNC42MTA4LTEuMDE4OS02Ljg5ODQtMS4wMDJ6bS0yMTcuMjkgNDIuMjNjLTEyLjcyOS0wLjAwMDg3LTIzLjE4OCAxMC40NTYtMjMuMTg4IDIzLjE4NiAwLjAwMSAxMi43MjggMTAuNDU5IDIzLjE4NiAyMy4xODggMjMuMTg2IDEyLjcyNy0wLjAwMSAyMy4xODMtMTAuNDU5IDIzLjE4NC0yMy4xODYgMC4wMDA4NzYtMTIuNzI4LTEwLjQ1Ni0yMy4xODUtMjMuMTg0LTIzLjE4NnptMCAxNDYuNjRjLTEyLjcyNy0wLjAwMDg3LTIzLjE4NiAxMC40NTUtMjMuMTg4IDIzLjE4NC0wLjAwMDg3MyAxMi43MjkgMTAuNDU4IDIzLjE4OCAyMy4xODggMjMuMTg4IDEyLjcyOC0wLjAwMSAyMy4xODQtMTAuNDYgMjMuMTg0LTIzLjE4OC0wLjAwMS0xMi43MjYtMTAuNDU3LTIzLjE4My0yMy4xODQtMjMuMTg0em0yNzAuNzkgNDIuMjExYy0xMi43MjcgMC0yMy4xODQgMTAuNDU3LTIzLjE4NCAyMy4xODRzMTAuNDU1IDIzLjE4OCAyMy4xODQgMjMuMTg4aDE1NC45OGMxMi43MjkgMCAyMy4xODYtMTAuNDYgMjMuMTg2LTIzLjE4OCAwLjAwMS0xMi43MjgtMTAuNDU4LTIzLjE4NC0yMy4xODYtMjMuMTg0eiIgdHJhbnNmb3JtPSJtYXRyaXgoMS4wMzc2IDAgMCAxLjAzNzYgLTcuNTY3NiAtMTQuOTI1KSIgc3Ryb2tlLXdpZHRoPSIxLjI2OTMiLz48L2c+PC9zdmc+", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbRestApiCallNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rpc/TbSendRPCReplyNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rpc/TbSendRPCReplyNode.java index 4ab84d935f..2fc84e1c32 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rpc/TbSendRPCReplyNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rpc/TbSendRPCReplyNode.java @@ -41,7 +41,7 @@ import java.util.UUID; uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeRpcReplyConfig", icon = "call_merge", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbSendRPCReplyNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNode.java index a192596614..6e04afd123 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNode.java @@ -52,7 +52,7 @@ import java.util.concurrent.TimeUnit; uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeRpcRequestConfig", icon = "call_made", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbSendRPCRequestNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java index 9f70677453..cb536714e7 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java @@ -46,7 +46,7 @@ import java.util.Set; uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"}, configDirective = "tbActionNodeAttributesConfig", icon = "file_upload", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbMsgAttributesNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java index cb0cf8a4e2..24ba417b89 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java @@ -47,7 +47,7 @@ import java.util.Map; uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"}, configDirective = "tbActionNodeTimeseriesConfig", icon = "file_upload", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbMsgTimeseriesNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transaction/TbSynchronizationBeginNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transaction/TbSynchronizationBeginNode.java index eabff52f8f..c66cbc1652 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transaction/TbSynchronizationBeginNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transaction/TbSynchronizationBeginNode.java @@ -40,7 +40,7 @@ import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; "Size of the queue per originator and timeout values are configurable on a system level", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbNodeEmptyConfig", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) @Deprecated public class TbSynchronizationBeginNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transaction/TbSynchronizationEndNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transaction/TbSynchronizationEndNode.java index 5febcf2276..83d221285a 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transaction/TbSynchronizationEndNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transaction/TbSynchronizationEndNode.java @@ -40,7 +40,7 @@ import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; nodeDetails = "", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = ("tbNodeEmptyConfig"), - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) @Deprecated public class TbSynchronizationEndNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java index 9ff11922d3..0a0de55a46 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java @@ -48,7 +48,7 @@ import java.util.HashSet; uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"}, configDirective = "tbTransformationNodeChangeOriginatorConfig", icon = "find_replace", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbChangeOriginatorNode extends TbAbstractTransformNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbTransformMsgNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbTransformMsgNode.java index 4d8dcef644..434a5ce61a 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbTransformMsgNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbTransformMsgNode.java @@ -39,7 +39,7 @@ import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; "All fields in resulting object are optional and will be taken from original message if not specified.", uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"}, configDirective = "tbTransformationNodeScriptConfig", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbTransformMsgNode extends TbAbstractTransformNode { diff --git a/ui/src/app/common/types.constant.js b/ui/src/app/common/types.constant.js index a877f1d3b2..a4036362c9 100644 --- a/ui/src/app/common/types.constant.js +++ b/ui/src/app/common/types.constant.js @@ -744,7 +744,7 @@ export default angular.module('thingsboard.types', []) clientSide: false } }, - systemRuleChainType: "SYSTEM", + coreRuleChainType: "CORE", edgeRuleChainType: "EDGE", ruleNodeTypeComponentTypes: ["FILTER", "ENRICHMENT", "TRANSFORMATION", "ACTION", "EXTERNAL"], ruleChainNodeComponent: { diff --git a/ui/src/app/locale/locale.constant-de_DE.json b/ui/src/app/locale/locale.constant-de_DE.json index 5e69ab6758..62f0bc5156 100644 --- a/ui/src/app/locale/locale.constant-de_DE.json +++ b/ui/src/app/locale/locale.constant-de_DE.json @@ -1351,7 +1351,7 @@ "rulechain": { "rulechain": "Regelkette", "rulechains": "Regelketten", - "system-rulechains": "Systemeregelketten", + "core-rulechains": "Kernregelketten", "edge-rulechains": "Randregelketten", "root": "Wurzel", "delete": "Regelkette löschen", diff --git a/ui/src/app/locale/locale.constant-en_US.json b/ui/src/app/locale/locale.constant-en_US.json index 32960b16ab..d6b186c42e 100644 --- a/ui/src/app/locale/locale.constant-en_US.json +++ b/ui/src/app/locale/locale.constant-en_US.json @@ -1533,7 +1533,7 @@ "rulechain": { "rulechain": "Rule chain", "rulechains": "Rule chains", - "system-rulechains": "System Rule chains", + "core-rulechains": "Core Rule chains", "edge-rulechains": "Edge Rule chains", "root": "Root", "delete": "Delete rule chain", diff --git a/ui/src/app/locale/locale.constant-es_ES.json b/ui/src/app/locale/locale.constant-es_ES.json index 31083093dc..75fed86ed1 100644 --- a/ui/src/app/locale/locale.constant-es_ES.json +++ b/ui/src/app/locale/locale.constant-es_ES.json @@ -1410,7 +1410,7 @@ "rulechain": { "rulechain": "Cadena de reglas", "rulechains": "Cadenas de reglas", - "system-rulechains": "Cadenas de reglas del sistema", + "core-rulechains": "Cadenas de reglas centrales", "edge-rulechains": "Cadenas de reglas de borde", "root": "Raíz", "delete": "Eliminar cadena de reglas", diff --git a/ui/src/app/locale/locale.constant-fr_FR.json b/ui/src/app/locale/locale.constant-fr_FR.json index fd25adced3..0a0e385350 100644 --- a/ui/src/app/locale/locale.constant-fr_FR.json +++ b/ui/src/app/locale/locale.constant-fr_FR.json @@ -1429,7 +1429,7 @@ "rulechain-required": "Chaîne de règles requise", "rulechains": "Chaînes de règles", "select-rulechain": "Sélectionner la chaîne de règles", - "system-rulechains": "Chaînes de règles du système", + "core-rulechains": "Chaînes de règles fondamentales", "edge-rulechains": "Chaînes de règles de la bordure", "set-root": "Rend la chaîne de règles racine (root) ", "set-root-rulechain-text": "Après la confirmation, la chaîne de règles deviendra racine (root) et gérera tous les messages de transport entrants.", diff --git a/ui/src/app/rulechain/rulechain.controller.js b/ui/src/app/rulechain/rulechain.controller.js index edeefb18e4..292c3f71c1 100644 --- a/ui/src/app/rulechain/rulechain.controller.js +++ b/ui/src/app/rulechain/rulechain.controller.js @@ -1270,7 +1270,7 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time vm.isImport = false; $mdUtil.nextTick(() => { if (vm.ruleChain.type === vm.types.systemRuleChainType) { - $state.go('home.ruleChains.system.ruleChain', {ruleChainId: vm.ruleChain.id.id}); + $state.go('home.ruleChains.core.ruleChain', {ruleChainId: vm.ruleChain.id.id}); } else { $state.go('home.ruleChains.edge.ruleChain', {ruleChainId: vm.ruleChain.id.id}); } diff --git a/ui/src/app/rulechain/rulechain.routes.js b/ui/src/app/rulechain/rulechain.routes.js index 8f12031873..2991000e58 100644 --- a/ui/src/app/rulechain/rulechain.routes.js +++ b/ui/src/app/rulechain/rulechain.routes.js @@ -31,12 +31,12 @@ export default function RuleChainRoutes($stateProvider, NodeTemplatePathProvider url: '/ruleChains', module: 'private', auth: ['SYS_ADMIN', 'TENANT_ADMIN'], - redirectTo: 'home.ruleChains.system', + redirectTo: 'home.ruleChains.core', ncyBreadcrumb: { label: '{"icon": "settings_ethernet", "label": "rulechain.rulechains"}' } }) - .state('home.ruleChains.system', { + .state('home.ruleChains.core', { url: '/ruleChains/system', params: {'topIndex': 0}, module: 'private', @@ -50,13 +50,13 @@ export default function RuleChainRoutes($stateProvider, NodeTemplatePathProvider }, data: { searchEnabled: true, - pageTitle: 'rulechain.system-rulechains', + pageTitle: 'rulechain.core-rulechains', ruleChainsType: 'tenant' }, ncyBreadcrumb: { - label: '{"icon": "settings_ethernet", "label": "rulechain.system-rulechains"}' + label: '{"icon": "settings_ethernet", "label": "rulechain.core-rulechains"}' } - }).state('home.ruleChains.system.ruleChain', { + }).state('home.ruleChains.core.ruleChain', { url: '/:ruleChainId', reloadOnSearch: false, module: 'private', diff --git a/ui/src/app/rulechain/rulechains.controller.js b/ui/src/app/rulechain/rulechains.controller.js index 0261af3301..aba5c3bb2a 100644 --- a/ui/src/app/rulechain/rulechains.controller.js +++ b/ui/src/app/rulechain/rulechains.controller.js @@ -401,7 +401,7 @@ export default function RuleChainsController(ruleChainService, userService, edge } else if (vm.ruleChainsScope === 'edges') { $state.go('home.ruleChains.edge.ruleChain', {ruleChainId: ruleChain.id.id}); } else { - $state.go('home.ruleChains.system.ruleChain', {ruleChainId: ruleChain.id.id}); + $state.go('home.ruleChains.core.ruleChain', {ruleChainId: ruleChain.id.id}); } } diff --git a/ui/src/app/services/menu.service.js b/ui/src/app/services/menu.service.js index d8e975a226..0f53b54158 100644 --- a/ui/src/app/services/menu.service.js +++ b/ui/src/app/services/menu.service.js @@ -162,9 +162,9 @@ function Menu(userService, $state, $rootScope) { icon: 'settings_ethernet', pages: [ { - name: 'rulechain.system-rulechains', + name: 'rulechain.core-rulechains', type: 'link', - state: 'home.ruleChains.system', + state: 'home.ruleChains.core', icon: 'settings_ethernet' }, { @@ -229,9 +229,9 @@ function Menu(userService, $state, $rootScope) { name: 'rulechain.management', places: [ { - name: 'rulechain.system-rulechains', + name: 'rulechain.core-rulechains', icon: 'settings_ethernet', - state: 'home.ruleChains.system' + state: 'home.ruleChains.core' }, { name: 'rulechain.edge-rulechains', From 8d9a5875ece387dc29c08e90975d2c140b2a87ce Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Tue, 16 Jun 2020 16:43:10 +0300 Subject: [PATCH 49/52] added use system proxy properties --- .../rule/engine/rest/TbHttpClient.java | 73 +++++++++++++++---- .../rule/engine/rest/TbRestApiCallNode.java | 4 +- .../rest/TbRestApiCallNodeConfiguration.java | 4 +- .../static/rulenode/rulenode-core-config.js | 8 +- 4 files changed, 68 insertions(+), 21 deletions(-) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java index 8948d30081..57bde77f9c 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java @@ -26,7 +26,9 @@ import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.CredentialsProvider; import org.apache.http.conn.ssl.DefaultHostnameVerifier; import org.apache.http.impl.client.BasicCredentialsProvider; +import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; +import org.apache.http.impl.nio.client.HttpAsyncClients; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; @@ -47,6 +49,8 @@ import org.thingsboard.server.common.msg.TbMsgMetaData; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLException; +import java.net.Authenticator; +import java.net.PasswordAuthentication; import java.security.NoSuchAlgorithmException; import java.util.Deque; import java.util.concurrent.ConcurrentLinkedDeque; @@ -61,6 +65,7 @@ class TbHttpClient { private static final String STATUS_REASON = "statusReason"; private static final String ERROR = "error"; private static final String ERROR_BODY = "error_body"; + private static final String ERROR_SYSTEM_PROPERTIES = "Didn't set any system proxy properties. Should be added next system proxy properties: \"http.proxyHost\" and \"http.proxyPort\" or \"https.proxyHost\" and \"https.proxyPort\" or \"socksProxyHost\" and \"socksProxyPort\""; private final TbRestApiCallNodeConfiguration config; @@ -79,22 +84,48 @@ class TbHttpClient { checkProxyHost(config.getProxyHost()); checkProxyPort(config.getProxyPort()); - HttpAsyncClientBuilder httpAsyncClientBuilder = HttpAsyncClientBuilder.create() - .setSSLHostnameVerifier(new DefaultHostnameVerifier()) - .setSSLContext(SSLContext.getDefault()) - .setProxy(new HttpHost(config.getProxyHost(), config.getProxyPort())); - - if (!StringUtils.isEmpty(config.getProxyUser()) && !StringUtils.isEmpty(config.getProxyPassword())) { - CredentialsProvider credsProvider = new BasicCredentialsProvider(); - credsProvider.setCredentials( - new AuthScope(config.getProxyHost(), config.getProxyPort()), - new UsernamePasswordCredentials(config.getProxyUser(), config.getProxyPassword()) - ); - httpAsyncClientBuilder.setDefaultCredentialsProvider(credsProvider); - } + String proxyUser; + String proxyPassword; + CloseableHttpAsyncClient asyncClient; HttpComponentsAsyncClientHttpRequestFactory requestFactory = new HttpComponentsAsyncClientHttpRequestFactory(); - requestFactory.setAsyncClient(httpAsyncClientBuilder.build()); + + if (config.isUseSystemProxyProperties()) { + checkSystemProxyProperties(); + + asyncClient = HttpAsyncClients.createSystem(); + + proxyUser = System.getProperty("tb.proxy.user"); + proxyPassword = System.getProperty("tb.proxy.password"); + + if (useAuth(proxyUser, proxyPassword)) { + Authenticator.setDefault(new Authenticator() { + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(proxyUser, proxyPassword.toCharArray()); + } + }); + } + } else { + HttpAsyncClientBuilder httpAsyncClientBuilder = HttpAsyncClientBuilder.create() + .setSSLHostnameVerifier(new DefaultHostnameVerifier()) + .setSSLContext(SSLContext.getDefault()) + .setProxy(new HttpHost(config.getProxyHost(), config.getProxyPort(), config.getProxyScheme())); + + proxyUser = config.getProxyUser(); + proxyPassword = config.getProxyPassword(); + + if (useAuth(proxyUser, proxyPassword)) { + CredentialsProvider credsProvider = new BasicCredentialsProvider(); + credsProvider.setCredentials( + new AuthScope(config.getProxyHost(), config.getProxyPort()), + new UsernamePasswordCredentials(proxyUser, proxyPassword) + ); + httpAsyncClientBuilder.setDefaultCredentialsProvider(credsProvider); + } + asyncClient = httpAsyncClientBuilder.build(); + } + + requestFactory.setAsyncClient(asyncClient); requestFactory.setReadTimeout(config.getReadTimeoutMs()); httpClient = new AsyncRestTemplate(requestFactory); } else if (config.isUseSimpleClientHttpFactory()) { @@ -111,6 +142,20 @@ class TbHttpClient { } } + private void checkSystemProxyProperties() throws TbNodeException { + boolean useHttpProxy = !StringUtils.isEmpty(System.getProperty("http.proxyHost")) && !StringUtils.isEmpty(System.getProperty("http.proxyPort")); + boolean useHttpsProxy = !StringUtils.isEmpty(System.getProperty("https.proxyHost")) && !StringUtils.isEmpty(System.getProperty("https.proxyPort")); + boolean useSocksProxy = !StringUtils.isEmpty(System.getProperty("socksProxyHost")) && !StringUtils.isEmpty(System.getProperty("socksProxyPort")); + if (!(useHttpProxy || useHttpsProxy || useSocksProxy)) { + log.warn(ERROR_SYSTEM_PROPERTIES); + throw new TbNodeException(ERROR_SYSTEM_PROPERTIES); + } + } + + private boolean useAuth(String proxyUser, String proxyPassword) { + return !StringUtils.isEmpty(proxyUser) && !StringUtils.isEmpty(proxyPassword); + } + void destroy() { if (this.eventLoopGroup != null) { this.eventLoopGroup.shutdownGracefully(0, 5, TimeUnit.SECONDS); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNode.java index 9cb171d0dc..51363866bf 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNode.java @@ -36,7 +36,9 @@ import org.thingsboard.server.common.msg.TbMsg; " Outbound message will contain response fields " + "(status, statusCode, statusReason and response headers) in the Message Metadata." + " Response body saved in outbound Message payload. " + - "For example statusCode field can be accessed with metadata.statusCode.", + "For example statusCode field can be accessed with metadata.statusCode." + + "
Note- if you use system proxy properties, the next system proxy properties should be added: \"http.proxyHost\" and \"http.proxyPort\" or \"https.proxyHost\" and \"https.proxyPort\" or \"socksProxyHost\" and \"socksProxyPort\"," + + "and if your proxy with auth, the next ones should be added: \"tb.proxy.user\" and \"tb.proxy.password\" to the thingsboard.conf file.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeRestApiCallConfig", iconUrl = "data:image/svg+xml;base64,PHN2ZyBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbDpzcGFjZT0icHJlc2VydmUiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiB2ZXJzaW9uPSIxLjEiIHk9IjBweCIgeD0iMHB4Ij48ZyB0cmFuc2Zvcm09Im1hdHJpeCguOTQ5NzUgMCAwIC45NDk3NSAxNy4xMiAyNi40OTIpIj48cGF0aCBkPSJtMTY5LjExIDEwOC41NGMtOS45MDY2IDAuMDczNC0xOS4wMTQgNi41NzI0LTIyLjAxNCAxNi40NjlsLTY5Ljk5MyAyMzEuMDhjLTMuNjkwNCAxMi4xODEgMy4yODkyIDI1LjIyIDE1LjQ2OSAyOC45MSAyLjIyNTkgMC42NzQ4MSA0LjQ5NjkgMSA2LjcyODUgMSA5Ljk3MjEgMCAxOS4xNjUtNi41MTUzIDIyLjE4Mi0xNi40NjdhNi41MjI0IDYuNTIyNCAwIDAgMCAwLjAwMiAtMC4wMDJsNjkuOTktMjMxLjA3YTYuNTIyNCA2LjUyMjQgMCAwIDAgMCAtMC4wMDJjMy42ODU1LTEyLjE4MS0zLjI4Ny0yNS4yMjUtMTUuNDcxLTI4LjkxMi0yLjI4MjUtMC42OTE0NS00LjYxMTYtMS4wMTY5LTYuODk4NC0xem04NC45ODggMGMtOS45MDQ4IDAuMDczNC0xOS4wMTggNi41Njc1LTIyLjAxOCAxNi40NjlsLTY5Ljk4NiAyMzEuMDhjLTMuNjg5OCAxMi4xNzkgMy4yODUzIDI1LjIxNyAxNS40NjUgMjguOTA4IDIuMjI5NyAwLjY3NjQ3IDQuNTAwOCAxLjAwMiA2LjczMjQgMS4wMDIgOS45NzIxIDAgMTkuMTY1LTYuNTE1MyAyMi4xODItMTYuNDY3YTYuNTIyNCA2LjUyMjQgMCAwIDAgMC4wMDIgLTAuMDAybDY5Ljk4OC0yMzEuMDdjMy42OTA4LTEyLjE4MS0zLjI4NTItMjUuMjIzLTE1LjQ2Ny0yOC45MTItMi4yODE0LTAuNjkyMzEtNC42MTA4LTEuMDE4OS02Ljg5ODQtMS4wMDJ6bS0yMTcuMjkgNDIuMjNjLTEyLjcyOS0wLjAwMDg3LTIzLjE4OCAxMC40NTYtMjMuMTg4IDIzLjE4NiAwLjAwMSAxMi43MjggMTAuNDU5IDIzLjE4NiAyMy4xODggMjMuMTg2IDEyLjcyNy0wLjAwMSAyMy4xODMtMTAuNDU5IDIzLjE4NC0yMy4xODYgMC4wMDA4NzYtMTIuNzI4LTEwLjQ1Ni0yMy4xODUtMjMuMTg0LTIzLjE4NnptMCAxNDYuNjRjLTEyLjcyNy0wLjAwMDg3LTIzLjE4NiAxMC40NTUtMjMuMTg4IDIzLjE4NC0wLjAwMDg3MyAxMi43MjkgMTAuNDU4IDIzLjE4OCAyMy4xODggMjMuMTg4IDEyLjcyOC0wLjAwMSAyMy4xODQtMTAuNDYgMjMuMTg0LTIzLjE4OC0wLjAwMS0xMi43MjYtMTAuNDU3LTIzLjE4My0yMy4xODQtMjMuMTg0em0yNzAuNzkgNDIuMjExYy0xMi43MjcgMC0yMy4xODQgMTAuNDU3LTIzLjE4NCAyMy4xODRzMTAuNDU1IDIzLjE4OCAyMy4xODQgMjMuMTg4aDE1NC45OGMxMi43MjkgMCAyMy4xODYtMTAuNDYgMjMuMTg2LTIzLjE4OCAwLjAwMS0xMi43MjgtMTAuNDU4LTIzLjE4NC0yMy4xODYtMjMuMTg0eiIgdHJhbnNmb3JtPSJtYXRyaXgoMS4wMzc2IDAgMCAxLjAzNzYgLTcuNTY3NiAtMTQuOTI1KSIgc3Ryb2tlLXdpZHRoPSIxLjI2OTMiLz48L2c+PC9zdmc+" diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeConfiguration.java index 6e9a9af500..3cccb2c2c5 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeConfiguration.java @@ -18,7 +18,6 @@ package org.thingsboard.rule.engine.rest; import lombok.Data; import org.thingsboard.rule.engine.api.NodeConfiguration; -import java.net.Proxy; import java.util.Collections; import java.util.Map; @@ -35,11 +34,12 @@ public class TbRestApiCallNodeConfiguration implements NodeConfiguration
tb.rulenode.customer-name-pattern-required
tb.rulenode.customer-name-pattern-hint
{{ 'tb.rulenode.create-customer-if-not-exists' | translate }}
tb.rulenode.customer-cache-expiration-required
tb.rulenode.customer-cache-expiration-range
tb.rulenode.customer-cache-expiration-hint
"},function(e,t){e.exports='
{{scope.name | translate}}
'},function(e,t){e.exports="
tb.rulenode.select-queue-hint
"},function(e,t){e.exports="
{{ 'tb.rulenode.test-details-function' | translate }}
tb.rulenode.alarm-type-required
tb.rulenode.entity-type-pattern-hint
"},function(e,t){e.exports="
{{ 'tb.rulenode.test-details-function' | translate }}
{{ 'tb.rulenode.use-message-alarm-data' | translate }}
tb.rulenode.alarm-type-required
tb.rulenode.entity-type-pattern-hint
{{ severity.name | translate}}
tb.rulenode.alarm-severity-required
{{ 'tb.rulenode.propagate' | translate }}
tb.rulenode.relation-types-list-hint
"},function(e,t){e.exports="
{{ ('relation.search-direction.' + direction) | translate}}
tb.rulenode.entity-name-pattern-required
tb.rulenode.entity-name-pattern-hint
tb.rulenode.entity-type-pattern-required
tb.rulenode.entity-type-pattern-hint
tb.rulenode.relation-type-pattern-required
tb.rulenode.relation-type-pattern-hint
{{ 'tb.rulenode.create-entity-if-not-exists' | translate }}
tb.rulenode.create-entity-if-not-exists-hint
{{ 'tb.rulenode.remove-current-relations' | translate }}
tb.rulenode.remove-current-relations-hint
{{ 'tb.rulenode.change-originator-to-related-entity' | translate }}
tb.rulenode.change-originator-to-related-entity-hint
tb.rulenode.entity-cache-expiration-required
tb.rulenode.entity-cache-expiration-range
tb.rulenode.entity-cache-expiration-hint
"},function(e,t){e.exports="
{{ 'tb.rulenode.delete-relation-to-specific-entity' | translate }}
tb.rulenode.delete-relation-hint
{{ ('relation.search-direction.' + direction) | translate}}
tb.rulenode.entity-name-pattern-required
tb.rulenode.entity-name-pattern-hint
tb.rulenode.relation-type-pattern-required
tb.rulenode.relation-type-pattern-hint
tb.rulenode.entity-cache-expiration-required
tb.rulenode.entity-cache-expiration-range
tb.rulenode.entity-cache-expiration-hint
"},function(e,t){e.exports="
tb.rulenode.message-count-required
tb.rulenode.min-message-count-message
tb.rulenode.period-seconds-required
tb.rulenode.min-period-seconds-message
{{ 'tb.rulenode.test-generator-function' | translate }}
"},function(e,t){e.exports='
tb.rulenode.latitude-key-name-required
tb.rulenode.longitude-key-name-required
{{ \'tb.rulenode.fetch-perimeter-info-from-message-metadata\' | translate }}
{{ type.name | translate}}
tb.rulenode.circle-center-latitude-required
tb.rulenode.circle-center-longitude-required
tb.rulenode.range-required
{{ type.name | translate}}
tb.rulenode.polygon-definition-required
tb.rulenode.polygon-definition-hint
tb.rulenode.min-inside-duration-value-required
tb.rulenode.time-value-range
tb.rulenode.time-value-range
{{timeUnit.name | translate}}
tb.rulenode.min-outside-duration-value-required
tb.rulenode.time-value-range
tb.rulenode.time-value-range
{{timeUnit.name | translate}}
'},function(e,t){e.exports='
tb.rulenode.topic-pattern-required
tb.rulenode.bootstrap-servers-required
tb.rulenode.min-retries-message
tb.rulenode.min-batch-size-bytes-message
tb.rulenode.min-linger-ms-message
tb.rulenode.min-buffer-memory-bytes-message
{{ ackValue }}
tb.rulenode.key-serializer-required
tb.rulenode.value-serializer-required
{{ \'tb.rulenode.add-metadata-key-values-as-kafka-headers\' | translate }}
tb.rulenode.add-metadata-key-values-as-kafka-headers-hint
{{charset.name | translate}}
'},function(e,t){e.exports="
{{ 'tb.rulenode.test-to-string-function' | translate }}
"},function(e,t){e.exports='
tb.rulenode.topic-pattern-required
tb.rulenode.mqtt-topic-pattern-hint
tb.rulenode.host-required
tb.rulenode.port-required
tb.rulenode.port-range
tb.rulenode.port-range
tb.rulenode.connect-timeout-required
tb.rulenode.connect-timeout-range
tb.rulenode.connect-timeout-range
{{ \'tb.rulenode.clean-session\' | translate }} {{ \'tb.rulenode.enable-ssl\' | translate }}
{{ \'tb.rulenode.credentials\' | translate }}
{{ ruleNodeTypes.mqttCredentialTypes[configuration.credentials.type].name | translate }}
{{ \'tb.rulenode.credentials\' | translate }}
{{ ruleNodeTypes.mqttCredentialTypes[configuration.credentials.type].name | translate }}
{{credentialsValue.name | translate}}
tb.rulenode.credentials-type-required
tb.rulenode.username-required
tb.rulenode.password-required
'; -},function(e,t){e.exports="
tb.rulenode.interval-seconds-required
tb.rulenode.min-interval-seconds-message
tb.rulenode.output-timeseries-key-prefix-required
"},function(e,t){e.exports='
{{ \'tb.rulenode.use-metadata-period-in-seconds-patterns\' | translate }}
tb.rulenode.use-metadata-period-in-seconds-patterns-hint
tb.rulenode.period-seconds-required
tb.rulenode.min-period-0-seconds-message
tb.rulenode.period-in-seconds-pattern-required
tb.rulenode.period-in-seconds-pattern-hint
tb.rulenode.max-pending-messages-required
tb.rulenode.max-pending-messages-range
tb.rulenode.max-pending-messages-range
'},function(e,t){e.exports="
tb.rulenode.gcp-project-id-required
tb.rulenode.pubsub-topic-name-required
{{ 'action.remove' | translate }} close
tb.rulenode.message-attributes-hint
"},function(e,t){e.exports='
{{ property }}
tb.rulenode.host-required
tb.rulenode.port-required
tb.rulenode.port-range
tb.rulenode.port-range
{{ \'tb.rulenode.automatic-recovery\' | translate }}
tb.rulenode.min-connection-timeout-ms-message
tb.rulenode.min-handshake-timeout-ms-message
'},function(e,t){e.exports='
tb.rulenode.endpoint-url-pattern-required
tb.rulenode.endpoint-url-pattern-hint
{{ type }} {{ \'tb.rulenode.enable-proxy\' | translate }} {{ \'tb.rulenode.use-simple-client-http-factory\' | translate }}
tb.rulenode.proxy-host-required
tb.rulenode.proxy-port-required
tb.rulenode.proxy-port-range
tb.rulenode.proxy-port-range
tb.rulenode.read-timeout-hint
tb.rulenode.max-parallel-requests-count-hint
tb.rulenode.headers-hint
{{ \'tb.rulenode.use-redis-queue\' | translate }}
{{ \'tb.rulenode.trim-redis-queue\' | translate }}
'},function(e,t){e.exports="
"},function(e,t){e.exports="
tb.rulenode.timeout-required
tb.rulenode.min-timeout-message
"},function(e,t){e.exports='
tb.rulenode.custom-table-name-required
tb.rulenode.custom-table-hint
'},function(e,t){e.exports='
{{ \'tb.rulenode.use-system-smtp-settings\' | translate }}
{{smtpProtocol.toUpperCase()}}
tb.rulenode.smtp-host-required
tb.rulenode.smtp-port-required
tb.rulenode.smtp-port-range
tb.rulenode.smtp-port-range
tb.rulenode.timeout-required
tb.rulenode.min-timeout-msec-message
{{ \'tb.rulenode.enable-tls\' | translate }} {{tlsVersion}} {{ \'tb.rulenode.enable-proxy\' | translate }}
tb.rulenode.proxy-host-required
tb.rulenode.proxy-port-required
tb.rulenode.proxy-port-range
tb.rulenode.proxy-port-range
'},function(e,t){e.exports="
tb.rulenode.topic-arn-pattern-required
tb.rulenode.topic-arn-pattern-hint
tb.rulenode.aws-access-key-id-required
tb.rulenode.aws-secret-access-key-required
tb.rulenode.aws-region-required
"},function(e,t){e.exports='
{{ type.name | translate }}
tb.rulenode.queue-url-pattern-required
tb.rulenode.queue-url-pattern-hint
tb.rulenode.min-delay-seconds-message
tb.rulenode.max-delay-seconds-message
tb.rulenode.message-attributes-hint
tb.rulenode.aws-access-key-id-required
tb.rulenode.aws-secret-access-key-required
tb.rulenode.aws-region-required
'},function(e,t){e.exports="
tb.rulenode.default-ttl-required
tb.rulenode.min-default-ttl-message
"},function(e,t){e.exports="
tb.rulenode.customer-name-pattern-required
tb.rulenode.customer-name-pattern-hint
tb.rulenode.customer-cache-expiration-required
tb.rulenode.customer-cache-expiration-range
tb.rulenode.customer-cache-expiration-hint
"},function(e,t){e.exports="
{{ 'alias.last-level-relation' | translate}}
{{ ('relation.search-direction.' + direction) | translate}}
relation.relation-type
device.device-types
"},function(e,t){e.exports="
{{ 'tb.rulenode.latest-telemetry' | translate }}
"},function(e,t){e.exports='
{{ \'tb.rulenode.tell-failure-if-absent\' | translate }}
tb.rulenode.tell-failure-if-absent-hint
{{ \'tb.rulenode.get-latest-value-with-ts\' | translate }}
tb.rulenode.get-latest-value-with-ts-hint
'},function(e,t){e.exports='
{{\'tb.rulenode.entity-details-\'+item.toLowerCase() | translate}} tb.rulenode.no-entity-details-matching {{\'tb.rulenode.entity-details-\'+$chip.toLowerCase() | translate}} {{ \'tb.rulenode.add-to-metadata\' | translate }}
tb.rulenode.add-to-metadata-hint
'},function(e,t){e.exports='
{{ type }}
tb.rulenode.fetch-mode-hint
{{ type }}
tb.rulenode.order-by-hint
tb.rulenode.limit-hint
{{ \'tb.rulenode.use-metadata-interval-patterns\' | translate }}
tb.rulenode.use-metadata-interval-patterns-hint
tb.rulenode.start-interval-value-required
tb.rulenode.time-value-range
tb.rulenode.time-value-range
{{timeUnit.name | translate}}
tb.rulenode.end-interval-value-required
tb.rulenode.time-value-range
tb.rulenode.time-value-range
{{timeUnit.name | translate}}
tb.rulenode.start-interval-pattern-required
tb.rulenode.start-interval-pattern-hint
tb.rulenode.end-interval-pattern-required
tb.rulenode.end-interval-pattern-hint
'; -},function(e,t){e.exports='
{{ \'tb.rulenode.tell-failure-if-absent\' | translate }}
tb.rulenode.tell-failure-if-absent-hint
{{ \'tb.rulenode.get-latest-value-with-ts\' | translate }}
tb.rulenode.get-latest-value-with-ts-hint
'},function(e,t){e.exports='
'},function(e,t){e.exports="
{{ 'tb.rulenode.latest-telemetry' | translate }}
"},32,function(e,t){e.exports="
{{'alarm.display-status.' + item | translate}} {{'alarm.display-status.' + $chip | translate}}
"},function(e,t){e.exports='
tb.rulenode.separator-hint
tb.rulenode.separator-hint
{{ \'tb.rulenode.check-all-keys\' | translate }}
tb.rulenode.check-all-keys-hint
'},function(e,t){e.exports="
{{ 'tb.rulenode.check-relation-to-specific-entity' | translate }}
tb.rulenode.check-relation-hint
{{ ('relation.search-direction.' + direction) | translate}}
"},function(e,t){e.exports='
tb.rulenode.latitude-key-name-required
tb.rulenode.longitude-key-name-required
{{ \'tb.rulenode.fetch-perimeter-info-from-message-metadata\' | translate }}
{{ type.name | translate}}
tb.rulenode.circle-center-latitude-required
tb.rulenode.circle-center-longitude-required
tb.rulenode.range-required
{{ type.name | translate}}
tb.rulenode.polygon-definition-required
tb.rulenode.polygon-definition-hint
'},function(e,t){e.exports='
{{item}}
tb.rulenode.no-message-types-found
tb.rulenode.no-message-type-matching tb.rulenode.create-new-message-type
{{$chip.name}}
'},function(e,t){e.exports='
'},function(e,t){e.exports="
{{ 'tb.rulenode.test-filter-function' | translate }}
"},function(e,t){e.exports="
{{ 'tb.rulenode.test-switch-function' | translate }}
"},function(e,t){e.exports='
{{ keyText }} {{ valText }}  
{{keyRequiredText}}
{{valRequiredText}}
{{ \'tb.key-val.remove-entry\' | translate }} close
{{ \'tb.key-val.add-entry\' | translate }} add {{ \'action.add\' | translate }}
'},function(e,t){e.exports="
{{ 'alias.last-level-relation' | translate}}
{{ ('relation.search-direction.' + direction) | translate}}
relation.relation-filters
"},function(e,t){e.exports='
{{ source.name | translate}}
'},function(e,t){e.exports="
{{ 'tb.rulenode.test-transformer-function' | translate }}
"},function(e,t){e.exports="
tb.rulenode.from-template-required
tb.rulenode.from-template-hint
tb.rulenode.to-template-required
tb.rulenode.mail-address-list-template-hint
tb.rulenode.mail-address-list-template-hint
tb.rulenode.mail-address-list-template-hint
tb.rulenode.subject-template-required
tb.rulenode.subject-template-hint
tb.rulenode.body-template-required
tb.rulenode.body-template-hint
"},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(6),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(7),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.serviceType="TB_RULE_ENGINE",n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(8),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t,n,a){var i=function(i,r,l,s){var d=o.default;r.html(d),i.types=n,i.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(i.configuration)}),s.$render=function(){i.configuration=s.$viewValue},i.testDetailsBuildJs=function(e){var n=angular.copy(i.configuration.alarmDetailsBuildJs);a.testNodeScript(e,n,"json",t.instant("tb.rulenode.details")+"","Details",["msg","metadata","msgType"],i.ruleNodeId).then(function(e){i.configuration.alarmDetailsBuildJs=e,s.$setDirty()})},e(r.contents())(i)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:i}}i.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(9),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t,n,a){var i=function(i,r,l,s){var d=o.default;r.html(d),i.types=n,i.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(i.configuration)}),s.$render=function(){i.configuration=s.$viewValue,i.configuration.hasOwnProperty("relationTypes")||(i.configuration.relationTypes=[])},i.testDetailsBuildJs=function(e){var n=angular.copy(i.configuration.alarmDetailsBuildJs);a.testNodeScript(e,n,"json",t.instant("tb.rulenode.details")+"","Details",["msg","metadata","msgType"],i.ruleNodeId).then(function(e){i.configuration.alarmDetailsBuildJs=e,s.$setDirty()})},e(r.contents())(i)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:i}}i.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(10),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(11),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(12),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t,n,a){var i=function(i,r,l,s){var d=o.default;r.html(d),i.types=n,i.originator=null,i.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(i.configuration)}),s.$render=function(){i.configuration=s.$viewValue,i.configuration.originatorId&&i.configuration.originatorType?i.originator={id:i.configuration.originatorId,entityType:i.configuration.originatorType}:i.originator=null,i.$watch("originator",function(e,t){angular.equals(e,t)||(i.originator?(s.$viewValue.originatorId=i.originator.id,s.$viewValue.originatorType=i.originator.entityType):(s.$viewValue.originatorId=null,s.$viewValue.originatorType=null))},!0)},i.testScript=function(e){var n=angular.copy(i.configuration.jsScript);a.testNodeScript(e,n,"generate",t.instant("tb.rulenode.generator")+"","Generate",["prevMsg","prevMetadata","prevMsgType"],i.ruleNodeId).then(function(e){i.configuration.jsScript=e,s.$setDirty()})},e(r.contents())(i)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:i}}i.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i,n(1);var r=n(13),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}i.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(14),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var i=n(77),r=a(i),o=n(54),l=a(o),s=n(60),d=a(s),u=n(57),c=a(u),m=n(56),g=a(m),p=n(64),f=a(p),b=n(71),v=a(b),y=n(72),h=a(y),q=n(70),x=a(q),k=n(63),$=a(k),T=n(75),C=a(T),w=n(76),M=a(w),N=n(69),S=a(N),_=n(65),P=a(_),F=n(74),E=a(F),A=n(67),V=a(A),I=n(66),j=a(I),O=n(53),D=a(O),L=n(78),R=a(L),K=n(59),U=a(K),z=n(58),H=a(z),B=n(73),G=a(B),Y=n(61),Q=a(Y),W=n(68),J=a(W),Z=n(55),X=a(Z);t.default=angular.module("thingsboard.ruleChain.config.action",[]).directive("tbActionNodeTimeseriesConfig",r.default).directive("tbActionNodeAttributesConfig",l.default).directive("tbActionNodeGeneratorConfig",d.default).directive("tbActionNodeCreateAlarmConfig",c.default).directive("tbActionNodeClearAlarmConfig",g.default).directive("tbActionNodeLogConfig",f.default).directive("tbActionNodeRpcReplyConfig",v.default).directive("tbActionNodeRpcRequestConfig",h.default).directive("tbActionNodeRestApiCallConfig",x.default).directive("tbActionNodeKafkaConfig",$.default).directive("tbActionNodeSnsConfig",C.default).directive("tbActionNodeSqsConfig",M.default).directive("tbActionNodeRabbitMqConfig",S.default).directive("tbActionNodeMqttConfig",P.default).directive("tbActionNodeSendEmailConfig",E.default).directive("tbActionNodeMsgDelayConfig",V.default).directive("tbActionNodeMsgCountConfig",j.default).directive("tbActionNodeAssignToCustomerConfig",D.default).directive("tbActionNodeUnAssignToCustomerConfig",R.default).directive("tbActionNodeDeleteRelationConfig",U.default).directive("tbActionNodeCreateRelationConfig",H.default).directive("tbActionNodeCustomTableConfig",G.default).directive("tbActionNodeGpsGeofencingConfig",Q.default).directive("tbActionNodePubSubConfig",J.default).directive("tbActionNodeCheckPointConfig",X.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.ackValues=["all","-1","0","1"],n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue,n.configuration.hasOwnProperty("kafkaHeadersCharset")||(n.configuration.kafkaHeadersCharset="UTF-8")},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(15),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t,n){var a=function(a,i,r,l){var s=o.default;i.html(s),a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},a.testScript=function(e){var i=angular.copy(a.configuration.jsScript);n.testNodeScript(e,i,"string",t.instant("tb.rulenode.to-string")+"","ToString",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,l.$setDirty()})},e(i.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}i.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(16),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t,n){var a=function(a,i,r,l){var s=o.default;i.html(s),a.$mdExpansionPanel=t,a.ruleNodeTypes=n,a.credentialsTypeChanged=function(){var e=a.configuration.credentials.type;a.configuration.credentials={},a.configuration.credentials.type=e,a.updateValidity()},a.certFileAdded=function(e,t){var n=new FileReader;n.onload=function(n){a.$apply(function(){if(n.target.result){l.$setDirty();var i=n.target.result;i&&i.length>0&&("caCert"==t&&(a.configuration.credentials.caCertFileName=e.name,a.configuration.credentials.caCert=i),"privateKey"==t&&(a.configuration.credentials.privateKeyFileName=e.name,a.configuration.credentials.privateKey=i),"Cert"==t&&(a.configuration.credentials.certFileName=e.name,a.configuration.credentials.cert=i)),a.updateValidity()}})},n.readAsText(e.file)},a.clearCertFile=function(e){l.$setDirty(),"caCert"==e&&(a.configuration.credentials.caCertFileName=null,a.configuration.credentials.caCert=null),"privateKey"==e&&(a.configuration.credentials.privateKeyFileName=null,a.configuration.credentials.privateKey=null),"Cert"==e&&(a.configuration.credentials.certFileName=null,a.configuration.credentials.cert=null),a.updateValidity()},a.updateValidity=function(){var e=!0,t=a.configuration.credentials;t.type==n.mqttCredentialTypes["cert.PEM"].value&&(t.caCert&&t.cert&&t.privateKey||(e=!1)),l.$setValidity("Certs",e)},a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},e(i.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:a}}i.$inject=["$compile","$mdExpansionPanel","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i,n(2);var r=n(17),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(18),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){ -var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(19),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.serviceAccountFileAdded=function(e){var t=new FileReader;t.onload=function(t){n.$apply(function(){if(t.target.result){r.$setDirty();var a=t.target.result;a&&a.length>0&&(n.configuration.serviceAccountKeyFileName=e.name,n.configuration.serviceAccountKey=a),n.updateValidity()}})},t.readAsText(e.file)},n.clearServiceAccountFile=function(){r.$setDirty(),n.configuration.serviceAccountKeyFileName=null,n.configuration.serviceAccountKey=null,n.updateValidity()},n.updateValidity=function(){var e=!0,t=n.configuration;t.serviceAccountKeyFileName&&t.serviceAccountKey||(e=!1),r.$setValidity("SAKey",e)},n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}i.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(20),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.messageProperties=[null,"BASIC","TEXT_PLAIN","MINIMAL_BASIC","MINIMAL_PERSISTENT_BASIC","PERSISTENT_BASIC","PERSISTENT_TEXT_PLAIN"],t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(21),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}i.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(22),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(23),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(24),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(25),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.smtpProtocols=["smtp","smtps"],t.tlsVersions=["TLSv1","TLSv1.1","TLSv1.2","TLSv1.3"],t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(26),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(27),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}i.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(28),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(29),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(30),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||r.$setViewValue(n.query)}),r.$render=function(){n.query=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(31),o=a(r)},function(e,t){"use strict";function n(e){var t=function(t,n,a,i){n.html("
"),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}n.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(32),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(33),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),n.entityDetailsList=[];for(var s in t.entityDetails){var d=s;n.entityDetailsList.push(d)}r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(34),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t,n){var a=function(a,i,r,l){var s=o.default;i.html(s);var d=186;a.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,d],a.ruleNodeTypes=n,a.aggPeriodTimeUnits={},a.aggPeriodTimeUnits.MINUTES=n.timeUnit.MINUTES,a.aggPeriodTimeUnits.HOURS=n.timeUnit.HOURS,a.aggPeriodTimeUnits.DAYS=n.timeUnit.DAYS,a.aggPeriodTimeUnits.MILLISECONDS=n.timeUnit.MILLISECONDS,a.aggPeriodTimeUnits.SECONDS=n.timeUnit.SECONDS,a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},e(i.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{},link:a}}i.$inject=["$compile","$mdConstant","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(35),o=a(r);n(3)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var i=n(86),r=a(i),o=n(87),l=a(o),s=n(82),d=a(s),u=n(88),c=a(u),m=n(81),g=a(m),p=n(89),f=a(p),b=n(84),v=a(b),y=n(83),h=a(y);t.default=angular.module("thingsboard.ruleChain.config.enrichment",[]).directive("tbEnrichmentNodeOriginatorAttributesConfig",r.default).directive("tbEnrichmentNodeOriginatorFieldsConfig",l.default).directive("tbEnrichmentNodeDeviceAttributesConfig",d.default).directive("tbEnrichmentNodeRelatedAttributesConfig",c.default).directive("tbEnrichmentNodeCustomerAttributesConfig",g.default).directive("tbEnrichmentNodeTenantAttributesConfig",f.default).directive("tbEnrichmentNodeGetTelemetryFromDatabase",v.default).directive("tbEnrichmentNodeEntityDetailsConfig",h.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(36),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(37),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(38),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(39),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),n.alarmStatusList=[];for(var s in t.alarmStatus)n.alarmStatusList.push(t.alarmStatus[s]);r.$render=function(){n.configuration=r.$viewValue},n.getAlarmStatusList=function(){return n.alarmStatusList.filter(function(e){return n.configuration.alarmStatusList.indexOf(e)===-1})},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{required:"=ngRequired",readonly:"=ngReadonly"},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(40),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(41),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(42),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}i.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(43),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var i=n(97),r=a(i),o=n(95),l=a(o),s=n(98),d=a(s),u=n(92),c=a(u),m=n(96),g=a(m),p=n(91),f=a(p),b=n(93),v=a(b),y=n(90),h=a(y);t.default=angular.module("thingsboard.ruleChain.config.filter",[]).directive("tbFilterNodeScriptConfig",r.default).directive("tbFilterNodeMessageTypeConfig",l.default).directive("tbFilterNodeSwitchConfig",d.default).directive("tbFilterNodeCheckRelationConfig",c.default).directive("tbFilterNodeOriginatorTypeConfig",g.default).directive("tbFilterNodeCheckMessageConfig",f.default).directive("tbFilterNodeGpsGeofencingConfig",v.default).directive("tbFilterNodeCheckAlarmStatusConfig",h.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t,n){var a=function(a,i,r,l){function s(){if(l.$viewValue){for(var e=[],t=0;t-1&&t.kvList.splice(e,1)}function l(){t.kvList||(t.kvList=[]),t.kvList.push({key:"",value:""})}function s(){var e={};t.kvList.forEach(function(t){t.key&&(e[t.key]=t.value)}),i.$setViewValue(e),d()}function d(){var e=!0;t.required&&!t.kvList.length&&(e=!1),i.$setValidity("kvMap",e)}var u=o.default;n.html(u),t.ngModelCtrl=i,t.removeKeyVal=r,t.addKeyVal=l,t.kvList=[],t.$watch("query",function(e,n){angular.equals(e,n)||i.$setViewValue(t.query)}),i.$render=function(){if(i.$viewValue){var e=i.$viewValue;t.kvList.length=0;for(var n in e)t.kvList.push({key:n,value:e[n]})}t.$watch("kvList",function(e,t){angular.equals(e,t)||s()},!0),d()},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{required:"=ngRequired",disabled:"=ngDisabled",requiredText:"=",keyText:"=",keyRequiredText:"=",valText:"=",valRequiredText:"="},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(48),o=a(r);n(5)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||r.$setViewValue(n.query)}),r.$render=function(){n.query=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(49),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(50),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var i=n(101),r=a(i),o=n(103),l=a(o),s=n(104),d=a(s);t.default=angular.module("thingsboard.ruleChain.config.transform",[]).directive("tbTransformationNodeChangeOriginatorConfig",r.default).directive("tbTransformationNodeScriptConfig",l.default).directive("tbTransformationNodeToEmailConfig",d.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t,n){var a=function(a,i,r,l){var s=o.default;i.html(s),a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},a.testScript=function(e){var i=angular.copy(a.configuration.jsScript);n.testNodeScript(e,i,"update",t.instant("tb.rulenode.transformer")+"","Transform",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,l.$setDirty()})},e(i.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}i.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(51),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(52),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var i=n(108),r=a(i),o=n(94),l=a(o),s=n(85),d=a(s),u=n(102),c=a(u),m=n(62),g=a(m),p=n(80),f=a(p),b=n(100),v=a(b),y=n(79),h=a(y),q=n(99),x=a(q),k=n(107),$=a(k);t.default=angular.module("thingsboard.ruleChain.config",[r.default,l.default,d.default,c.default,g.default]).directive("tbNodeEmptyConfig",f.default).directive("tbRelationsQueryConfig",v.default).directive("tbDeviceRelationsQueryConfig",h.default).directive("tbKvMapConfig",x.default).config($.default).name},function(e,t){"use strict";function n(e){var t={tb:{rulenode:{"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-name-pattern-hint":"Name pattern, use ${metaKeyName} to substitute variables from metadata","entity-type-pattern":"Type pattern","entity-type-pattern-required":"Type pattern is required","entity-type-pattern-hint":"Type pattern, use ${metaKeyName} to substitute variables from metadata","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-name-pattern-hint":"Customer name pattern, use ${metaKeyName} to substitute variables from metadata","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.","start-interval":"Start Interval","end-interval":"End Interval","start-interval-time-unit":"Start Interval Time Unit","end-interval-time-unit":"End Interval Time Unit","fetch-mode":"Fetch mode","fetch-mode-hint":"If selected fetch mode 'ALL' you able to choose telemetry sampling order.","order-by":"Order by","order-by-hint":"Select to choose telemetry sampling order.",limit:"Limit","limit-hint":"Min limit value is 2, max - 1000. In case you want to fetch a single entry, select fetch mode 'FIRST' or 'LAST'.","time-unit-milliseconds":"Milliseconds","time-unit-seconds":"Seconds","time-unit-minutes":"Minutes","time-unit-hours":"Hours","time-unit-days":"Days","time-value-range":"Time value should be in a range from 1 to 2147483647'.","start-interval-value-required":"Start interval value is required.","end-interval-value-required":"End interval value is required.",filter:"Filter",switch:"Switch","message-type":"Message type","message-type-required":"Message type is required.","message-types-filter":"Message types filter","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","latest-timeseries":"Latest timeseries","data-keys":"Message data","metadata-keys":"Message metadata","relations-query":"Relations query","device-relations-query":"Device relations query","max-relation-level":"Max relation level","relation-type-pattern":"Relation type pattern","relation-type-pattern-hint":"Relation type pattern, use ${metaKeyName} to substitute variables from metadata","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","attr-mapping":"Attributes mapping","source-attribute":"Source attribute","source-attribute-required":"Source attribute is required.","source-telemetry":"Source telemetry","source-telemetry-required":"Source telemetry is required.","target-attribute":"Target attribute","target-attribute-required":"Target attribute is required.","attr-mapping-required":"At least one attribute mapping should be specified.","fields-mapping":"Fields mapping","fields-mapping-required":"At least one field mapping should be specified.","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","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 metadata period in seconds pattern","use-metadata-period-in-seconds-patterns-hint":"If selected, rule node use period in seconds interval pattern from message metadata assuming that intervals are in the seconds.","period-in-seconds-pattern":"Period in seconds metadata pattern","period-in-seconds-pattern-required":"Period in seconds pattern is required","period-in-seconds-pattern-hint":"Period in seconds pattern, use ${metaKeyName} to substitute variables from metadata","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","alarm-type-required":"Alarm type is required.","alarm-severity":"Alarm severity","alarm-severity-required":"Alarm severity is required","alarm-statuses-filter":"Alarm statuses filter","alarm-statuses-required":"Alarm statuses is required",propagate:"Propagate",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","from-template-hint":"From address template, use ${metaKeyName} to substitute variables from metadata","to-template":"To Template","to-template-required":"To Template is required","mail-address-list-template-hint":"Comma separated address list, use ${metaKeyName} to substitute variables from metadata","cc-template":"Cc Template","bcc-template":"Bcc Template","subject-template":"Subject Template","subject-template-required":"Subject Template is required","subject-template-hint":"Mail subject template, use ${metaKeyName} to substitute variables from metadata","body-template":"Body Template","body-template-required":"Body Template is required","body-template-hint":"Mail body template, use ${metaKeyName} to substitute variables from metadata","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", -"endpoint-url-pattern-hint":"HTTP URL address pattern, use ${metaKeyName} to substitute variables from metadata","request-method":"Request method","use-simple-client-http-factory":"Use simple client HTTP factory","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 ${metaKeyName} in header/value fields to substitute variables from metadata",header:"Header","header-required":"Header is required",value:"Value","value-required":"Value is required","topic-pattern":"Topic pattern","topic-pattern-required":"Topic pattern is required","mqtt-topic-pattern-hint":"MQTT topic pattern, use ${metaKeyName} to substitute variables from metadata","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","topic-arn-pattern-hint":"Topic ARN pattern, use ${metaKeyName} to substitute variables from metadata","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","queue-url-pattern-hint":"Queue URL pattern, use ${metaKeyName} to substitute variables from metadata","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 ${metaKeyName} in name/value fields to substitute variables from metadata","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","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","username-required":"Username is required.","password-required":"Password is required.","ca-cert":"CA certificate file *","private-key":"Private key file *",cert:"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-interval-patterns":"Use metadata interval patterns","use-metadata-interval-patterns-hint":"If selected, rule node use start and end interval patterns from message metadata assuming that intervals are in the milliseconds.","use-message-alarm-data":"Use message alarm data","check-all-keys":"Check that all selected keys 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-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-pattern":"Start interval pattern","end-interval-pattern":"End interval pattern","start-interval-pattern-required":"Start interval pattern is required","end-interval-pattern-required":"End interval pattern is required","start-interval-pattern-hint":"Start interval pattern, use ${metaKeyName} to substitute variables from metadata","end-interval-pattern-hint":"End interval pattern, use ${metaKeyName} to substitute variables from metadata","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","enable-proxy":"Enable proxy","proxy-host":"Proxy host","proxy-host-required":"Proxy host is required.","proxy-port":"Proxy port","proxy-port-required":"You must supply a proxy port.","proxy-port-range":"Proxy port should be in a range from 1 to 65535.","proxy-user":"Proxy user","proxy-password":"Proxy password","tls-version":"TLS version","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":'You should press "enter" to complete field input.',"entity-details":"Select entity details:","entity-details-title":"Title","entity-details-country":"Country","entity-details-state":"State","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","add-to-metadata":"Add selected details to message metadata","add-to-metadata-hint":"If selected, adds the selected details keys to the message metadata instead of message data.","entity-details-list-empty":"No entity details 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":"You should 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-key-name":"Latitude key name","longitude-key-name":"Longitude key name","latitude-key-name-required":"Latitude key name is required.","longitude-key-name-required":"Longitude key name is required.","fetch-perimeter-info-from-message-metadata":"Fetch perimeter information from message metadata","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:"Range","range-required":"Range is required.","polygon-definition":"Polygon definition","polygon-definition-required":"Polygon definition is required.","polygon-definition-hint":"Please, 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 Latest telemetry with Timestamp","get-latest-value-with-ts-hint":'If selected, latest telemetry values will be added to the outbound message metadata with timestamp, e.g: "temp": "{\\"ts\\":1574329385897,\\"value\\":42}"',"use-redis-queue":"Use redis queue for message persistence","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."},"key-val":{key:"Key",value:"Value","remove-entry":"Remove entry","add-entry":"Add entry"}}};e.translations("en_US",t)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){(0,o.default)(e)}i.$inject=["$translateProvider"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(106),o=a(r)},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=angular.module("thingsboard.ruleChain.config.types",[]).constant("ruleNodeTypes",{originatorSource:{CUSTOMER:{name:"tb.rulenode.originator-customer",value:"CUSTOMER"},TENANT:{name:"tb.rulenode.originator-tenant",value:"TENANT"},RELATED:{name:"tb.rulenode.originator-related",value:"RELATED"},ALARM_ORIGINATOR:{name:"tb.rulenode.originator-alarm-originator",value:"ALARM_ORIGINATOR"}},fetchModeType:["FIRST","LAST","ALL"],samplingOrder:["ASC","DESC"],httpRequestType:["GET","POST","PUT","DELETE"],entityDetails:{TITLE:{name:"tb.rulenode.entity-details-title",value:"TITLE"},COUNTRY:{name:"tb.rulenode.entity-details-country",value:"COUNTRY"},STATE:{name:"tb.rulenode.entity-details-state",value:"STATE"},ZIP:{name:"tb.rulenode.entity-details-zip",value:"ZIP"},ADDRESS:{name:"tb.rulenode.entity-details-address",value:"ADDRESS"},ADDRESS2:{name:"tb.rulenode.entity-details-address2",value:"ADDRESS2"},PHONE:{name:"tb.rulenode.entity-details-phone",value:"PHONE"},EMAIL:{name:"tb.rulenode.entity-details-email",value:"EMAIL"},ADDITIONAL_INFO:{name:"tb.rulenode.entity-details-additional_info",value:"ADDITIONAL_INFO"}},sqsQueueType:{STANDARD:{name:"tb.rulenode.sqs-queue-standard",value:"STANDARD"},FIFO:{name:"tb.rulenode.sqs-queue-fifo",value:"FIFO"}},perimeterType:{CIRCLE:{name:"tb.rulenode.perimeter-circle",value:"CIRCLE"},POLYGON:{name:"tb.rulenode.perimeter-polygon",value:"POLYGON"}},timeUnit:{MILLISECONDS:{value:"MILLISECONDS",name:"tb.rulenode.time-unit-milliseconds"},SECONDS:{value:"SECONDS",name:"tb.rulenode.time-unit-seconds"},MINUTES:{value:"MINUTES",name:"tb.rulenode.time-unit-minutes"},HOURS:{value:"HOURS",name:"tb.rulenode.time-unit-hours"},DAYS:{value:"DAYS",name:"tb.rulenode.time-unit-days"}},rangeUnit:{METER:{value:"METER",name:"tb.rulenode.range-unit-meter"},KILOMETER:{value:"KILOMETER",name:"tb.rulenode.range-unit-kilometer"},FOOT:{value:"FOOT",name:"tb.rulenode.range-unit-foot"},MILE:{value:"MILE",name:"tb.rulenode.range-unit-mile"},NAUTICAL_MILE:{value:"NAUTICAL_MILE",name:"tb.rulenode.range-unit-nautical-mile"}},mqttCredentialTypes:{anonymous:{value:"anonymous",name:"tb.rulenode.credentials-anonymous"},basic:{value:"basic",name:"tb.rulenode.credentials-basic"},"cert.PEM":{value:"cert.PEM",name:"tb.rulenode.credentials-pem"}},toBytesStandartCharsetTypes:{"US-ASCII":{value:"US-ASCII",name:"tb.rulenode.charset-us-ascii"},"ISO-8859-1":{value:"ISO-8859-1",name:"tb.rulenode.charset-iso-8859-1"},"UTF-8":{value:"UTF-8",name:"tb.rulenode.charset-utf-8"},"UTF-16BE":{value:"UTF-16BE",name:"tb.rulenode.charset-utf-16be"},"UTF-16LE":{value:"UTF-16LE",name:"tb.rulenode.charset-utf-16le"},"UTF-16":{value:"UTF-16",name:"tb.rulenode.charset-utf-16"}}}).name}])); +},function(e,t){e.exports="
tb.rulenode.interval-seconds-required
tb.rulenode.min-interval-seconds-message
tb.rulenode.output-timeseries-key-prefix-required
"},function(e,t){e.exports='
{{ \'tb.rulenode.use-metadata-period-in-seconds-patterns\' | translate }}
tb.rulenode.use-metadata-period-in-seconds-patterns-hint
tb.rulenode.period-seconds-required
tb.rulenode.min-period-0-seconds-message
tb.rulenode.period-in-seconds-pattern-required
tb.rulenode.period-in-seconds-pattern-hint
tb.rulenode.max-pending-messages-required
tb.rulenode.max-pending-messages-range
tb.rulenode.max-pending-messages-range
'},function(e,t){e.exports="
tb.rulenode.gcp-project-id-required
tb.rulenode.pubsub-topic-name-required
{{ 'action.remove' | translate }} close
tb.rulenode.message-attributes-hint
"},function(e,t){e.exports='
{{ property }}
tb.rulenode.host-required
tb.rulenode.port-required
tb.rulenode.port-range
tb.rulenode.port-range
{{ \'tb.rulenode.automatic-recovery\' | translate }}
tb.rulenode.min-connection-timeout-ms-message
tb.rulenode.min-handshake-timeout-ms-message
'},function(e,t){e.exports='
tb.rulenode.endpoint-url-pattern-required
tb.rulenode.endpoint-url-pattern-hint
{{ type }} {{ \'tb.rulenode.enable-proxy\' | translate }} {{ \'tb.rulenode.use-simple-client-http-factory\' | translate }}
{{ \'tb.rulenode.use-system-proxy-properties\' | translate }}
{{proxyScheme}}
tb.rulenode.proxy-host-required
tb.rulenode.proxy-port-required
tb.rulenode.proxy-port-range
tb.rulenode.proxy-port-range
tb.rulenode.read-timeout-hint
tb.rulenode.max-parallel-requests-count-hint
tb.rulenode.headers-hint
{{ \'tb.rulenode.use-redis-queue\' | translate }}
{{ \'tb.rulenode.trim-redis-queue\' | translate }}
'},function(e,t){e.exports="
"},function(e,t){e.exports="
tb.rulenode.timeout-required
tb.rulenode.min-timeout-message
"},function(e,t){e.exports='
tb.rulenode.custom-table-name-required
tb.rulenode.custom-table-hint
'},function(e,t){e.exports='
{{ \'tb.rulenode.use-system-smtp-settings\' | translate }}
{{smtpProtocol.toUpperCase()}}
tb.rulenode.smtp-host-required
tb.rulenode.smtp-port-required
tb.rulenode.smtp-port-range
tb.rulenode.smtp-port-range
tb.rulenode.timeout-required
tb.rulenode.min-timeout-msec-message
{{ \'tb.rulenode.enable-tls\' | translate }} {{tlsVersion}} {{ \'tb.rulenode.enable-proxy\' | translate }}
tb.rulenode.proxy-host-required
tb.rulenode.proxy-port-required
tb.rulenode.proxy-port-range
tb.rulenode.proxy-port-range
'},function(e,t){e.exports="
tb.rulenode.topic-arn-pattern-required
tb.rulenode.topic-arn-pattern-hint
tb.rulenode.aws-access-key-id-required
tb.rulenode.aws-secret-access-key-required
tb.rulenode.aws-region-required
"},function(e,t){e.exports='
{{ type.name | translate }}
tb.rulenode.queue-url-pattern-required
tb.rulenode.queue-url-pattern-hint
tb.rulenode.min-delay-seconds-message
tb.rulenode.max-delay-seconds-message
tb.rulenode.message-attributes-hint
tb.rulenode.aws-access-key-id-required
tb.rulenode.aws-secret-access-key-required
tb.rulenode.aws-region-required
'},function(e,t){e.exports="
tb.rulenode.default-ttl-required
tb.rulenode.min-default-ttl-message
"},function(e,t){e.exports="
tb.rulenode.customer-name-pattern-required
tb.rulenode.customer-name-pattern-hint
tb.rulenode.customer-cache-expiration-required
tb.rulenode.customer-cache-expiration-range
tb.rulenode.customer-cache-expiration-hint
"},function(e,t){e.exports="
{{ 'alias.last-level-relation' | translate}}
{{ ('relation.search-direction.' + direction) | translate}}
relation.relation-type
device.device-types
"},function(e,t){e.exports="
{{ 'tb.rulenode.latest-telemetry' | translate }}
"},function(e,t){e.exports='
{{ \'tb.rulenode.tell-failure-if-absent\' | translate }}
tb.rulenode.tell-failure-if-absent-hint
{{ \'tb.rulenode.get-latest-value-with-ts\' | translate }}
tb.rulenode.get-latest-value-with-ts-hint
'},function(e,t){e.exports='
{{\'tb.rulenode.entity-details-\'+item.toLowerCase() | translate}} tb.rulenode.no-entity-details-matching {{\'tb.rulenode.entity-details-\'+$chip.toLowerCase() | translate}} {{ \'tb.rulenode.add-to-metadata\' | translate }}
tb.rulenode.add-to-metadata-hint
'; +},function(e,t){e.exports='
{{ type }}
tb.rulenode.fetch-mode-hint
{{ type }}
tb.rulenode.order-by-hint
tb.rulenode.limit-hint
{{ \'tb.rulenode.use-metadata-interval-patterns\' | translate }}
tb.rulenode.use-metadata-interval-patterns-hint
tb.rulenode.start-interval-value-required
tb.rulenode.time-value-range
tb.rulenode.time-value-range
{{timeUnit.name | translate}}
tb.rulenode.end-interval-value-required
tb.rulenode.time-value-range
tb.rulenode.time-value-range
{{timeUnit.name | translate}}
tb.rulenode.start-interval-pattern-required
tb.rulenode.start-interval-pattern-hint
tb.rulenode.end-interval-pattern-required
tb.rulenode.end-interval-pattern-hint
'},function(e,t){e.exports='
{{ \'tb.rulenode.tell-failure-if-absent\' | translate }}
tb.rulenode.tell-failure-if-absent-hint
{{ \'tb.rulenode.get-latest-value-with-ts\' | translate }}
tb.rulenode.get-latest-value-with-ts-hint
'},function(e,t){e.exports='
'},function(e,t){e.exports="
{{ 'tb.rulenode.latest-telemetry' | translate }}
"},32,function(e,t){e.exports="
{{'alarm.display-status.' + item | translate}} {{'alarm.display-status.' + $chip | translate}}
"},function(e,t){e.exports='
tb.rulenode.separator-hint
tb.rulenode.separator-hint
{{ \'tb.rulenode.check-all-keys\' | translate }}
tb.rulenode.check-all-keys-hint
'},function(e,t){e.exports="
{{ 'tb.rulenode.check-relation-to-specific-entity' | translate }}
tb.rulenode.check-relation-hint
{{ ('relation.search-direction.' + direction) | translate}}
"},function(e,t){e.exports='
tb.rulenode.latitude-key-name-required
tb.rulenode.longitude-key-name-required
{{ \'tb.rulenode.fetch-perimeter-info-from-message-metadata\' | translate }}
{{ type.name | translate}}
tb.rulenode.circle-center-latitude-required
tb.rulenode.circle-center-longitude-required
tb.rulenode.range-required
{{ type.name | translate}}
tb.rulenode.polygon-definition-required
tb.rulenode.polygon-definition-hint
'},function(e,t){e.exports='
{{item}}
tb.rulenode.no-message-types-found
tb.rulenode.no-message-type-matching tb.rulenode.create-new-message-type
{{$chip.name}}
'},function(e,t){e.exports='
'},function(e,t){e.exports="
{{ 'tb.rulenode.test-filter-function' | translate }}
"},function(e,t){e.exports="
{{ 'tb.rulenode.test-switch-function' | translate }}
"},function(e,t){e.exports='
{{ keyText }} {{ valText }}  
{{keyRequiredText}}
{{valRequiredText}}
{{ \'tb.key-val.remove-entry\' | translate }} close
{{ \'tb.key-val.add-entry\' | translate }} add {{ \'action.add\' | translate }}
'},function(e,t){e.exports="
{{ 'alias.last-level-relation' | translate}}
{{ ('relation.search-direction.' + direction) | translate}}
relation.relation-filters
"},function(e,t){e.exports='
{{ source.name | translate}}
'},function(e,t){e.exports="
{{ 'tb.rulenode.test-transformer-function' | translate }}
"},function(e,t){e.exports="
tb.rulenode.from-template-required
tb.rulenode.from-template-hint
tb.rulenode.to-template-required
tb.rulenode.mail-address-list-template-hint
tb.rulenode.mail-address-list-template-hint
tb.rulenode.mail-address-list-template-hint
tb.rulenode.subject-template-required
tb.rulenode.subject-template-hint
tb.rulenode.body-template-required
tb.rulenode.body-template-hint
"},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(6),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(7),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.serviceType="TB_RULE_ENGINE",n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(8),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t,n,a){var i=function(i,r,l,s){var d=o.default;r.html(d),i.types=n,i.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(i.configuration)}),s.$render=function(){i.configuration=s.$viewValue},i.testDetailsBuildJs=function(e){var n=angular.copy(i.configuration.alarmDetailsBuildJs);a.testNodeScript(e,n,"json",t.instant("tb.rulenode.details")+"","Details",["msg","metadata","msgType"],i.ruleNodeId).then(function(e){i.configuration.alarmDetailsBuildJs=e,s.$setDirty()})},e(r.contents())(i)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:i}}i.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(9),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t,n,a){var i=function(i,r,l,s){var d=o.default;r.html(d),i.types=n,i.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(i.configuration)}),s.$render=function(){i.configuration=s.$viewValue,i.configuration.hasOwnProperty("relationTypes")||(i.configuration.relationTypes=[])},i.testDetailsBuildJs=function(e){var n=angular.copy(i.configuration.alarmDetailsBuildJs);a.testNodeScript(e,n,"json",t.instant("tb.rulenode.details")+"","Details",["msg","metadata","msgType"],i.ruleNodeId).then(function(e){i.configuration.alarmDetailsBuildJs=e,s.$setDirty()})},e(r.contents())(i)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:i}}i.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(10),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(11),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(12),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t,n,a){var i=function(i,r,l,s){var d=o.default;r.html(d),i.types=n,i.originator=null,i.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(i.configuration)}),s.$render=function(){i.configuration=s.$viewValue,i.configuration.originatorId&&i.configuration.originatorType?i.originator={id:i.configuration.originatorId,entityType:i.configuration.originatorType}:i.originator=null,i.$watch("originator",function(e,t){angular.equals(e,t)||(i.originator?(s.$viewValue.originatorId=i.originator.id,s.$viewValue.originatorType=i.originator.entityType):(s.$viewValue.originatorId=null,s.$viewValue.originatorType=null))},!0)},i.testScript=function(e){var n=angular.copy(i.configuration.jsScript);a.testNodeScript(e,n,"generate",t.instant("tb.rulenode.generator")+"","Generate",["prevMsg","prevMetadata","prevMsgType"],i.ruleNodeId).then(function(e){i.configuration.jsScript=e,s.$setDirty()})},e(r.contents())(i)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:i}}i.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i,n(1);var r=n(13),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}i.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(14),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var i=n(77),r=a(i),o=n(54),l=a(o),s=n(60),d=a(s),u=n(57),c=a(u),m=n(56),g=a(m),p=n(64),f=a(p),b=n(71),v=a(b),y=n(72),h=a(y),q=n(70),x=a(q),k=n(63),$=a(k),T=n(75),C=a(T),w=n(76),M=a(w),S=n(69),N=a(S),P=n(65),_=a(P),F=n(74),E=a(F),A=n(67),V=a(A),I=n(66),j=a(I),O=n(53),D=a(O),L=n(78),R=a(L),K=n(59),U=a(K),z=n(58),H=a(z),B=n(73),G=a(B),Y=n(61),Q=a(Y),W=n(68),J=a(W),Z=n(55),X=a(Z); +t.default=angular.module("thingsboard.ruleChain.config.action",[]).directive("tbActionNodeTimeseriesConfig",r.default).directive("tbActionNodeAttributesConfig",l.default).directive("tbActionNodeGeneratorConfig",d.default).directive("tbActionNodeCreateAlarmConfig",c.default).directive("tbActionNodeClearAlarmConfig",g.default).directive("tbActionNodeLogConfig",f.default).directive("tbActionNodeRpcReplyConfig",v.default).directive("tbActionNodeRpcRequestConfig",h.default).directive("tbActionNodeRestApiCallConfig",x.default).directive("tbActionNodeKafkaConfig",$.default).directive("tbActionNodeSnsConfig",C.default).directive("tbActionNodeSqsConfig",M.default).directive("tbActionNodeRabbitMqConfig",N.default).directive("tbActionNodeMqttConfig",_.default).directive("tbActionNodeSendEmailConfig",E.default).directive("tbActionNodeMsgDelayConfig",V.default).directive("tbActionNodeMsgCountConfig",j.default).directive("tbActionNodeAssignToCustomerConfig",D.default).directive("tbActionNodeUnAssignToCustomerConfig",R.default).directive("tbActionNodeDeleteRelationConfig",U.default).directive("tbActionNodeCreateRelationConfig",H.default).directive("tbActionNodeCustomTableConfig",G.default).directive("tbActionNodeGpsGeofencingConfig",Q.default).directive("tbActionNodePubSubConfig",J.default).directive("tbActionNodeCheckPointConfig",X.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.ackValues=["all","-1","0","1"],n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue,n.configuration.hasOwnProperty("kafkaHeadersCharset")||(n.configuration.kafkaHeadersCharset="UTF-8")},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(15),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t,n){var a=function(a,i,r,l){var s=o.default;i.html(s),a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},a.testScript=function(e){var i=angular.copy(a.configuration.jsScript);n.testNodeScript(e,i,"string",t.instant("tb.rulenode.to-string")+"","ToString",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,l.$setDirty()})},e(i.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}i.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(16),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t,n){var a=function(a,i,r,l){var s=o.default;i.html(s),a.$mdExpansionPanel=t,a.ruleNodeTypes=n,a.credentialsTypeChanged=function(){var e=a.configuration.credentials.type;a.configuration.credentials={},a.configuration.credentials.type=e,a.updateValidity()},a.certFileAdded=function(e,t){var n=new FileReader;n.onload=function(n){a.$apply(function(){if(n.target.result){l.$setDirty();var i=n.target.result;i&&i.length>0&&("caCert"==t&&(a.configuration.credentials.caCertFileName=e.name,a.configuration.credentials.caCert=i),"privateKey"==t&&(a.configuration.credentials.privateKeyFileName=e.name,a.configuration.credentials.privateKey=i),"Cert"==t&&(a.configuration.credentials.certFileName=e.name,a.configuration.credentials.cert=i)),a.updateValidity()}})},n.readAsText(e.file)},a.clearCertFile=function(e){l.$setDirty(),"caCert"==e&&(a.configuration.credentials.caCertFileName=null,a.configuration.credentials.caCert=null),"privateKey"==e&&(a.configuration.credentials.privateKeyFileName=null,a.configuration.credentials.privateKey=null),"Cert"==e&&(a.configuration.credentials.certFileName=null,a.configuration.credentials.cert=null),a.updateValidity()},a.updateValidity=function(){var e=!0,t=a.configuration.credentials;t.type==n.mqttCredentialTypes["cert.PEM"].value&&(t.caCert&&t.cert&&t.privateKey||(e=!1)),l.$setValidity("Certs",e)},a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},e(i.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:a}}i.$inject=["$compile","$mdExpansionPanel","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i,n(2);var r=n(17),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(18),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(19),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.serviceAccountFileAdded=function(e){var t=new FileReader;t.onload=function(t){n.$apply(function(){if(t.target.result){r.$setDirty();var a=t.target.result;a&&a.length>0&&(n.configuration.serviceAccountKeyFileName=e.name,n.configuration.serviceAccountKey=a),n.updateValidity()}})},t.readAsText(e.file)},n.clearServiceAccountFile=function(){r.$setDirty(),n.configuration.serviceAccountKeyFileName=null,n.configuration.serviceAccountKey=null,n.updateValidity()},n.updateValidity=function(){var e=!0,t=n.configuration;t.serviceAccountKeyFileName&&t.serviceAccountKey||(e=!1),r.$setValidity("SAKey",e)},n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}i.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(20),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.messageProperties=[null,"BASIC","TEXT_PLAIN","MINIMAL_BASIC","MINIMAL_PERSISTENT_BASIC","PERSISTENT_BASIC","PERSISTENT_TEXT_PLAIN"],t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(21),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.proxySchemes=["http","https"],n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}i.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(22),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(23),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(24),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(25),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.smtpProtocols=["smtp","smtps"],t.tlsVersions=["TLSv1","TLSv1.1","TLSv1.2","TLSv1.3"],t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(26),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(27),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}i.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(28),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(29),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(30),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||r.$setViewValue(n.query)}),r.$render=function(){n.query=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(31),o=a(r)},function(e,t){"use strict";function n(e){var t=function(t,n,a,i){n.html("
"),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}n.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(32),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(33),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),n.entityDetailsList=[];for(var s in t.entityDetails){var d=s;n.entityDetailsList.push(d)}r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(34),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t,n){var a=function(a,i,r,l){var s=o.default;i.html(s);var d=186;a.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,d],a.ruleNodeTypes=n,a.aggPeriodTimeUnits={},a.aggPeriodTimeUnits.MINUTES=n.timeUnit.MINUTES,a.aggPeriodTimeUnits.HOURS=n.timeUnit.HOURS,a.aggPeriodTimeUnits.DAYS=n.timeUnit.DAYS,a.aggPeriodTimeUnits.MILLISECONDS=n.timeUnit.MILLISECONDS,a.aggPeriodTimeUnits.SECONDS=n.timeUnit.SECONDS,a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},e(i.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{},link:a}}i.$inject=["$compile","$mdConstant","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(35),o=a(r);n(3)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var i=n(86),r=a(i),o=n(87),l=a(o),s=n(82),d=a(s),u=n(88),c=a(u),m=n(81),g=a(m),p=n(89),f=a(p),b=n(84),v=a(b),y=n(83),h=a(y);t.default=angular.module("thingsboard.ruleChain.config.enrichment",[]).directive("tbEnrichmentNodeOriginatorAttributesConfig",r.default).directive("tbEnrichmentNodeOriginatorFieldsConfig",l.default).directive("tbEnrichmentNodeDeviceAttributesConfig",d.default).directive("tbEnrichmentNodeRelatedAttributesConfig",c.default).directive("tbEnrichmentNodeCustomerAttributesConfig",g.default).directive("tbEnrichmentNodeTenantAttributesConfig",f.default).directive("tbEnrichmentNodeGetTelemetryFromDatabase",v.default).directive("tbEnrichmentNodeEntityDetailsConfig",h.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(36),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(37),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(38),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(39),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),n.alarmStatusList=[];for(var s in t.alarmStatus)n.alarmStatusList.push(t.alarmStatus[s]);r.$render=function(){n.configuration=r.$viewValue},n.getAlarmStatusList=function(){return n.alarmStatusList.filter(function(e){return n.configuration.alarmStatusList.indexOf(e)===-1})},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{required:"=ngRequired",readonly:"=ngReadonly"},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(40),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(41),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(42),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}i.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(43),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var i=n(97),r=a(i),o=n(95),l=a(o),s=n(98),d=a(s),u=n(92),c=a(u),m=n(96),g=a(m),p=n(91),f=a(p),b=n(93),v=a(b),y=n(90),h=a(y);t.default=angular.module("thingsboard.ruleChain.config.filter",[]).directive("tbFilterNodeScriptConfig",r.default).directive("tbFilterNodeMessageTypeConfig",l.default).directive("tbFilterNodeSwitchConfig",d.default).directive("tbFilterNodeCheckRelationConfig",c.default).directive("tbFilterNodeOriginatorTypeConfig",g.default).directive("tbFilterNodeCheckMessageConfig",f.default).directive("tbFilterNodeGpsGeofencingConfig",v.default).directive("tbFilterNodeCheckAlarmStatusConfig",h.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t,n){var a=function(a,i,r,l){function s(){if(l.$viewValue){for(var e=[],t=0;t-1&&t.kvList.splice(e,1)}function l(){t.kvList||(t.kvList=[]),t.kvList.push({key:"",value:""})}function s(){var e={};t.kvList.forEach(function(t){t.key&&(e[t.key]=t.value)}),i.$setViewValue(e),d()}function d(){var e=!0;t.required&&!t.kvList.length&&(e=!1),i.$setValidity("kvMap",e)}var u=o.default;n.html(u),t.ngModelCtrl=i,t.removeKeyVal=r,t.addKeyVal=l,t.kvList=[],t.$watch("query",function(e,n){angular.equals(e,n)||i.$setViewValue(t.query)}),i.$render=function(){if(i.$viewValue){var e=i.$viewValue;t.kvList.length=0;for(var n in e)t.kvList.push({key:n,value:e[n]})}t.$watch("kvList",function(e,t){angular.equals(e,t)||s()},!0),d()},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{required:"=ngRequired",disabled:"=ngDisabled",requiredText:"=",keyText:"=",keyRequiredText:"=",valText:"=",valRequiredText:"="},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(48),o=a(r);n(5)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||r.$setViewValue(n.query)}),r.$render=function(){n.query=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(49),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=function(n,a,i,r){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||r.$setViewValue(n.configuration)}),r.$render=function(){n.configuration=r.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}i.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(50),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var i=n(101),r=a(i),o=n(103),l=a(o),s=n(104),d=a(s);t.default=angular.module("thingsboard.ruleChain.config.transform",[]).directive("tbTransformationNodeChangeOriginatorConfig",r.default).directive("tbTransformationNodeScriptConfig",l.default).directive("tbTransformationNodeToEmailConfig",d.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e,t,n){var a=function(a,i,r,l){var s=o.default;i.html(s),a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},a.testScript=function(e){var i=angular.copy(a.configuration.jsScript);n.testNodeScript(e,i,"update",t.instant("tb.rulenode.transformer")+"","Transform",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,l.$setDirty()})},e(i.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}i.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(51),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){var t=function(t,n,a,i){var r=o.default;n.html(r),t.$watch("configuration",function(e,n){angular.equals(e,n)||i.$setViewValue(t.configuration)}),i.$render=function(){t.configuration=i.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}i.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(52),o=a(r)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var i=n(108),r=a(i),o=n(94),l=a(o),s=n(85),d=a(s),u=n(102),c=a(u),m=n(62),g=a(m),p=n(80),f=a(p),b=n(100),v=a(b),y=n(79),h=a(y),q=n(99),x=a(q),k=n(107),$=a(k);t.default=angular.module("thingsboard.ruleChain.config",[r.default,l.default,d.default,c.default,g.default]).directive("tbNodeEmptyConfig",f.default).directive("tbRelationsQueryConfig",v.default).directive("tbDeviceRelationsQueryConfig",h.default).directive("tbKvMapConfig",x.default).config($.default).name},function(e,t){"use strict";function n(e){var t={tb:{rulenode:{"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-name-pattern-hint":"Name pattern, use ${metaKeyName} to substitute variables from metadata","entity-type-pattern":"Type pattern","entity-type-pattern-required":"Type pattern is required","entity-type-pattern-hint":"Type pattern, use ${metaKeyName} to substitute variables from metadata","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-name-pattern-hint":"Customer name pattern, use ${metaKeyName} to substitute variables from metadata","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.","start-interval":"Start Interval","end-interval":"End Interval","start-interval-time-unit":"Start Interval Time Unit","end-interval-time-unit":"End Interval Time Unit","fetch-mode":"Fetch mode","fetch-mode-hint":"If selected fetch mode 'ALL' you able to choose telemetry sampling order.","order-by":"Order by","order-by-hint":"Select to choose telemetry sampling order.",limit:"Limit","limit-hint":"Min limit value is 2, max - 1000. In case you want to fetch a single entry, select fetch mode 'FIRST' or 'LAST'.","time-unit-milliseconds":"Milliseconds","time-unit-seconds":"Seconds","time-unit-minutes":"Minutes","time-unit-hours":"Hours","time-unit-days":"Days","time-value-range":"Time value should be in a range from 1 to 2147483647'.","start-interval-value-required":"Start interval value is required.","end-interval-value-required":"End interval value is required.",filter:"Filter",switch:"Switch","message-type":"Message type", +"message-type-required":"Message type is required.","message-types-filter":"Message types filter","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","latest-timeseries":"Latest timeseries","data-keys":"Message data","metadata-keys":"Message metadata","relations-query":"Relations query","device-relations-query":"Device relations query","max-relation-level":"Max relation level","relation-type-pattern":"Relation type pattern","relation-type-pattern-hint":"Relation type pattern, use ${metaKeyName} to substitute variables from metadata","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","attr-mapping":"Attributes mapping","source-attribute":"Source attribute","source-attribute-required":"Source attribute is required.","source-telemetry":"Source telemetry","source-telemetry-required":"Source telemetry is required.","target-attribute":"Target attribute","target-attribute-required":"Target attribute is required.","attr-mapping-required":"At least one attribute mapping should be specified.","fields-mapping":"Fields mapping","fields-mapping-required":"At least one field mapping should be specified.","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","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 metadata period in seconds pattern","use-metadata-period-in-seconds-patterns-hint":"If selected, rule node use period in seconds interval pattern from message metadata assuming that intervals are in the seconds.","period-in-seconds-pattern":"Period in seconds metadata pattern","period-in-seconds-pattern-required":"Period in seconds pattern is required","period-in-seconds-pattern-hint":"Period in seconds pattern, use ${metaKeyName} to substitute variables from metadata","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","alarm-type-required":"Alarm type is required.","alarm-severity":"Alarm severity","alarm-severity-required":"Alarm severity is required","alarm-statuses-filter":"Alarm statuses filter","alarm-statuses-required":"Alarm statuses is required",propagate:"Propagate",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","from-template-hint":"From address template, use ${metaKeyName} to substitute variables from metadata","to-template":"To Template","to-template-required":"To Template is required","mail-address-list-template-hint":"Comma separated address list, use ${metaKeyName} to substitute variables from metadata","cc-template":"Cc Template","bcc-template":"Bcc Template","subject-template":"Subject Template","subject-template-required":"Subject Template is required","subject-template-hint":"Mail subject template, use ${metaKeyName} to substitute variables from metadata","body-template":"Body Template","body-template-required":"Body Template is required","body-template-hint":"Mail body template, use ${metaKeyName} to substitute variables from metadata","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","endpoint-url-pattern-hint":"HTTP URL address pattern, use ${metaKeyName} to substitute variables from metadata","request-method":"Request method","use-simple-client-http-factory":"Use simple client HTTP factory","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 ${metaKeyName} in header/value fields to substitute variables from metadata",header:"Header","header-required":"Header is required",value:"Value","value-required":"Value is required","topic-pattern":"Topic pattern","topic-pattern-required":"Topic pattern is required","mqtt-topic-pattern-hint":"MQTT topic pattern, use ${metaKeyName} to substitute variables from metadata","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","topic-arn-pattern-hint":"Topic ARN pattern, use ${metaKeyName} to substitute variables from metadata","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","queue-url-pattern-hint":"Queue URL pattern, use ${metaKeyName} to substitute variables from metadata","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 ${metaKeyName} in name/value fields to substitute variables from metadata","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","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","username-required":"Username is required.","password-required":"Password is required.","ca-cert":"CA certificate file *","private-key":"Private key file *",cert:"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-interval-patterns":"Use metadata interval patterns","use-metadata-interval-patterns-hint":"If selected, rule node use start and end interval patterns from message metadata assuming that intervals are in the milliseconds.","use-message-alarm-data":"Use message alarm data","check-all-keys":"Check that all selected keys 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-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-pattern":"Start interval pattern","end-interval-pattern":"End interval pattern","start-interval-pattern-required":"Start interval pattern is required","end-interval-pattern-required":"End interval pattern is required","start-interval-pattern-hint":"Start interval pattern, use ${metaKeyName} to substitute variables from metadata","end-interval-pattern-hint":"End interval pattern, use ${metaKeyName} to substitute variables from metadata","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","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":"You must supply a proxy port.","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","tls-version":"TLS version","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":'You should press "enter" to complete field input.',"entity-details":"Select entity details:","entity-details-title":"Title","entity-details-country":"Country","entity-details-state":"State","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","add-to-metadata":"Add selected details to message metadata","add-to-metadata-hint":"If selected, adds the selected details keys to the message metadata instead of message data.","entity-details-list-empty":"No entity details 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":"You should 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-key-name":"Latitude key name","longitude-key-name":"Longitude key name","latitude-key-name-required":"Latitude key name is required.","longitude-key-name-required":"Longitude key name is required.","fetch-perimeter-info-from-message-metadata":"Fetch perimeter information from message metadata","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:"Range","range-required":"Range is required.","polygon-definition":"Polygon definition","polygon-definition-required":"Polygon definition is required.","polygon-definition-hint":"Please, 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 Latest telemetry with Timestamp","get-latest-value-with-ts-hint":'If selected, latest telemetry values will be added to the outbound message metadata with timestamp, e.g: "temp": "{\\"ts\\":1574329385897,\\"value\\":42}"',"use-redis-queue":"Use redis queue for message persistence","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."},"key-val":{key:"Key",value:"Value","remove-entry":"Remove entry","add-entry":"Add entry"}}};e.translations("en_US",t)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function i(e){(0,o.default)(e)}i.$inject=["$translateProvider"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var r=n(106),o=a(r)},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=angular.module("thingsboard.ruleChain.config.types",[]).constant("ruleNodeTypes",{originatorSource:{CUSTOMER:{name:"tb.rulenode.originator-customer",value:"CUSTOMER"},TENANT:{name:"tb.rulenode.originator-tenant",value:"TENANT"},RELATED:{name:"tb.rulenode.originator-related",value:"RELATED"},ALARM_ORIGINATOR:{name:"tb.rulenode.originator-alarm-originator",value:"ALARM_ORIGINATOR"}},fetchModeType:["FIRST","LAST","ALL"],samplingOrder:["ASC","DESC"],httpRequestType:["GET","POST","PUT","DELETE"],entityDetails:{TITLE:{name:"tb.rulenode.entity-details-title",value:"TITLE"},COUNTRY:{name:"tb.rulenode.entity-details-country",value:"COUNTRY"},STATE:{name:"tb.rulenode.entity-details-state",value:"STATE"},ZIP:{name:"tb.rulenode.entity-details-zip",value:"ZIP"},ADDRESS:{name:"tb.rulenode.entity-details-address",value:"ADDRESS"},ADDRESS2:{name:"tb.rulenode.entity-details-address2",value:"ADDRESS2"},PHONE:{name:"tb.rulenode.entity-details-phone",value:"PHONE"},EMAIL:{name:"tb.rulenode.entity-details-email",value:"EMAIL"},ADDITIONAL_INFO:{name:"tb.rulenode.entity-details-additional_info",value:"ADDITIONAL_INFO"}},sqsQueueType:{STANDARD:{name:"tb.rulenode.sqs-queue-standard",value:"STANDARD"},FIFO:{name:"tb.rulenode.sqs-queue-fifo",value:"FIFO"}},perimeterType:{CIRCLE:{name:"tb.rulenode.perimeter-circle",value:"CIRCLE"},POLYGON:{name:"tb.rulenode.perimeter-polygon",value:"POLYGON"}},timeUnit:{MILLISECONDS:{value:"MILLISECONDS",name:"tb.rulenode.time-unit-milliseconds"},SECONDS:{value:"SECONDS",name:"tb.rulenode.time-unit-seconds"},MINUTES:{value:"MINUTES",name:"tb.rulenode.time-unit-minutes"},HOURS:{value:"HOURS",name:"tb.rulenode.time-unit-hours"},DAYS:{value:"DAYS",name:"tb.rulenode.time-unit-days"}},rangeUnit:{METER:{value:"METER",name:"tb.rulenode.range-unit-meter"},KILOMETER:{value:"KILOMETER",name:"tb.rulenode.range-unit-kilometer"},FOOT:{value:"FOOT",name:"tb.rulenode.range-unit-foot"},MILE:{value:"MILE",name:"tb.rulenode.range-unit-mile"},NAUTICAL_MILE:{value:"NAUTICAL_MILE",name:"tb.rulenode.range-unit-nautical-mile"}},mqttCredentialTypes:{anonymous:{value:"anonymous",name:"tb.rulenode.credentials-anonymous"},basic:{value:"basic",name:"tb.rulenode.credentials-basic"},"cert.PEM":{value:"cert.PEM",name:"tb.rulenode.credentials-pem"}},toBytesStandartCharsetTypes:{"US-ASCII":{value:"US-ASCII",name:"tb.rulenode.charset-us-ascii"},"ISO-8859-1":{value:"ISO-8859-1",name:"tb.rulenode.charset-iso-8859-1"},"UTF-8":{value:"UTF-8",name:"tb.rulenode.charset-utf-8"},"UTF-16BE":{value:"UTF-16BE",name:"tb.rulenode.charset-utf-16be"},"UTF-16LE":{value:"UTF-16LE",name:"tb.rulenode.charset-utf-16le"},"UTF-16":{value:"UTF-16",name:"tb.rulenode.charset-utf-16"}}}).name}])); //# sourceMappingURL=rulenode-core-config.js.map \ No newline at end of file From f4baab4969623a4c290105dc118ce891e62cb1b4 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Thu, 18 Jun 2020 13:30:45 +0300 Subject: [PATCH 50/52] Edge Notifitcation Service --- .../demo/rule_chains/thermostat_alarms.json | 2 +- .../tenant/rule_chains/root_rule_chain.json | 2 +- .../edge/DefaultEdgeNotificationService.java | 446 +++++--------- .../service/edge/EdgeContextComponent.java | 15 + .../service/edge/rpc/EdgeGrpcSession.java | 552 +++++++++++------- .../constructor/EntityDataMsgConstructor.java | 56 ++ .../server/common/data/DataConstants.java | 3 + .../common/data/id/EntityIdFactory.java | 25 + common/edge-api/src/main/proto/edge.proto | 6 +- .../server/dao/edge/BaseEdgeEventService.java | 2 + .../rule/engine/edge/TbMsgPushToEdgeNode.java | 63 +- ui/src/app/rulechain/rulechain.routes.js | 2 +- 12 files changed, 645 insertions(+), 529 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityDataMsgConstructor.java diff --git a/application/src/main/data/json/demo/rule_chains/thermostat_alarms.json b/application/src/main/data/json/demo/rule_chains/thermostat_alarms.json index 155d05f119..e3ba2e3192 100644 --- a/application/src/main/data/json/demo/rule_chains/thermostat_alarms.json +++ b/application/src/main/data/json/demo/rule_chains/thermostat_alarms.json @@ -2,7 +2,7 @@ "ruleChain": { "additionalInfo": null, "name": "Thermostat Alarms", - "type": "SYSTEM", + "type": "CORE", "firstRuleNodeId": null, "root": false, "debugMode": false, diff --git a/application/src/main/data/json/tenant/rule_chains/root_rule_chain.json b/application/src/main/data/json/tenant/rule_chains/root_rule_chain.json index e7591a05aa..cec15f3643 100644 --- a/application/src/main/data/json/tenant/rule_chains/root_rule_chain.json +++ b/application/src/main/data/json/tenant/rule_chains/root_rule_chain.json @@ -2,7 +2,7 @@ "ruleChain": { "additionalInfo": null, "name": "Root Rule Chain", - "type": "SYSTEM", + "type": "CORE", "firstRuleNodeId": null, "root": true, "debugMode": false, diff --git a/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java b/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java index 96f127d9be..79d0569e2e 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java @@ -15,29 +15,25 @@ */ package org.thingsboard.server.service.edge; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.MoreExecutors; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.codec.binary.Base64; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.Dashboard; -import org.thingsboard.server.common.data.DataConstants; -import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.EntityView; -import org.thingsboard.server.common.data.Event; import org.thingsboard.server.common.data.alarm.Alarm; -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.edge.EdgeEvent; import org.thingsboard.server.common.data.edge.EdgeEventType; +import org.thingsboard.server.common.data.id.AlarmId; +import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.EntityIdFactory; +import org.thingsboard.server.common.data.id.IdBased; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TimePageData; @@ -46,29 +42,28 @@ import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.RelationTypeGroup; 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.msg.TbMsg; import org.thingsboard.server.common.msg.queue.TbCallback; -import org.thingsboard.server.common.msg.session.SessionMsgType; -import org.thingsboard.server.dao.asset.AssetService; -import org.thingsboard.server.dao.device.DeviceService; +import org.thingsboard.server.dao.alarm.AlarmService; import org.thingsboard.server.dao.edge.EdgeEventService; import org.thingsboard.server.dao.edge.EdgeService; -import org.thingsboard.server.dao.entityview.EntityViewService; -import org.thingsboard.server.dao.event.EventService; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.executors.DbCallbackExecutorService; -import javax.annotation.Nullable; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.UUID; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.stream.Collectors; @Service @TbCoreComponent @@ -81,16 +76,10 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { private EdgeService edgeService; @Autowired - private DeviceService deviceService; - - @Autowired - private AssetService assetService; - - @Autowired - private EntityViewService entityViewService; + private RuleChainService ruleChainService; @Autowired - private RuleChainService ruleChainService; + private AlarmService alarmService; @Autowired private RelationService relationService; @@ -98,6 +87,9 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { @Autowired private EdgeEventService edgeEventService; + @Autowired + private DbCallbackExecutorService dbCallbackExecutorService; + private ExecutorService tsCallBackExecutor; @PostConstruct @@ -121,315 +113,160 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { public Edge setEdgeRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) throws IOException { edge.setRootRuleChainId(ruleChainId); Edge savedEdge = edgeService.saveEdge(edge); - RuleChain ruleChain = ruleChainService.findRuleChainById(tenantId, ruleChainId); - saveEventToEdgeQueue(tenantId, edge.getId(), EdgeEventType.RULE_CHAIN, DataConstants.ENTITY_UPDATED, mapper.writeValueAsString(ruleChain), new FutureCallback() { - @Override - public void onSuccess(@Nullable Void aVoid) { - log.debug("Event saved successfully!"); - } - - @Override - public void onFailure(Throwable t) { - log.debug("Failure during event save", t); - } - }); + saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.RULE_CHAIN, ActionType.UPDATED, ruleChainId, null); return savedEdge; } - private void saveEventToEdgeQueue(TenantId tenantId, EdgeId edgeId, EdgeEventType entityType, String type, String data, FutureCallback callback) throws IOException { - log.debug("Pushing single event to edge queue. tenantId [{}], edgeId [{}], entityType [{}], type[{}], data [{}]", tenantId, edgeId, entityType, type, data); - -// EdgeEQueueEntry queueEntry = new EdgeQueueEntry(); -// queueEntry.setEntityType(entityType); -// queueEntry.setType(type); -// queueEntry.setData(data); + private void saveEdgeEvent(TenantId tenantId, + EdgeId edgeId, + EdgeEventType edgeEventType, + ActionType edgeEventAction, + EntityId entityId, + JsonNode entityBody) { + log.debug("Pushing edge event to edge queue. tenantId [{}], edgeId [{}], edgeEventType [{}], edgeEventAction[{}], entityId [{}], entityBody [{}]", + tenantId, edgeId, edgeEventType, edgeEventAction, entityId, entityBody); EdgeEvent edgeEvent = new EdgeEvent(); edgeEvent.setEdgeId(edgeId); edgeEvent.setTenantId(tenantId); -// event.setType(DataConstants.EDGE_QUEUE_EVENT_TYPE); -// event.setBody(mapper.valueToTree(queueEntry)); - ListenableFuture saveFuture = edgeEventService.saveAsync(edgeEvent); - - addMainCallback(saveFuture, callback); - } - - private void addMainCallback(ListenableFuture saveFuture, final FutureCallback callback) { - Futures.addCallback(saveFuture, new FutureCallback() { - @Override - public void onSuccess(@Nullable EdgeEvent result) { - callback.onSuccess(null); - } - - @Override - public void onFailure(Throwable t) { - callback.onFailure(t); - } - }, tsCallBackExecutor); + edgeEvent.setEdgeEventType(edgeEventType); + edgeEvent.setEdgeEventAction(edgeEventAction.name()); + if (entityId != null) { + edgeEvent.setEntityId(entityId.getId()); + } + edgeEvent.setEntityBody(entityBody); + edgeEventService.saveAsync(edgeEvent); } @Override public void pushNotificationToEdge(TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg, TbCallback callback) { -// if (tbMsg.getType().equals(SessionMsgType.POST_TELEMETRY_REQUEST.name()) || -// tbMsg.getType().equals(SessionMsgType.POST_ATTRIBUTES_REQUEST.name()) || -// tbMsg.getType().equals(DataConstants.ATTRIBUTES_UPDATED) || -// tbMsg.getType().equals(DataConstants.ATTRIBUTES_DELETED)) { -// processCustomTbMsg(tenantId, tbMsg, callback); -// } else { -// try { -// switch (tbMsg.getOriginator().getEntityType()) { -// case EDGE: -// processEdge(tenantId, tbMsg, callback); -// break; -// case ASSET: -// processAsset(tenantId, tbMsg, callback); -// break; -// case DEVICE: -// processDevice(tenantId, tbMsg, callback); -// break; -// case DASHBOARD: -// processDashboard(tenantId, tbMsg, callback); -// break; -// case RULE_CHAIN: -// processRuleChain(tenantId, tbMsg, callback); -// break; -// case ENTITY_VIEW: -// processEntityView(tenantId, tbMsg, callback); -// break; -// case ALARM: -// processAlarm(tenantId, tbMsg, callback); -// break; -// default: -// log.debug("Entity type [{}] is not designed to be pushed to edge", tbMsg.getOriginator().getEntityType()); -// } -// } catch (IOException e) { -// log.error("Can't push to edge updates, entity type [{}], data [{}]", tbMsg.getOriginator().getEntityType(), tbMsg.getData(), e); -// } -// } - } - - - private void processCustomTbMsg(TenantId tenantId, TbMsg tbMsg, FutureCallback callback) { - ListenableFuture edgeIdFuture = getEdgeIdByOriginatorId(tenantId, tbMsg.getOriginator()); - Futures.transform(edgeIdFuture, edgeId -> { - EdgeEventType edgeEventType = getEdgeQueueTypeByEntityType(tbMsg.getOriginator().getEntityType()); - if (edgeId != null && edgeEventType != null) { - try { - saveEventToEdgeQueue(tenantId, edgeId, edgeEventType, tbMsg.getType(), Base64.encodeBase64String(TbMsg.toByteArray(tbMsg)), callback); - } catch (IOException e) { - log.error("Error while saving custom tbMsg into Edge Queue", e); - } + try { + EdgeEventType edgeEventType = EdgeEventType.valueOf(edgeNotificationMsg.getEdgeEventType()); + ActionType edgeEventAction = ActionType.valueOf(edgeNotificationMsg.getEdgeEventAction()); + TenantId tenantId = new TenantId(new UUID(edgeNotificationMsg.getTenantIdMSB(), edgeNotificationMsg.getTenantIdLSB())); + switch (edgeEventType) { + // TODO: voba - handle edge updates + // case EDGE: + case ASSET: + case DEVICE: + case ENTITY_VIEW: + case DASHBOARD: + case RULE_CHAIN: + EntityId entityId = EntityIdFactory.getByEdgeEventTypeAndUuid(edgeEventType, new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB())); + ListenableFuture> edgeIdsFuture = findRelatedEdgeIdsEntityId(tenantId, entityId); + Futures.transform(edgeIdsFuture, edgeIds -> { + if (edgeIds != null && !edgeIds.isEmpty()) { + for (EdgeId edgeId : edgeIds) { + try { + saveEdgeEvent(tenantId, edgeId, edgeEventType, edgeEventAction, entityId, null); + if (edgeEventType.equals(EdgeEventType.RULE_CHAIN) && + (ActionType.UPDATED.equals(edgeEventAction) || ActionType.ADDED.equals(edgeEventAction))) { + RuleChainMetaData ruleChainMetaData = ruleChainService.loadRuleChainMetaData(tenantId, new RuleChainId(entityId.getId())); + saveEdgeEvent(tenantId, edgeId, EdgeEventType.RULE_CHAIN_METADATA, edgeEventAction, ruleChainMetaData.getRuleChainId(), null); + } + } catch (Exception e) { + log.error("[{}] Failed to push event to edge, edgeId [{}], edgeEventType [{}], edgeEventAction [{}], entityId [{}]", + tenantId, edgeId, edgeEventType, edgeEventAction, entityId, e); + } + } + } + return null; + }, dbCallbackExecutorService); + break; + case ALARM: + EntityId alarmId = EntityIdFactory.getByEdgeEventTypeAndUuid(edgeEventType, new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB())); + processAlarm(tenantId, edgeEventAction, alarmId); + break; + case RELATION: + EntityRelation entityRelation = mapper.convertValue(edgeNotificationMsg.getEntityBody(), EntityRelation.class); + processRelation(tenantId, edgeEventAction, entityRelation); + break; + default: + log.debug("Edge event type [{}] is not designed to be pushed to edge", edgeEventType); } - return null; - }, MoreExecutors.directExecutor()); - } - - private void processDevice(TenantId tenantId, TbMsg tbMsg, FutureCallback callback) throws IOException { - switch (tbMsg.getType()) { - case DataConstants.ENTITY_ASSIGNED_TO_EDGE: - case DataConstants.ENTITY_UNASSIGNED_FROM_EDGE: - processAssignedEntity(tenantId, tbMsg, EdgeEventType.DEVICE, callback); - break; - case DataConstants.ENTITY_DELETED: - case DataConstants.ENTITY_CREATED: - case DataConstants.ENTITY_UPDATED: - Device device = mapper.readValue(tbMsg.getData(), Device.class); - pushEventToEdge(tenantId, device.getId(), EdgeEventType.DEVICE, tbMsg, callback); - break; - default: - log.warn("Unsupported msgType [{}], tbMsg [{}]", tbMsg.getType(), tbMsg); - } - } - - private void processEdge(TenantId tenantId, TbMsg tbMsg, FutureCallback callback) throws IOException { - switch (tbMsg.getType()) { - case DataConstants.ENTITY_DELETED: - case DataConstants.ENTITY_CREATED: - case DataConstants.ENTITY_UPDATED: - // TODO: voba - handle properly edge creation - break; - default: - log.warn("Unsupported msgType [{}], tbMsg [{}]", tbMsg.getType(), tbMsg); - } - } - - private void processAsset(TenantId tenantId, TbMsg tbMsg, FutureCallback callback) throws IOException { - switch (tbMsg.getType()) { - case DataConstants.ENTITY_ASSIGNED_TO_EDGE: - case DataConstants.ENTITY_UNASSIGNED_FROM_EDGE: - processAssignedEntity(tenantId, tbMsg, EdgeEventType.ASSET, callback); - break; - case DataConstants.ENTITY_DELETED: - case DataConstants.ENTITY_CREATED: - case DataConstants.ENTITY_UPDATED: - Asset asset = mapper.readValue(tbMsg.getData(), Asset.class); - pushEventToEdge(tenantId, asset.getId(), EdgeEventType.ASSET, tbMsg, callback); - break; - default: - log.warn("Unsupported msgType [{}], tbMsg [{}]", tbMsg.getType(), tbMsg); + } catch (Exception e) { + callback.onFailure(e); + log.error("Can't push to edge updates, edgeNotificationMsg [{}]", edgeNotificationMsg, e); + } finally { + callback.onSuccess(); } } - private void processEntityView(TenantId tenantId, TbMsg tbMsg, FutureCallback callback) throws IOException { - switch (tbMsg.getType()) { - case DataConstants.ENTITY_ASSIGNED_TO_EDGE: - case DataConstants.ENTITY_UNASSIGNED_FROM_EDGE: - processAssignedEntity(tenantId, tbMsg, EdgeEventType.ENTITY_VIEW, callback); - break; - case DataConstants.ENTITY_DELETED: - case DataConstants.ENTITY_CREATED: - case DataConstants.ENTITY_UPDATED: - EntityView entityView = mapper.readValue(tbMsg.getData(), EntityView.class); - pushEventToEdge(tenantId, entityView.getId(), EdgeEventType.ENTITY_VIEW, tbMsg, callback); - break; - default: - log.warn("Unsupported msgType [{}], tbMsg [{}]", tbMsg.getType(), tbMsg); - } - } - - private void processAlarm(TenantId tenantId, TbMsg tbMsg, FutureCallback callback) throws IOException { - switch (tbMsg.getType()) { - case DataConstants.ENTITY_DELETED: - case DataConstants.ENTITY_CREATED: - case DataConstants.ENTITY_UPDATED: - case DataConstants.ALARM_ACK: - case DataConstants.ALARM_CLEAR: - Alarm alarm = mapper.readValue(tbMsg.getData(), Alarm.class); + private void processAlarm(TenantId tenantId, ActionType edgeActionType, EntityId alarmId) { + ListenableFuture alarmFuture = alarmService.findAlarmByIdAsync(tenantId, new AlarmId(alarmId.getId())); + Futures.transform(alarmFuture, alarm -> { + if (alarm != null) { EdgeEventType edgeEventType = getEdgeQueueTypeByEntityType(alarm.getOriginator().getEntityType()); if (edgeEventType != null) { - pushEventToEdge(tenantId, alarm.getOriginator(), EdgeEventType.ALARM, tbMsg, callback); - } - break; - default: - log.warn("Unsupported msgType [{}], tbMsg [{}]", tbMsg.getType(), tbMsg); - } - } - - private void processDashboard(TenantId tenantId, TbMsg tbMsg, FutureCallback callback) throws IOException { - switch (tbMsg.getType()) { - case DataConstants.ENTITY_ASSIGNED_TO_EDGE: - case DataConstants.ENTITY_UNASSIGNED_FROM_EDGE: - processAssignedEntity(tenantId, tbMsg, EdgeEventType.DASHBOARD, callback); - break; - case DataConstants.ENTITY_DELETED: - case DataConstants.ENTITY_CREATED: - case DataConstants.ENTITY_UPDATED: - Dashboard dashboard = mapper.readValue(tbMsg.getData(), Dashboard.class); - ListenableFuture> future = edgeService.findEdgesByTenantIdAndDashboardId(tenantId, dashboard.getId(), new TimePageLink(Integer.MAX_VALUE)); - Futures.transform(future, edges -> { - if (edges != null && edges.getData() != null && !edges.getData().isEmpty()) { - try { - for (Edge edge : edges.getData()) { - pushEventToEdge(tenantId, edge.getId(), EdgeEventType.DASHBOARD, tbMsg, callback); - } - } catch (IOException e) { - log.error("Can't push event to edge", e); - } - } - return null; - }, MoreExecutors.directExecutor()); - break; - default: - log.warn("Unsupported msgType [{}], tbMsg [{}]", tbMsg.getType(), tbMsg); - } - } - - private void processRuleChain(TenantId tenantId, TbMsg tbMsg, FutureCallback callback) throws IOException { - switch (tbMsg.getType()) { - case DataConstants.ENTITY_ASSIGNED_TO_EDGE: - case DataConstants.ENTITY_UNASSIGNED_FROM_EDGE: - processAssignedEntity(tenantId, tbMsg, EdgeEventType.RULE_CHAIN, callback); - break; - case DataConstants.ENTITY_DELETED: - case DataConstants.ENTITY_CREATED: - case DataConstants.ENTITY_UPDATED: - RuleChain ruleChain = mapper.readValue(tbMsg.getData(), RuleChain.class); - if (RuleChainType.EDGE.equals(ruleChain.getType())) { - ListenableFuture> future = edgeService.findEdgesByTenantIdAndRuleChainId(tenantId, ruleChain.getId(), new TimePageLink(Integer.MAX_VALUE)); - Futures.transform(future, edges -> { - if (edges != null && edges.getData() != null && !edges.getData().isEmpty()) { - try { - for (Edge edge : edges.getData()) { - pushEventToEdge(tenantId, edge.getId(), EdgeEventType.RULE_CHAIN, tbMsg, callback); - } - } catch (IOException e) { - log.error("Can't push event to edge", e); + ListenableFuture> relatedEdgeIdsEntityIdFuture = findRelatedEdgeIdsEntityId(tenantId, alarm.getOriginator()); + Futures.transform(relatedEdgeIdsEntityIdFuture, relatedEdgeIdsEntityId -> { + if (relatedEdgeIdsEntityId != null) { + for (EdgeId edgeId : relatedEdgeIdsEntityId) { + saveEdgeEvent(tenantId, edgeId, EdgeEventType.ALARM, edgeActionType, alarmId, null); } } return null; - }, MoreExecutors.directExecutor()); + }, dbCallbackExecutorService); } - break; - default: - log.warn("Unsupported msgType [{}], tbMsg [{}]", tbMsg.getType(), tbMsg); - } - } - - - private void processAssignedEntity(TenantId tenantId, TbMsg tbMsg, EdgeEventType entityType, FutureCallback callback) throws IOException { - EdgeId edgeId; - switch (tbMsg.getType()) { - case DataConstants.ENTITY_ASSIGNED_TO_EDGE: - edgeId = new EdgeId(UUID.fromString(tbMsg.getMetaData().getValue("assignedEdgeId"))); - pushEventToEdge(tenantId, edgeId, entityType, tbMsg, callback); - break; - case DataConstants.ENTITY_UNASSIGNED_FROM_EDGE: - edgeId = new EdgeId(UUID.fromString(tbMsg.getMetaData().getValue("unassignedEdgeId"))); - pushEventToEdge(tenantId, edgeId, entityType, tbMsg, callback); - break; - - } + } + return null; + }, dbCallbackExecutorService); } - private void pushEventToEdge(TenantId tenantId, EntityId originatorId, EdgeEventType edgeEventType, TbMsg tbMsg, FutureCallback callback) { - ListenableFuture edgeIdFuture = getEdgeIdByOriginatorId(tenantId, originatorId); - Futures.transform(edgeIdFuture, edgeId -> { - if (edgeId != null) { - try { - pushEventToEdge(tenantId, edgeId, edgeEventType, tbMsg, callback); - } catch (Exception e) { - log.error("Failed to push event to edge, edgeId [{}], tbMsg [{}]", edgeId, tbMsg, e); - } + private void processRelation(TenantId tenantId, ActionType edgeActionType, EntityRelation entityRelation) { + List>> futures = new ArrayList<>(); + futures.add(findRelatedEdgeIdsEntityId(tenantId, entityRelation.getTo())); + futures.add(findRelatedEdgeIdsEntityId(tenantId, entityRelation.getFrom())); + ListenableFuture>> combinedFuture = Futures.allAsList(futures); + Futures.transform(combinedFuture, listOfListsEdgeIds -> { + Set uniqueEdgeIds = new HashSet<>(); + if (listOfListsEdgeIds != null && !listOfListsEdgeIds.isEmpty()) { + for (List listOfListsEdgeId : listOfListsEdgeIds) { + if (listOfListsEdgeId != null) { + uniqueEdgeIds.addAll(listOfListsEdgeId); } - return null; - }, - MoreExecutors.directExecutor()); - } - - private ListenableFuture getEdgeIdByOriginatorId(TenantId tenantId, EntityId originatorId) { - List originatorEdgeRelations = relationService.findByToAndType(tenantId, originatorId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE); - if (originatorEdgeRelations != null && originatorEdgeRelations.size() > 0) { - return Futures.immediateFuture(new EdgeId(originatorEdgeRelations.get(0).getFrom().getId())); - } else { - return Futures.immediateFuture(null); - } - } - - - private void pushEventToEdge(TenantId tenantId, EdgeId edgeId, EdgeEventType entityType, TbMsg tbMsg, FutureCallback callback) throws IOException { - log.debug("Pushing event(s) to edge queue. tenantId [{}], edgeId [{}], entityType [{}], tbMsg [{}]", tenantId, edgeId, entityType, tbMsg); - - saveEventToEdgeQueue(tenantId, edgeId, entityType, tbMsg.getType(), tbMsg.getData(), callback); - - if (entityType.equals(EdgeEventType.RULE_CHAIN)) { - pushRuleChainMetadataToEdge(tenantId, edgeId, tbMsg, callback); - } + } + } + if (!uniqueEdgeIds.isEmpty()) { + for (EdgeId edgeId : uniqueEdgeIds) { + saveEdgeEvent(tenantId, edgeId, EdgeEventType.RELATION, edgeActionType, null, mapper.valueToTree(entityRelation)); + } + } + return null; + }, dbCallbackExecutorService); } - private void pushRuleChainMetadataToEdge(TenantId tenantId, EdgeId edgeId, TbMsg tbMsg, FutureCallback callback) throws IOException { - RuleChain ruleChain = mapper.readValue(tbMsg.getData(), RuleChain.class); - switch (tbMsg.getType()) { - case DataConstants.ENTITY_ASSIGNED_TO_EDGE: - case DataConstants.ENTITY_UNASSIGNED_FROM_EDGE: - case DataConstants.ENTITY_UPDATED: - RuleChainMetaData ruleChainMetaData = ruleChainService.loadRuleChainMetaData(tenantId, ruleChain.getId()); - saveEventToEdgeQueue(tenantId, edgeId, EdgeEventType.RULE_CHAIN_METADATA, tbMsg.getType(), mapper.writeValueAsString(ruleChainMetaData), callback); - break; + private ListenableFuture> findRelatedEdgeIdsEntityId(TenantId tenantId, EntityId entityId) { + switch (entityId.getEntityType()) { + case DEVICE: + case ASSET: + case ENTITY_VIEW: + ListenableFuture> originatorEdgeRelationsFuture = relationService.findByToAndTypeAsync(tenantId, entityId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE); + return Futures.transform(originatorEdgeRelationsFuture, originatorEdgeRelations -> { + if (originatorEdgeRelations != null && originatorEdgeRelations.size() > 0) { + return Collections.singletonList(new EdgeId(originatorEdgeRelations.get(0).getFrom().getId())); + } else { + return Collections.emptyList(); + } + }, dbCallbackExecutorService); + case DASHBOARD: + return convertToEdgeIds(edgeService.findEdgesByTenantIdAndDashboardId(tenantId, new DashboardId(entityId.getId()), new TimePageLink(Integer.MAX_VALUE))); + case RULE_CHAIN: + return convertToEdgeIds(edgeService.findEdgesByTenantIdAndRuleChainId(tenantId, new RuleChainId(entityId.getId()), new TimePageLink(Integer.MAX_VALUE))); default: - log.warn("Unsupported msgType [{}], tbMsg [{}]", tbMsg.getType(), tbMsg); + return Futures.immediateFuture(Collections.emptyList()); } } + private ListenableFuture> convertToEdgeIds(ListenableFuture> future) { + return Futures.transform(future, edges -> { + if (edges != null && edges.getData() != null && !edges.getData().isEmpty()) { + return edges.getData().stream().map(IdBased::getId).collect(Collectors.toList()); + } else { + return Collections.emptyList(); + } + }, dbCallbackExecutorService); + } private EdgeEventType getEdgeQueueTypeByEntityType(EntityType entityType) { switch (entityType) { @@ -446,3 +283,4 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { } } + 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 26e094f9b3..32f38d2621 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 @@ -31,11 +31,14 @@ import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.relation.RelationService; +import org.thingsboard.server.dao.rule.RuleChainService; +import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.service.edge.rpc.EdgeEventStorageSettings; import org.thingsboard.server.service.edge.rpc.constructor.AlarmUpdateMsgConstructor; import org.thingsboard.server.service.edge.rpc.constructor.AssetUpdateMsgConstructor; import org.thingsboard.server.service.edge.rpc.constructor.DashboardUpdateMsgConstructor; import org.thingsboard.server.service.edge.rpc.constructor.DeviceUpdateMsgConstructor; +import org.thingsboard.server.service.edge.rpc.constructor.EntityDataMsgConstructor; import org.thingsboard.server.service.edge.rpc.constructor.EntityViewUpdateMsgConstructor; import org.thingsboard.server.service.edge.rpc.constructor.RelationUpdateMsgConstructor; import org.thingsboard.server.service.edge.rpc.constructor.RuleChainUpdateMsgConstructor; @@ -93,6 +96,14 @@ public class EdgeContextComponent { @Autowired private DashboardService dashboardService; + @Lazy + @Autowired + private RuleChainService ruleChainService; + + @Lazy + @Autowired + private UserService userService; + @Lazy @Autowired private ActorService actorService; @@ -141,6 +152,10 @@ public class EdgeContextComponent { @Autowired private RelationUpdateMsgConstructor relationUpdateMsgConstructor; + @Lazy + @Autowired + private EntityDataMsgConstructor entityDataMsgConstructor; + @Lazy @Autowired private EdgeEventStorageSettings edgeEventStorageSettings; 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 aa48f0a370..a3d9bb7593 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 @@ -19,15 +19,17 @@ import com.datastax.driver.core.utils.UUIDs; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; -import com.google.protobuf.ByteString; +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import io.grpc.stub.StreamObserver; import lombok.Data; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.codec.binary.Base64; -import org.thingsboard.server.common.data.Customer; +import org.checkerframework.checker.nullness.qual.Nullable; import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; @@ -41,12 +43,16 @@ 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.edge.EdgeEvent; +import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.AssetId; +import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityViewId; +import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.DataType; @@ -60,14 +66,13 @@ import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentialsType; 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.queue.TbMsgCallback; +import org.thingsboard.server.common.msg.session.SessionMsgType; +import org.thingsboard.server.common.transport.util.JsonUtils; import org.thingsboard.server.gen.edge.AlarmUpdateMsg; import org.thingsboard.server.gen.edge.ConnectRequestMsg; import org.thingsboard.server.gen.edge.ConnectResponseCode; import org.thingsboard.server.gen.edge.ConnectResponseMsg; -import org.thingsboard.server.gen.edge.CustomerUpdateMsg; import org.thingsboard.server.gen.edge.DeviceUpdateMsg; import org.thingsboard.server.gen.edge.DownlinkMsg; import org.thingsboard.server.gen.edge.EdgeConfiguration; @@ -81,7 +86,7 @@ import org.thingsboard.server.gen.edge.RuleChainMetadataUpdateMsg; import org.thingsboard.server.gen.edge.UpdateMsgType; import org.thingsboard.server.gen.edge.UplinkMsg; import org.thingsboard.server.gen.edge.UplinkResponseMsg; -import org.thingsboard.server.gen.edge.UserUpdateMsg; +import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.service.edge.EdgeContextComponent; import java.io.Closeable; @@ -103,6 +108,8 @@ public final class EdgeGrpcSession implements Closeable { private static final ReentrantLock deviceCreationLock = new ReentrantLock(); + private final Gson gson = new Gson(); + private static final String QUEUE_START_TS_ATTR_KEY = "queueStartTs"; private final UUID sessionId; @@ -182,9 +189,9 @@ public final class EdgeGrpcSession implements Closeable { processTelemetryMessage(edgeEvent); } else { processEntityCRUDMessage(edgeEvent, msgType); - } - if (ENTITY_CREATED_RPC_MESSAGE.equals(msgType)) { - pushEntityAttributesToEdge(edgeEvent); + if (ENTITY_CREATED_RPC_MESSAGE.equals(msgType)) { + pushEntityAttributesToEdge(edgeEvent); + } } } catch (Exception e) { log.error("Exception during processing records from queue", e); @@ -213,63 +220,66 @@ public final class EdgeGrpcSession implements Closeable { } } + private ListenableFuture getQueueStartTs() { + ListenableFuture> future = + ctx.getAttributesService().find(edge.getTenantId(), edge.getId(), DataConstants.SERVER_SCOPE, QUEUE_START_TS_ATTR_KEY); + return Futures.transform(future, attributeKvEntryOpt -> { + if (attributeKvEntryOpt != null && attributeKvEntryOpt.isPresent()) { + AttributeKvEntry attributeKvEntry = attributeKvEntryOpt.get(); + return attributeKvEntry.getLongValue().isPresent() ? attributeKvEntry.getLongValue().get() : 0L; + } else { + return 0L; + } + }, MoreExecutors.directExecutor()); + } + + private void updateQueueStartTs(Long newStartTs) { + newStartTs = ++newStartTs; // increments ts by 1 - next edge event search starts from current offset + 1 + List attributes = Collections.singletonList(new BaseAttributeKvEntry(new LongDataEntry(QUEUE_START_TS_ATTR_KEY, newStartTs), System.currentTimeMillis())); + ctx.getAttributesService().save(edge.getTenantId(), edge.getId(), DataConstants.SERVER_SCOPE, attributes); + } + private void pushEntityAttributesToEdge(EdgeEvent edgeEvent) throws IOException { EntityId entityId = null; - String entityName = null; switch (edgeEvent.getEdgeEventType()) { case EDGE: - Edge edge = objectMapper.readValue(entry.getData(), Edge.class); entityId = edge.getId(); - entityName = edge.getName(); break; case DEVICE: - Device device = objectMapper.readValue(entry.getData(), Device.class); - entityId = device.getId(); - entityName = device.getName(); + entityId = new DeviceId(edgeEvent.getEntityId()); break; case ASSET: - Asset asset = objectMapper.readValue(entry.getData(), Asset.class); - entityId = asset.getId(); - entityName = asset.getName(); + entityId = new AssetId(edgeEvent.getEntityId()); break; case ENTITY_VIEW: - EntityView entityView = objectMapper.readValue(entry.getData(), EntityView.class); - entityId = entityView.getId(); - entityName = entityView.getName(); + entityId = new EntityViewId(edgeEvent.getEntityId()); break; case DASHBOARD: - Dashboard dashboard = objectMapper.readValue(entry.getData(), Dashboard.class); - entityId = dashboard.getId(); - entityName = dashboard.getName(); + entityId = new DashboardId(edgeEvent.getEntityId()); break; } if (entityId != null) { final EntityId finalEntityId = entityId; - final String finalEntityName = entityName; ListenableFuture> ssAttrFuture = ctx.getAttributesService().findAll(edge.getTenantId(), entityId, DataConstants.SERVER_SCOPE); Futures.transform(ssAttrFuture, ssAttributes -> { if (ssAttributes != null && !ssAttributes.isEmpty()) { try { - TbMsgMetaData metaData = new TbMsgMetaData(); ObjectNode entityNode = objectMapper.createObjectNode(); - metaData.putValue("scope", DataConstants.SERVER_SCOPE); for (AttributeKvEntry attr : ssAttributes) { - if (attr.getDataType() == DataType.BOOLEAN) { + if (attr.getDataType() == DataType.BOOLEAN && attr.getBooleanValue().isPresent()) { entityNode.put(attr.getKey(), attr.getBooleanValue().get()); - } else if (attr.getDataType() == DataType.DOUBLE) { + } else if (attr.getDataType() == DataType.DOUBLE && attr.getDoubleValue().isPresent()) { entityNode.put(attr.getKey(), attr.getDoubleValue().get()); - } else if (attr.getDataType() == DataType.LONG) { + } else if (attr.getDataType() == DataType.LONG && attr.getLongValue().isPresent()) { entityNode.put(attr.getKey(), attr.getLongValue().get()); } else { entityNode.put(attr.getKey(), attr.getValueAsString()); } } - TbMsg tbMsg = TbMsg.newMsg(DataConstants.ATTRIBUTES_UPDATED, finalEntityId, metaData, TbMsgDataType.JSON - , objectMapper.writeValueAsString(entityNode)); - log.debug("Sending donwlink entity data msg, entityName [{}], tbMsg [{}]", finalEntityName, tbMsg); + log.debug("Sending attributes data msg, entityId [{}], attributes [{}]", finalEntityId, entityNode); + DownlinkMsg value = constructEntityDataProtoMsg(finalEntityId, ActionType.ATTRIBUTES_UPDATED, JsonUtils.parse(objectMapper.writeValueAsString(entityNode))); outputStream.onNext(ResponseMsg.newBuilder() - .setDownlinkMsg(constructEntityDataProtoMsg(finalEntityName, finalEntityId, tbMsg)) - .build()); + .setDownlinkMsg(value).build()); } catch (Exception e) { log.error("[{}] Failed to send attribute updates to the edge", edge.getName(), e); } @@ -283,188 +293,276 @@ public final class EdgeGrpcSession implements Closeable { private void processTelemetryMessage(EdgeEvent edgeEvent) throws IOException { log.trace("Executing processTelemetryMessage, edgeEvent [{}]", edgeEvent); - TbMsg tbMsg = TbMsg.fromBytes(Base64.decodeBase64(entry.getData()), TbMsgCallback.EMPTY); - String entityName = null; EntityId entityId = null; switch (edgeEvent.getEdgeEventType()) { case DEVICE: - Device device = ctx.getDeviceService().findDeviceById(edge.getTenantId(), new DeviceId(tbMsg.getOriginator().getId())); - entityName = device.getName(); - entityId = device.getId(); + entityId = new DeviceId(edgeEvent.getEntityId()); break; case ASSET: - Asset asset = ctx.getAssetService().findAssetById(edge.getTenantId(), new AssetId(tbMsg.getOriginator().getId())); - entityName = asset.getName(); - entityId = asset.getId(); + entityId = new AssetId(edgeEvent.getEntityId()); break; case ENTITY_VIEW: - EntityView entityView = ctx.getEntityViewService().findEntityViewById(edge.getTenantId(), new EntityViewId(tbMsg.getOriginator().getId())); - entityName = entityView.getName(); - entityId = entityView.getId(); + entityId = new EntityViewId(edgeEvent.getEntityId()); break; } - if (entityName != null && entityId != null) { - log.debug("Sending downlink entity data msg, entityName [{}], tbMsg [{}]", entityName, tbMsg); - outputStream.onNext(ResponseMsg.newBuilder() - .setDownlinkMsg(constructEntityDataProtoMsg(entityName, entityId, tbMsg)) - .build()); + if (entityId != null) { + log.debug("Sending telemetry data msg, entityId [{}], body [{}]", edgeEvent.getEntityId(), edgeEvent.getEntityBody()); + DownlinkMsg downlinkMsg; + try { + ActionType actionType = ActionType.valueOf(edgeEvent.getEdgeEventAction()); + downlinkMsg = constructEntityDataProtoMsg(entityId, actionType, JsonUtils.parse(objectMapper.writeValueAsString(edgeEvent.getEntityBody()))); + outputStream.onNext(ResponseMsg.newBuilder() + .setDownlinkMsg(downlinkMsg) + .build()); + } catch (Exception e) { + log.warn("Can't send telemetry data msg, entityId [{}], body [{}]", edgeEvent.getEntityId(), edgeEvent.getEntityBody(), e); + } + } } - private void processEntityCRUDMessage(EdgeEvent edgeEvent, UpdateMsgType msgType) throws java.io.IOException { + private void processEntityCRUDMessage(EdgeEvent edgeEvent, UpdateMsgType msgType) { log.trace("Executing processEntityCRUDMessage, edgeEvent [{}], msgType [{}]", edgeEvent, msgType); switch (edgeEvent.getEdgeEventType()) { case EDGE: - Edge edge = objectMapper.readValue(entry.getData(), Edge.class); - onEdgeUpdated(msgType, edge); + // TODO: voba - add edge update logic break; case DEVICE: - Device device = objectMapper.readValue(entry.getData(), Device.class); - onDeviceUpdated(msgType, device); + processDeviceCRUD(edgeEvent, msgType); break; case ASSET: - Asset asset = objectMapper.readValue(entry.getData(), Asset.class); - onAssetUpdated(msgType, asset); + processAssetCRUD(edgeEvent, msgType); break; case ENTITY_VIEW: - EntityView entityView = objectMapper.readValue(entry.getData(), EntityView.class); - onEntityViewUpdated(msgType, entityView); + processEntityViewCRUD(edgeEvent, msgType); break; case DASHBOARD: - Dashboard dashboard = objectMapper.readValue(entry.getData(), Dashboard.class); - onDashboardUpdated(msgType, dashboard); + processDashboardCRUD(edgeEvent, msgType); break; case RULE_CHAIN: - RuleChain ruleChain = objectMapper.readValue(entry.getData(), RuleChain.class); - onRuleChainUpdated(msgType, ruleChain); + processRuleChainCRUD(edgeEvent, msgType); break; case RULE_CHAIN_METADATA: - RuleChainMetaData ruleChainMetaData = objectMapper.readValue(entry.getData(), RuleChainMetaData.class); - onRuleChainMetadataUpdated(msgType, ruleChainMetaData); + processRuleChainMetadataCRUD(edgeEvent, msgType); break; case ALARM: - Alarm alarm = objectMapper.readValue(entry.getData(), Alarm.class); - onAlarmUpdated(msgType, alarm); + processAlarmCRUD(edgeEvent, msgType); break; case USER: - User user = objectMapper.readValue(entry.getData(), User.class); - onUserUpdated(msgType, user); + processUserCRUD(edgeEvent, msgType); break; case RELATION: - EntityRelation entityRelation = objectMapper.readValue(entry.getData(), EntityRelation.class); - onEntityRelationUpdated(msgType, entityRelation); + processRelationCRUD(edgeEvent, msgType); break; } } - private void updateQueueStartTs(Long newStartTs) { - newStartTs = ++newStartTs; // increments ts by 1 - next edge event search starts from current offset + 1 - List attributes = Collections.singletonList(new BaseAttributeKvEntry(new LongDataEntry(QUEUE_START_TS_ATTR_KEY, newStartTs), System.currentTimeMillis())); - ctx.getAttributesService().save(edge.getTenantId(), edge.getId(), DataConstants.SERVER_SCOPE, attributes); - } - - private ListenableFuture getQueueStartTs() { - ListenableFuture> future = - ctx.getAttributesService().find(edge.getTenantId(), edge.getId(), DataConstants.SERVER_SCOPE, QUEUE_START_TS_ATTR_KEY); - return Futures.transform(future, attributeKvEntryOpt -> { - if (attributeKvEntryOpt != null && attributeKvEntryOpt.isPresent()) { - AttributeKvEntry attributeKvEntry = attributeKvEntryOpt.get(); - return attributeKvEntry.getLongValue().isPresent() ? attributeKvEntry.getLongValue().get() : 0L; - } else { - return 0L; - } - }, MoreExecutors.directExecutor()); - } + private void processDeviceCRUD(EdgeEvent edgeEvent, UpdateMsgType msgType) { + DeviceId deviceId = new DeviceId(edgeEvent.getEntityId()); + ListenableFuture deviceFuture = ctx.getDeviceService().findDeviceByIdAsync(edgeEvent.getTenantId(), deviceId); + Futures.addCallback(deviceFuture, + new FutureCallback() { + @Override + public void onSuccess(@Nullable Device device) { + if (device != null) { + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setDeviceUpdateMsg(ctx.getDeviceUpdateMsgConstructor().constructDeviceUpdatedMsg(msgType, device)) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + } + } - private void onEdgeUpdated(UpdateMsgType msgType, Edge edge) { - // TODO: voba add configuration update to edge - this.edge = edge; - } + @Override + public void onFailure(Throwable t) { + log.warn("Can't processDeviceCRUD, edgeEvent [{}]", edgeEvent, t); + } + }, ctx.getDbCallbackExecutor()); + } + + private void processAssetCRUD(EdgeEvent edgeEvent, UpdateMsgType msgType) { + AssetId assetId = new AssetId(edgeEvent.getEntityId()); + ListenableFuture assetFuture = ctx.getAssetService().findAssetByIdAsync(edgeEvent.getTenantId(), assetId); + Futures.addCallback(assetFuture, + new FutureCallback() { + @Override + public void onSuccess(@Nullable Asset asset) { + if (asset != null) { + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setAssetUpdateMsg(ctx.getAssetUpdateMsgConstructor().constructAssetUpdatedMsg(msgType, asset)) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + } + } - private void onDeviceUpdated(UpdateMsgType msgType, Device device) { - EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() - .setDeviceUpdateMsg(ctx.getDeviceUpdateMsgConstructor().constructDeviceUpdatedMsg(msgType, device)) - .build(); - outputStream.onNext(ResponseMsg.newBuilder() - .setEntityUpdateMsg(entityUpdateMsg) - .build()); - } + @Override + public void onFailure(Throwable t) { + log.warn("Can't processAssetCRUD, edgeEvent [{}]", edgeEvent, t); + } + }, ctx.getDbCallbackExecutor()); + } + + private void processEntityViewCRUD(EdgeEvent edgeEvent, UpdateMsgType msgType) { + EntityViewId entityViewId = new EntityViewId(edgeEvent.getEntityId()); + ListenableFuture entityViewFuture = ctx.getEntityViewService().findEntityViewByIdAsync(edgeEvent.getTenantId(), entityViewId); + Futures.addCallback(entityViewFuture, + new FutureCallback() { + @Override + public void onSuccess(@Nullable EntityView entityView) { + if (entityView != null) { + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setEntityViewUpdateMsg(ctx.getEntityViewUpdateMsgConstructor().constructEntityViewUpdatedMsg(msgType, entityView)) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + } + } - private void onAssetUpdated(UpdateMsgType msgType, Asset asset) { - EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() - .setAssetUpdateMsg(ctx.getAssetUpdateMsgConstructor().constructAssetUpdatedMsg(msgType, asset)) - .build(); - outputStream.onNext(ResponseMsg.newBuilder() - .setEntityUpdateMsg(entityUpdateMsg) - .build()); - } + @Override + public void onFailure(Throwable t) { + log.warn("Can't processEntityViewCRUD, edgeEvent [{}]", edgeEvent, t); + } + }, ctx.getDbCallbackExecutor()); + } + + private void processDashboardCRUD(EdgeEvent edgeEvent, UpdateMsgType msgType) { + DashboardId dashboardId = new DashboardId(edgeEvent.getEntityId()); + ListenableFuture dashboardFuture = ctx.getDashboardService().findDashboardByIdAsync(edgeEvent.getTenantId(), dashboardId); + Futures.addCallback(dashboardFuture, + new FutureCallback() { + @Override + public void onSuccess(@Nullable Dashboard dashboard) { + if (dashboard != null) { + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setDashboardUpdateMsg(ctx.getDashboardUpdateMsgConstructor().constructDashboardUpdatedMsg(msgType, dashboard)) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + } + } - private void onEntityViewUpdated(UpdateMsgType msgType, EntityView entityView) { - EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() - .setEntityViewUpdateMsg(ctx.getEntityViewUpdateMsgConstructor().constructEntityViewUpdatedMsg(msgType, entityView)) - .build(); - outputStream.onNext(ResponseMsg.newBuilder() - .setEntityUpdateMsg(entityUpdateMsg) - .build()); - } + @Override + public void onFailure(Throwable t) { + log.warn("Can't processDashboardCRUD, edgeEvent [{}]", edgeEvent, t); + } + }, ctx.getDbCallbackExecutor()); + } + + private void processRuleChainCRUD(EdgeEvent edgeEvent, UpdateMsgType msgType) { + RuleChainId ruleChainId = new RuleChainId(edgeEvent.getEntityId()); + ListenableFuture ruleChainFuture = ctx.getRuleChainService().findRuleChainByIdAsync(edgeEvent.getTenantId(), ruleChainId); + Futures.addCallback(ruleChainFuture, + new FutureCallback() { + @Override + public void onSuccess(@Nullable RuleChain ruleChain) { + if (ruleChain != null) { + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setRuleChainUpdateMsg(ctx.getRuleChainUpdateMsgConstructor().constructRuleChainUpdatedMsg(edge.getRootRuleChainId(), msgType, ruleChain)) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + } + } - private void onRuleChainUpdated(UpdateMsgType msgType, RuleChain ruleChain) { - EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() - .setRuleChainUpdateMsg(ctx.getRuleChainUpdateMsgConstructor().constructRuleChainUpdatedMsg(edge.getRootRuleChainId(), msgType, ruleChain)) - .build(); - outputStream.onNext(ResponseMsg.newBuilder() - .setEntityUpdateMsg(entityUpdateMsg) - .build()); - } + @Override + public void onFailure(Throwable t) { + log.warn("Can't processRuleChainCRUD, edgeEvent [{}]", edgeEvent, t); + } + }, ctx.getDbCallbackExecutor()); + } + + private void processRuleChainMetadataCRUD(EdgeEvent edgeEvent, UpdateMsgType msgType) { + RuleChainId ruleChainId = new RuleChainId(edgeEvent.getEntityId()); + ListenableFuture ruleChainFuture = ctx.getRuleChainService().findRuleChainByIdAsync(edgeEvent.getTenantId(), ruleChainId); + Futures.addCallback(ruleChainFuture, + new FutureCallback() { + @Override + public void onSuccess(@Nullable RuleChain ruleChain) { + if (ruleChain != null) { + RuleChainMetaData ruleChainMetaData = ctx.getRuleChainService().loadRuleChainMetaData(edgeEvent.getTenantId(), ruleChainId); + RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = + ctx.getRuleChainUpdateMsgConstructor().constructRuleChainMetadataUpdatedMsg(msgType, ruleChainMetaData); + if (ruleChainMetadataUpdateMsg != null) { + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setRuleChainMetadataUpdateMsg(ruleChainMetadataUpdateMsg) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + } + } + } - private void onRuleChainMetadataUpdated(UpdateMsgType msgType, RuleChainMetaData ruleChainMetaData) { - RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = - ctx.getRuleChainUpdateMsgConstructor().constructRuleChainMetadataUpdatedMsg(msgType, ruleChainMetaData); - if (ruleChainMetadataUpdateMsg != null) { - EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() - .setRuleChainMetadataUpdateMsg(ruleChainMetadataUpdateMsg) - .build(); - outputStream.onNext(ResponseMsg.newBuilder() - .setEntityUpdateMsg(entityUpdateMsg) - .build()); - } - } + @Override + public void onFailure(Throwable t) { + log.warn("Can't processRuleChainMetadataCRUD, edgeEvent [{}]", edgeEvent, t); + } + }, ctx.getDbCallbackExecutor()); + } + + private void processUserCRUD(EdgeEvent edgeEvent, UpdateMsgType msgType) { + UserId userId = new UserId(edgeEvent.getEntityId()); + ListenableFuture userFuture = ctx.getUserService().findUserByIdAsync(edgeEvent.getTenantId(), userId); + Futures.addCallback(userFuture, + new FutureCallback() { + @Override + public void onSuccess(@Nullable User user) { + if (user != null) { + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setUserUpdateMsg(ctx.getUserUpdateMsgConstructor().constructUserUpdatedMsg(msgType, user)) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + } + } - private void onDashboardUpdated(UpdateMsgType msgType, Dashboard dashboard) { - EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() - .setDashboardUpdateMsg(ctx.getDashboardUpdateMsgConstructor().constructDashboardUpdatedMsg(msgType, dashboard)) - .build(); - outputStream.onNext(ResponseMsg.newBuilder() - .setEntityUpdateMsg(entityUpdateMsg) - .build()); + @Override + public void onFailure(Throwable t) { + log.warn("Can't processUserCRUD, edgeEvent [{}]", edgeEvent, t); + } + }, ctx.getDbCallbackExecutor()); } - private void onAlarmUpdated(UpdateMsgType msgType, Alarm alarm) { + private void processRelationCRUD(EdgeEvent edgeEvent, UpdateMsgType msgType) { + EntityRelation entityRelation = objectMapper.convertValue(edgeEvent.getEntityBody(), EntityRelation.class); EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() - .setAlarmUpdateMsg(ctx.getAlarmUpdateMsgConstructor().constructAlarmUpdatedMsg(edge.getTenantId(), msgType, alarm)) + .setRelationUpdateMsg(ctx.getRelationUpdateMsgConstructor().constructRelationUpdatedMsg(msgType, entityRelation)) .build(); outputStream.onNext(ResponseMsg.newBuilder() .setEntityUpdateMsg(entityUpdateMsg) .build()); } - private void onUserUpdated(UpdateMsgType msgType, User user) { - EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() - .setUserUpdateMsg(ctx.getUserUpdateMsgConstructor().constructUserUpdatedMsg(msgType, user)) - .build(); - outputStream.onNext(ResponseMsg.newBuilder() - .setEntityUpdateMsg(entityUpdateMsg) - .build()); - } + private void processAlarmCRUD(EdgeEvent edgeEvent, UpdateMsgType msgType) { + AlarmId alarmId = new AlarmId(edgeEvent.getEntityId()); + ListenableFuture alarmFuture = ctx.getAlarmService().findAlarmByIdAsync(edgeEvent.getTenantId(), alarmId); + Futures.addCallback(alarmFuture, + new FutureCallback() { + @Override + public void onSuccess(@Nullable Alarm alarm) { + if (alarm != null) { + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setAlarmUpdateMsg(ctx.getAlarmUpdateMsgConstructor().constructAlarmUpdatedMsg(edge.getTenantId(), msgType, alarm)) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + } + } - private void onEntityRelationUpdated(UpdateMsgType msgType, EntityRelation entityRelation) { - EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() - .setRelationUpdateMsg(ctx.getRelationUpdateMsgConstructor().constructRelationUpdatedMsg(msgType, entityRelation)) - .build(); - outputStream.onNext(ResponseMsg.newBuilder() - .setEntityUpdateMsg(entityUpdateMsg) - .build()); + @Override + public void onFailure(Throwable t) { + log.warn("Can't processAlarmCRUD, edgeEvent [{}]", edgeEvent, t); + } + }, ctx.getDbCallbackExecutor()); } private UpdateMsgType getResponseMsgType(ActionType actionType) { @@ -490,29 +588,10 @@ public final class EdgeGrpcSession implements Closeable { } } - private DownlinkMsg constructEntityDataProtoMsg(String entityName, EntityId entityId, TbMsg tbMsg) { - EntityDataProto entityData = EntityDataProto.newBuilder() - .setEntityName(entityName) - .setTbMsg(ByteString.copyFrom(TbMsg.toByteArray(tbMsg))) - .setEntityIdMSB(entityId.getId().getMostSignificantBits()) - .setEntityIdLSB(entityId.getId().getLeastSignificantBits()) - .build(); - + private DownlinkMsg constructEntityDataProtoMsg(EntityId entityId, ActionType actionType, JsonElement entityData) { + EntityDataProto entityDataProto = ctx.getEntityDataMsgConstructor().constructEntityDataMsg(entityId, actionType, entityData); DownlinkMsg.Builder builder = DownlinkMsg.newBuilder() - .addAllEntityData(Collections.singletonList(entityData)); - - return builder.build(); - } - - private CustomerUpdateMsg constructCustomerUpdatedMsg(UpdateMsgType msgType, Customer customer) { - CustomerUpdateMsg.Builder builder = CustomerUpdateMsg.newBuilder() - .setMsgType(msgType); - return builder.build(); - } - - private UserUpdateMsg constructUserUpdatedMsg(UpdateMsgType msgType, User user) { - UserUpdateMsg.Builder builder = UserUpdateMsg.newBuilder() - .setMsgType(msgType); + .addAllEntityData(Collections.singletonList(entityDataProto)); return builder.build(); } @@ -520,20 +599,21 @@ public final class EdgeGrpcSession implements Closeable { try { if (uplinkMsg.getEntityDataList() != null && !uplinkMsg.getEntityDataList().isEmpty()) { for (EntityDataProto entityData : uplinkMsg.getEntityDataList()) { - TbMsg tbMsg = null; - TbMsg originalTbMsg = TbMsg.fromBytes(entityData.getTbMsg().toByteArray(), TbMsgCallback.EMPTY); - if (originalTbMsg.getOriginator().getEntityType() == EntityType.DEVICE) { - String deviceName = entityData.getEntityName(); - Device device = ctx.getDeviceService().findDeviceByTenantIdAndName(edge.getTenantId(), deviceName); - if (device != null) { - tbMsg = TbMsg.newMsg(originalTbMsg.getType(), device.getId(), originalTbMsg.getMetaData().copy(), - originalTbMsg.getDataType(), originalTbMsg.getData()); - } - } else { - tbMsg = originalTbMsg; - } - if (tbMsg != null) { - ctx.getTbClusterService().pushMsgToRuleEngine(edge.getTenantId(), tbMsg.getOriginator(), tbMsg, null); + EntityId entityId = constructEntityId(entityData); + if ((entityData.hasPostAttributesMsg() || entityData.hasPostTelemetryMsg()) && entityId != null) { + ListenableFuture metaDataFuture = constructBaseMsgMetadata(entityId); + Futures.transform(metaDataFuture, metaData -> { + if (metaData != null) { + metaData.putValue(DataConstants.MSG_SOURCE_KEY, DataConstants.EDGE_MSG_SOURCE); + if (entityData.hasPostAttributesMsg()) { + processPostAttributes(entityId, entityData.getPostAttributesMsg(), metaData); + } + if (entityData.hasPostTelemetryMsg()) { + processPostTelemetry(entityId, entityData.getPostTelemetryMsg(), metaData); + } + } + return null; + }, ctx.getDbCallbackExecutor()); } } } @@ -559,6 +639,78 @@ public final class EdgeGrpcSession implements Closeable { return UplinkResponseMsg.newBuilder().setSuccess(true).build(); } + private ListenableFuture constructBaseMsgMetadata(EntityId entityId) { + switch (entityId.getEntityType()) { + case DEVICE: + ListenableFuture deviceFuture = ctx.getDeviceService().findDeviceByIdAsync(edge.getTenantId(), new DeviceId(entityId.getId())); + return Futures.transform(deviceFuture, device -> { + TbMsgMetaData metaData = new TbMsgMetaData(); + if (device != null) { + metaData.putValue("deviceName", device.getName()); + metaData.putValue("deviceType", device.getType()); + } + return metaData; + }, ctx.getDbCallbackExecutor()); + case ASSET: + ListenableFuture assetFuture = ctx.getAssetService().findAssetByIdAsync(edge.getTenantId(), new AssetId(entityId.getId())); + return Futures.transform(assetFuture, asset -> { + TbMsgMetaData metaData = new TbMsgMetaData(); + if (asset != null) { + metaData.putValue("assetName", asset.getName()); + metaData.putValue("assetType", asset.getType()); + } + return metaData; + }, ctx.getDbCallbackExecutor()); + case ENTITY_VIEW: + ListenableFuture entityViewFuture = ctx.getEntityViewService().findEntityViewByIdAsync(edge.getTenantId(), new EntityViewId(entityId.getId())); + return Futures.transform(entityViewFuture, entityView -> { + TbMsgMetaData metaData = new TbMsgMetaData(); + if (entityView != null) { + metaData.putValue("entityViewName", entityView.getName()); + metaData.putValue("entityViewType", entityView.getType()); + } + return metaData; + }, ctx.getDbCallbackExecutor()); + default: + log.debug("Constructing empty metadata for entityId [{}]", entityId); + return Futures.immediateFuture(new TbMsgMetaData()); + } + } + + private EntityId constructEntityId(EntityDataProto entityData) { + EntityType entityType = EntityType.valueOf(entityData.getEntityType()); + switch (entityType) { + case DEVICE: + return new DeviceId(new UUID(entityData.getEntityIdMSB(), entityData.getEntityIdLSB())); + case ASSET: + return new AssetId(new UUID(entityData.getEntityIdMSB(), entityData.getEntityIdLSB())); + case ENTITY_VIEW: + return new EntityViewId(new UUID(entityData.getEntityIdMSB(), entityData.getEntityIdLSB())); + case DASHBOARD: + return new DashboardId(new UUID(entityData.getEntityIdMSB(), entityData.getEntityIdLSB())); + default: + log.warn("Unsupported entity type [{}] during construct of entity id. EntityDataProto [{}]", entityData.getEntityType(), entityData); + return null; + } + } + + private void processPostTelemetry(EntityId entityId, TransportProtos.PostTelemetryMsg msg, TbMsgMetaData metaData) { + for (TransportProtos.TsKvListProto tsKv : msg.getTsKvListList()) { + JsonObject json = JsonUtils.getJsonObject(tsKv.getKvList()); + metaData.putValue("ts", tsKv.getTs() + ""); + TbMsg tbMsg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), entityId, metaData, gson.toJson(json)); + // TODO: voba - verify that null callback is OK + ctx.getTbClusterService().pushMsgToRuleEngine(edge.getTenantId(), tbMsg.getOriginator(), tbMsg, null); + } + } + + private void processPostAttributes(EntityId entityId, TransportProtos.PostAttributeMsg msg, TbMsgMetaData metaData) { + JsonObject json = JsonUtils.getJsonObject(msg.getKvList()); + TbMsg tbMsg = TbMsg.newMsg(SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), entityId, metaData, gson.toJson(json)); + // TODO: voba - verify that null callback is OK + ctx.getTbClusterService().pushMsgToRuleEngine(edge.getTenantId(), tbMsg.getOriginator(), tbMsg, null); + } + private void onDeviceUpdate(DeviceUpdateMsg deviceUpdateMsg) { log.info("onDeviceUpdate {}", deviceUpdateMsg); DeviceId edgeDeviceId = new DeviceId(new UUID(deviceUpdateMsg.getIdMSB(), deviceUpdateMsg.getIdLSB())); @@ -759,7 +911,7 @@ public final class EdgeGrpcSession implements Closeable { .setTenantIdLSB(edge.getTenantId().getId().getLeastSignificantBits()) .setName(edge.getName()) .setRoutingKey(edge.getRoutingKey()) - .setType(edge.getType().toString()) + .setType(edge.getType()) .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/EntityDataMsgConstructor.java new file mode 100644 index 0000000000..d9a06655c5 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityDataMsgConstructor.java @@ -0,0 +1,56 @@ +/** + * Copyright © 2016-2020 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.gson.JsonElement; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.transport.adaptor.JsonConverter; +import org.thingsboard.server.gen.edge.EntityDataProto; + +@Component +@Slf4j +public class EntityDataMsgConstructor { + + public EntityDataProto constructEntityDataMsg(EntityId entityId, ActionType actionType, JsonElement entityData) { + EntityDataProto.Builder builder = EntityDataProto.newBuilder() + .setEntityIdMSB(entityId.getId().getMostSignificantBits()) + .setEntityIdLSB(entityId.getId().getLeastSignificantBits()) + .setEntityType(entityId.getEntityType().name()); + switch (actionType) { + case TIMESERIES_UPDATED: + try { + builder.setPostTelemetryMsg(JsonConverter.convertToTelemetryProto(entityData)); + } catch (Exception e) { + log.warn("Can't convert to telemetry proto, entityData [{}]", entityData, e); + } + break; + case ATTRIBUTES_UPDATED: + try { + builder.setPostAttributesMsg(JsonConverter.convertToAttributesProto(entityData)); + } catch (Exception e) { + log.warn("Can't convert to attributes proto, entityData [{}]", entityData, e); + } + break; + // TODO: voba - add support for attribute delete + // case ATTRIBUTES_DELETED: + } + return builder.build(); + } + +} 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 01b9e86d25..ccb3b8765f 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 @@ -65,4 +65,7 @@ public class DataConstants { public static final String DEFAULT_SECRET_KEY = ""; public static final String SECRET_KEY_FIELD_NAME = "secretKey"; public static final String DURATION_MS_FIELD_NAME = "durationMs"; + + public static final String EDGE_MSG_SOURCE = "edge"; + public static final String MSG_SOURCE_KEY = "source"; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java index 583b3b8e67..17d86e26ec 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java @@ -16,6 +16,7 @@ package org.thingsboard.server.common.data.id; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.edge.EdgeEventType; import java.util.UUID; @@ -68,4 +69,28 @@ public class EntityIdFactory { throw new IllegalArgumentException("EntityType " + type + " is not supported!"); } + public static EntityId getByEdgeEventTypeAndUuid(EdgeEventType edgeEventType, UUID uuid) { + switch (edgeEventType) { + case CUSTOMER: + return new CustomerId(uuid); + case USER: + return new UserId(uuid); + case DASHBOARD: + return new DashboardId(uuid); + case DEVICE: + return new DeviceId(uuid); + case ASSET: + return new AssetId(uuid); + case ALARM: + return new AlarmId(uuid); + case RULE_CHAIN: + return new RuleChainId(uuid); + case ENTITY_VIEW: + return new EntityViewId(uuid); + case EDGE: + return new EdgeId(uuid); + } + throw new IllegalArgumentException("EdgeEventType " + edgeEventType + " is not supported!"); + } + } diff --git a/common/edge-api/src/main/proto/edge.proto b/common/edge-api/src/main/proto/edge.proto index 787de743be..38cbe7092c 100644 --- a/common/edge-api/src/main/proto/edge.proto +++ b/common/edge-api/src/main/proto/edge.proto @@ -100,9 +100,9 @@ enum UpdateMsgType { } message EntityDataProto { - string entityName = 1; - int64 entityIdMSB = 2; - int64 entityIdLSB = 3; + int64 entityIdMSB = 1; + int64 entityIdLSB = 2; + string entityType = 3; transport.PostTelemetryMsg postTelemetryMsg = 4; transport.PostAttributeMsg postAttributesMsg = 5; // transport.ToDeviceRpcRequestMsg ??? diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeEventService.java b/dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeEventService.java index 134ffe3b06..fb8f3a03a4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeEventService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeEventService.java @@ -52,6 +52,8 @@ public class BaseEdgeEventService implements EdgeEventService { return EdgeEventType.DASHBOARD; case USER: return EdgeEventType.USER; + case ALARM: + return EdgeEventType.ALARM; default: log.warn("Failed to push notification to edge service. Unsupported entity type [{}]", entityType); return null; diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/edge/TbMsgPushToEdgeNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/edge/TbMsgPushToEdgeNode.java index 1e42224c70..ad757220b3 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/edge/TbMsgPushToEdgeNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/edge/TbMsgPushToEdgeNode.java @@ -70,34 +70,23 @@ public class TbMsgPushToEdgeNode implements TbNode { @Override public void onMsg(TbContext ctx, TbMsg msg) { - if (EntityType.DEVICE.equals(msg.getOriginator().getEntityType()) || - EntityType.DEVICE.equals(msg.getOriginator().getEntityType()) || - EntityType.DEVICE.equals(msg.getOriginator().getEntityType()) || - EntityType.DEVICE.equals(msg.getOriginator().getEntityType())) { - if (SessionMsgType.POST_TELEMETRY_REQUEST.name().equals(msg.getType()) || - SessionMsgType.POST_ATTRIBUTES_REQUEST.name().equals(msg.getType()) || - DataConstants.ATTRIBUTES_UPDATED.equals(msg.getType()) || - DataConstants.ATTRIBUTES_DELETED.equals(msg.getType())) { + if (DataConstants.EDGE_MSG_SOURCE.equalsIgnoreCase(msg.getMetaData().getValue(DataConstants.MSG_SOURCE_KEY))) { + log.debug("Ignoring msg from the cloud, msg [{}]", msg); + return; + } + if (isSupportedOriginator(msg.getOriginator().getEntityType())) { + if (isSupportedMsgType(msg.getType())) { ListenableFuture getEdgeIdFuture = getEdgeIdByOriginatorId(ctx, ctx.getTenantId(), msg.getOriginator()); Futures.transform(getEdgeIdFuture, edgeId -> { EdgeEventType edgeEventTypeByEntityType = ctx.getEdgeEventService().getEdgeEventTypeByEntityType(msg.getOriginator().getEntityType()); if (edgeEventTypeByEntityType == null) { log.debug("Edge event type is null. Entity Type {}", msg.getOriginator().getEntityType()); - ctx.tellFailure(msg, new RuntimeException("Edge event type is null. Entity Type '"+ msg.getOriginator().getEntityType() + "'")); - } - ActionType actionType; - if (SessionMsgType.POST_TELEMETRY_REQUEST.name().equals(msg.getType())) { - actionType = ActionType.TIMESERIES_UPDATED; - } else if (SessionMsgType.POST_ATTRIBUTES_REQUEST.name().equals(msg.getType()) || - DataConstants.ATTRIBUTES_UPDATED.equals(msg.getType())) { - actionType = ActionType.ATTRIBUTES_UPDATED; - } else { - actionType = ActionType.ATTRIBUTES_DELETED; + ctx.tellFailure(msg, new RuntimeException("Edge event type is null. Entity Type '" + msg.getOriginator().getEntityType() + "'")); } EdgeEvent edgeEvent = new EdgeEvent(); edgeEvent.setTenantId(ctx.getTenantId()); edgeEvent.setEdgeId(edgeId); - edgeEvent.setEdgeEventAction(actionType.name()); + edgeEvent.setEdgeEventAction(getActionTypeByMsgType(msg.getType()).name()); edgeEvent.setEntityId(msg.getOriginator().getId()); edgeEvent.setEdgeEventType(edgeEventTypeByEntityType); edgeEvent.setEntityBody(json.valueToTree(msg.getData())); @@ -126,6 +115,42 @@ public class TbMsgPushToEdgeNode implements TbNode { } } + private ActionType getActionTypeByMsgType(String msgType) { + ActionType actionType; + if (SessionMsgType.POST_TELEMETRY_REQUEST.name().equals(msgType)) { + actionType = ActionType.TIMESERIES_UPDATED; + } else if (SessionMsgType.POST_ATTRIBUTES_REQUEST.name().equals(msgType) + || DataConstants.ATTRIBUTES_UPDATED.equals(msgType)) { + actionType = ActionType.ATTRIBUTES_UPDATED; + } else { + actionType = ActionType.ATTRIBUTES_DELETED; + } + return actionType; + } + + private boolean isSupportedOriginator(EntityType entityType) { + switch (entityType) { + case DEVICE: + case ASSET: + case ENTITY_VIEW: + case DASHBOARD: + return true; + default: + return false; + } + } + + private boolean isSupportedMsgType(String msgType) { + if (SessionMsgType.POST_TELEMETRY_REQUEST.name().equals(msgType) + || SessionMsgType.POST_ATTRIBUTES_REQUEST.name().equals(msgType) + || DataConstants.ATTRIBUTES_UPDATED.equals(msgType) + || DataConstants.ATTRIBUTES_DELETED.equals(msgType)) { + return true; + } else { + return false; + } + } + private ListenableFuture getEdgeIdByOriginatorId(TbContext ctx, TenantId tenantId, EntityId originatorId) { ListenableFuture> future = ctx.getRelationService().findByToAndTypeAsync(tenantId, originatorId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE); return Futures.transform(future, relations -> { diff --git a/ui/src/app/rulechain/rulechain.routes.js b/ui/src/app/rulechain/rulechain.routes.js index 2991000e58..08dc2f522e 100644 --- a/ui/src/app/rulechain/rulechain.routes.js +++ b/ui/src/app/rulechain/rulechain.routes.js @@ -37,7 +37,7 @@ export default function RuleChainRoutes($stateProvider, NodeTemplatePathProvider } }) .state('home.ruleChains.core', { - url: '/ruleChains/system', + url: '/ruleChains/core', params: {'topIndex': 0}, module: 'private', auth: ['SYS_ADMIN', 'TENANT_ADMIN'], From 8462481ebfc2960b340720569841aa29b42f58ed Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Thu, 18 Jun 2020 18:57:01 +0300 Subject: [PATCH 51/52] Refacroting of pushing edge notification events --- .../server/controller/AlarmController.java | 8 +- .../server/controller/AssetController.java | 10 +- .../server/controller/BaseController.java | 14 +- .../controller/DashboardController.java | 10 +- .../server/controller/DeviceController.java | 11 +- .../controller/EntityViewController.java | 11 +- .../controller/RuleChainController.java | 12 +- .../edge/DefaultEdgeNotificationService.java | 115 ++++--- .../service/edge/rpc/EdgeGrpcSession.java | 317 +++++++++++------- .../AssetUpdateMsgConstructor.java | 7 + .../DashboardUpdateMsgConstructor.java | 8 + .../DeviceUpdateMsgConstructor.java | 8 + .../EntityViewUpdateMsgConstructor.java | 7 + .../RuleChainUpdateMsgConstructor.java | 9 +- .../constructor/UserUpdateMsgConstructor.java | 8 + .../server/dao/edge/EdgeService.java | 1 - common/queue/src/main/proto/queue.proto | 18 +- 17 files changed, 379 insertions(+), 195 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java index e56b4b34ef..01c5ea28ae 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java @@ -91,6 +91,9 @@ public class AlarmController extends BaseController { logEntityAction(savedAlarm.getId(), savedAlarm, getCurrentUser().getCustomerId(), alarm.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); + + sendNotificationMsgToEdgeService(getTenantId(), savedAlarm.getId(), alarm.getId() == null ? ActionType.ADDED : ActionType.UPDATED); + return savedAlarm; } catch (Exception e) { logEntityAction(emptyId(EntityType.ALARM), alarm, @@ -107,8 +110,11 @@ public class AlarmController extends BaseController { try { AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); checkAlarmId(alarmId, Operation.WRITE); + + sendNotificationMsgToEdgeService(getTenantId(), alarmId, ActionType.DELETED); + return alarmService.deleteAlarm(getTenantId(), alarmId); - } catch (Exception e) { + } catch (Exception e) { throw handleException(e); } } diff --git a/application/src/main/java/org/thingsboard/server/controller/AssetController.java b/application/src/main/java/org/thingsboard/server/controller/AssetController.java index 31f256c03a..20fae1e4c6 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AssetController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AssetController.java @@ -88,7 +88,8 @@ public class AssetController extends BaseController { Asset savedAsset = checkNotNull(assetService.saveAsset(asset)); - sendNotificationMsgToEdgeService(savedAsset.getTenantId(), savedAsset.getId(), EdgeEventType.ASSET, asset.getId() == null ? ActionType.ADDED : ActionType.UPDATED); + sendNotificationMsgToEdgeService(savedAsset.getTenantId(), null, + savedAsset.getId(), EdgeEventType.ASSET, asset.getId() == null ? ActionType.ADDED : ActionType.UPDATED); logEntityAction(savedAsset.getId(), savedAsset, savedAsset.getCustomerId(), @@ -116,7 +117,7 @@ public class AssetController extends BaseController { asset.getCustomerId(), ActionType.DELETED, null, strAssetId); - sendNotificationMsgToEdgeService(getTenantId(), assetId, EdgeEventType.ASSET, ActionType.DELETED); + sendNotificationMsgToEdgeService(getTenantId(), null, assetId, EdgeEventType.ASSET, ActionType.DELETED); } catch (Exception e) { logEntityAction(emptyId(EntityType.ASSET), null, @@ -359,7 +360,7 @@ public class AssetController extends BaseController { savedAsset.getCustomerId(), ActionType.ASSIGNED_TO_EDGE, null, strAssetId, strEdgeId, edge.getName()); - sendNotificationMsgToEdgeService(getTenantId(), savedAsset.getId(), EdgeEventType.ASSET, ActionType.ASSIGNED_TO_EDGE); + sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedAsset.getId(), EdgeEventType.ASSET, ActionType.ASSIGNED_TO_EDGE); return savedAsset; } catch (Exception e) { @@ -392,7 +393,8 @@ public class AssetController extends BaseController { asset.getCustomerId(), ActionType.UNASSIGNED_FROM_EDGE, null, strAssetId, edge.getId().toString(), edge.getName()); - sendNotificationMsgToEdgeService(getTenantId(), savedAsset.getId(), EdgeEventType.ASSET, ActionType.UNASSIGNED_FROM_EDGE); + sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedAsset.getId(), + EdgeEventType.ASSET, ActionType.UNASSIGNED_FROM_EDGE); return savedAsset; } catch (Exception e) { 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 77f066795c..f71c98d63d 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -701,7 +701,7 @@ public abstract class BaseController { protected void sendNotificationMsgToEdgeService(TenantId tenantId, EntityRelation relation, ActionType edgeEventAction) { try { - sendNotificationMsgToEdgeService(tenantId, null, json.writeValueAsString(relation), EdgeEventType.RELATION, edgeEventAction); + sendNotificationMsgToEdgeService(tenantId, null, null, json.writeValueAsString(relation), EdgeEventType.RELATION, edgeEventAction); } catch (Exception e) { log.warn("Failed to push relation to core: {}", relation, e); } @@ -710,15 +710,15 @@ public abstract class BaseController { protected void sendNotificationMsgToEdgeService(TenantId tenantId, EntityId entityId, ActionType edgeEventAction) { EdgeEventType edgeEventType = edgeEventService.getEdgeEventTypeByEntityType(entityId.getEntityType()); if (edgeEventType != null) { - sendNotificationMsgToEdgeService(tenantId, entityId, null, edgeEventType, edgeEventAction); + sendNotificationMsgToEdgeService(tenantId, null, entityId, null, edgeEventType, edgeEventAction); } } - protected void sendNotificationMsgToEdgeService(TenantId tenantId, EntityId entityId, EdgeEventType edgeEventType, ActionType edgeEventAction) { - sendNotificationMsgToEdgeService(tenantId, entityId, null, edgeEventType, edgeEventAction); + protected void sendNotificationMsgToEdgeService(TenantId tenantId, EdgeId edgeId, EntityId entityId, EdgeEventType edgeEventType, ActionType edgeEventAction) { + sendNotificationMsgToEdgeService(tenantId, edgeId, entityId, null, edgeEventType, edgeEventAction); } - private void sendNotificationMsgToEdgeService(TenantId tenantId, EntityId entityId, String entityBody, EdgeEventType edgeEventType, ActionType edgeEventAction) { + private void sendNotificationMsgToEdgeService(TenantId tenantId, EdgeId edgeId, EntityId entityId, String entityBody, EdgeEventType edgeEventType, ActionType edgeEventAction) { TransportProtos.EdgeNotificationMsgProto.Builder builder = TransportProtos.EdgeNotificationMsgProto.newBuilder(); builder.setTenantIdMSB(tenantId.getId().getMostSignificantBits()); builder.setTenantIdLSB(tenantId.getId().getLeastSignificantBits()); @@ -729,6 +729,10 @@ public abstract class BaseController { builder.setEntityIdLSB(entityId.getId().getLeastSignificantBits()); builder.setEntityType(entityId.getEntityType().name()); } + if (edgeId != null) { + builder.setEdgeIdMSB(edgeId.getId().getMostSignificantBits()); + builder.setEdgeIdLSB(edgeId.getId().getLeastSignificantBits()); + } if (entityBody != null) { builder.setEntityBody(entityBody); } diff --git a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java index 90d991a400..22674b6ced 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java @@ -117,7 +117,7 @@ public class DashboardController extends BaseController { null, dashboard.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); - sendNotificationMsgToEdgeService(savedDashboard.getTenantId(), savedDashboard.getId(), + sendNotificationMsgToEdgeService(savedDashboard.getTenantId(), null, savedDashboard.getId(), EdgeEventType.DASHBOARD, savedDashboard.getId() == null ? ActionType.ADDED : ActionType.UPDATED); return savedDashboard; @@ -143,7 +143,7 @@ public class DashboardController extends BaseController { null, ActionType.DELETED, null, strDashboardId); - sendNotificationMsgToEdgeService(getTenantId(), dashboardId, EdgeEventType.DASHBOARD, ActionType.DELETED); + sendNotificationMsgToEdgeService(getTenantId(), null, dashboardId, EdgeEventType.DASHBOARD, ActionType.DELETED); } catch (Exception e) { logEntityAction(emptyId(EntityType.DASHBOARD), @@ -500,7 +500,8 @@ public class DashboardController extends BaseController { null, ActionType.ASSIGNED_TO_EDGE, null, strDashboardId, strEdgeId, edge.getName()); - sendNotificationMsgToEdgeService(getTenantId(), savedDashboard.getId(), EdgeEventType.DASHBOARD, ActionType.ASSIGNED_TO_EDGE); + sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedDashboard.getId(), + EdgeEventType.DASHBOARD, ActionType.ASSIGNED_TO_EDGE); return savedDashboard; } catch (Exception e) { @@ -532,7 +533,8 @@ public class DashboardController extends BaseController { null, ActionType.UNASSIGNED_FROM_EDGE, null, strDashboardId, edge.getId().toString(), edge.getName()); - sendNotificationMsgToEdgeService(getTenantId(), savedDashboard.getId(), EdgeEventType.DASHBOARD, ActionType.UNASSIGNED_FROM_EDGE); + sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedDashboard.getId(), + EdgeEventType.DASHBOARD, ActionType.UNASSIGNED_FROM_EDGE); return savedDashboard; } catch (Exception e) { diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java index 0f24507cbc..6c7391127d 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java @@ -108,7 +108,8 @@ public class DeviceController extends BaseController { tbClusterService.pushMsgToCore(new DeviceNameOrTypeUpdateMsg(savedDevice.getTenantId(), savedDevice.getId(), savedDevice.getName(), savedDevice.getType()), null); - sendNotificationMsgToEdgeService(savedDevice.getTenantId(), savedDevice.getId(), EdgeEventType.DEVICE, device.getId() == null ? ActionType.ADDED : ActionType.UPDATED); + sendNotificationMsgToEdgeService(savedDevice.getTenantId(), null, savedDevice.getId(), + EdgeEventType.DEVICE, device.getId() == null ? ActionType.ADDED : ActionType.UPDATED); logEntityAction(savedDevice.getId(), savedDevice, savedDevice.getCustomerId(), @@ -141,7 +142,7 @@ public class DeviceController extends BaseController { device.getCustomerId(), ActionType.DELETED, null, strDeviceId); - sendNotificationMsgToEdgeService(getTenantId(), deviceId, EdgeEventType.DEVICE, ActionType.DELETED); + sendNotificationMsgToEdgeService(getTenantId(), null, deviceId, EdgeEventType.DEVICE, ActionType.DELETED); deviceStateService.onDeviceDeleted(device); } catch (Exception e) { @@ -266,7 +267,7 @@ public class DeviceController extends BaseController { tbClusterService.pushMsgToCore(new DeviceCredentialsUpdateNotificationMsg(getCurrentUser().getTenantId(), deviceCredentials.getDeviceId()), null); - sendNotificationMsgToEdgeService(getTenantId(), device.getId(), EdgeEventType.DEVICE, ActionType.CREDENTIALS_UPDATED); + sendNotificationMsgToEdgeService(getTenantId(), null, device.getId(), EdgeEventType.DEVICE, ActionType.CREDENTIALS_UPDATED); logEntityAction(device.getId(), device, device.getCustomerId(), @@ -517,7 +518,7 @@ public class DeviceController extends BaseController { savedDevice.getCustomerId(), ActionType.ASSIGNED_TO_EDGE, null, strDeviceId, strEdgeId, edge.getName()); - sendNotificationMsgToEdgeService(getTenantId(), savedDevice.getId(), EdgeEventType.DEVICE, ActionType.ASSIGNED_TO_EDGE); + sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedDevice.getId(), EdgeEventType.DEVICE, ActionType.ASSIGNED_TO_EDGE); return savedDevice; } catch (Exception e) { @@ -548,7 +549,7 @@ public class DeviceController extends BaseController { device.getCustomerId(), ActionType.UNASSIGNED_FROM_EDGE, null, strDeviceId, edge.getId().toString(), edge.getName()); - sendNotificationMsgToEdgeService(getTenantId(), savedDevice.getId(), EdgeEventType.DEVICE, ActionType.UNASSIGNED_FROM_EDGE); + sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedDevice.getId(), EdgeEventType.DEVICE, ActionType.UNASSIGNED_FROM_EDGE); return savedDevice; } catch (Exception e) { diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java index 970cd98db3..f2b95963f5 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java @@ -118,7 +118,8 @@ public class EntityViewController extends BaseController { logEntityAction(savedEntityView.getId(), savedEntityView, null, entityView.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); - sendNotificationMsgToEdgeService(getTenantId(), savedEntityView.getId(), EdgeEventType.ENTITY_VIEW, entityView.getId() == null ? ActionType.ADDED : ActionType.UPDATED); + sendNotificationMsgToEdgeService(getTenantId(), null, savedEntityView.getId(), + EdgeEventType.ENTITY_VIEW, entityView.getId() == null ? ActionType.ADDED : ActionType.UPDATED); return savedEntityView; } catch (Exception e) { logEntityAction(emptyId(EntityType.ENTITY_VIEW), entityView, null, @@ -189,7 +190,7 @@ public class EntityViewController extends BaseController { logEntityAction(entityViewId, entityView, entityView.getCustomerId(), ActionType.DELETED, null, strEntityViewId); - sendNotificationMsgToEdgeService(getTenantId(), entityViewId, EdgeEventType.ENTITY_VIEW, ActionType.DELETED); + sendNotificationMsgToEdgeService(getTenantId(), null, entityViewId, EdgeEventType.ENTITY_VIEW, ActionType.DELETED); } catch (Exception e) { logEntityAction(emptyId(EntityType.ENTITY_VIEW), null, @@ -395,7 +396,8 @@ public class EntityViewController extends BaseController { savedEntityView.getCustomerId(), ActionType.ASSIGNED_TO_EDGE, null, strEntityViewId, strEdgeId, edge.getName()); - sendNotificationMsgToEdgeService(getTenantId(), savedEntityView.getId(), EdgeEventType.ENTITY_VIEW, ActionType.ASSIGNED_TO_EDGE); + sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedEntityView.getId(), + EdgeEventType.ENTITY_VIEW, ActionType.ASSIGNED_TO_EDGE); return savedEntityView; } catch (Exception e) { @@ -425,7 +427,8 @@ public class EntityViewController extends BaseController { entityView.getCustomerId(), ActionType.UNASSIGNED_FROM_EDGE, null, strEntityViewId, edge.getId().toString(), edge.getName()); - sendNotificationMsgToEdgeService(getTenantId(), savedEntityView.getId(), EdgeEventType.ENTITY_VIEW, ActionType.UNASSIGNED_FROM_EDGE); + sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedEntityView.getId(), + EdgeEventType.ENTITY_VIEW, ActionType.UNASSIGNED_FROM_EDGE); return savedEntityView; } catch (Exception e) { diff --git a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java index ce4a96c2c9..463934b4ab 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java +++ b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java @@ -145,7 +145,7 @@ public class RuleChainController extends BaseController { created ? ActionType.ADDED : ActionType.UPDATED, null); if (RuleChainType.EDGE.equals(savedRuleChain.getType())) { - sendNotificationMsgToEdgeService(savedRuleChain.getTenantId(), + sendNotificationMsgToEdgeService(savedRuleChain.getTenantId(), null, savedRuleChain.getId(), EdgeEventType.RULE_CHAIN, savedRuleChain.getId() == null ? ActionType.ADDED : ActionType.UPDATED); } @@ -226,6 +226,7 @@ public class RuleChainController extends BaseController { if (RuleChainType.EDGE.equals(ruleChain.getType())) { sendNotificationMsgToEdgeService(ruleChain.getTenantId(), + null, ruleChain.getId(), EdgeEventType.RULE_CHAIN, ActionType.UPDATED); } @@ -292,9 +293,8 @@ public class RuleChainController extends BaseController { ActionType.DELETED, null, strRuleChainId); if (RuleChainType.EDGE.equals(ruleChain.getType())) { - sendNotificationMsgToEdgeService(ruleChain.getTenantId(), - ruleChain.getId(), EdgeEventType.RULE_CHAIN, - ActionType.DELETED); + sendNotificationMsgToEdgeService(ruleChain.getTenantId(), null, + ruleChain.getId(), EdgeEventType.RULE_CHAIN, ActionType.DELETED); } } catch (Exception e) { @@ -426,7 +426,7 @@ public class RuleChainController extends BaseController { null, ActionType.ASSIGNED_TO_EDGE, null, strRuleChainId, strEdgeId, edge.getName()); - sendNotificationMsgToEdgeService(getTenantId(), savedRuleChain.getId(), + sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedRuleChain.getId(), EdgeEventType.RULE_CHAIN, ActionType.ASSIGNED_TO_EDGE); return savedRuleChain; @@ -459,7 +459,7 @@ public class RuleChainController extends BaseController { null, ActionType.UNASSIGNED_FROM_EDGE, null, strRuleChainId, edge.getId().toString(), edge.getName()); - sendNotificationMsgToEdgeService(getTenantId(), savedRuleChain.getId(), + sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedRuleChain.getId(), EdgeEventType.RULE_CHAIN, ActionType.UNASSIGNED_FROM_EDGE); return savedRuleChain; diff --git a/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java b/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java index 79d0569e2e..954cef733d 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java @@ -36,11 +36,12 @@ import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.IdBased; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.TextPageData; +import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.page.TimePageData; import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.RelationTypeGroup; -import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.dao.alarm.AlarmService; @@ -141,9 +142,8 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { @Override public void pushNotificationToEdge(TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg, TbCallback callback) { try { - EdgeEventType edgeEventType = EdgeEventType.valueOf(edgeNotificationMsg.getEdgeEventType()); - ActionType edgeEventAction = ActionType.valueOf(edgeNotificationMsg.getEdgeEventAction()); TenantId tenantId = new TenantId(new UUID(edgeNotificationMsg.getTenantIdMSB(), edgeNotificationMsg.getTenantIdLSB())); + EdgeEventType edgeEventType = EdgeEventType.valueOf(edgeNotificationMsg.getEdgeEventType()); switch (edgeEventType) { // TODO: voba - handle edge updates // case EDGE: @@ -152,34 +152,13 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { case ENTITY_VIEW: case DASHBOARD: case RULE_CHAIN: - EntityId entityId = EntityIdFactory.getByEdgeEventTypeAndUuid(edgeEventType, new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB())); - ListenableFuture> edgeIdsFuture = findRelatedEdgeIdsEntityId(tenantId, entityId); - Futures.transform(edgeIdsFuture, edgeIds -> { - if (edgeIds != null && !edgeIds.isEmpty()) { - for (EdgeId edgeId : edgeIds) { - try { - saveEdgeEvent(tenantId, edgeId, edgeEventType, edgeEventAction, entityId, null); - if (edgeEventType.equals(EdgeEventType.RULE_CHAIN) && - (ActionType.UPDATED.equals(edgeEventAction) || ActionType.ADDED.equals(edgeEventAction))) { - RuleChainMetaData ruleChainMetaData = ruleChainService.loadRuleChainMetaData(tenantId, new RuleChainId(entityId.getId())); - saveEdgeEvent(tenantId, edgeId, EdgeEventType.RULE_CHAIN_METADATA, edgeEventAction, ruleChainMetaData.getRuleChainId(), null); - } - } catch (Exception e) { - log.error("[{}] Failed to push event to edge, edgeId [{}], edgeEventType [{}], edgeEventAction [{}], entityId [{}]", - tenantId, edgeId, edgeEventType, edgeEventAction, entityId, e); - } - } - } - return null; - }, dbCallbackExecutorService); + processEntities(tenantId, edgeNotificationMsg); break; case ALARM: - EntityId alarmId = EntityIdFactory.getByEdgeEventTypeAndUuid(edgeEventType, new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB())); - processAlarm(tenantId, edgeEventAction, alarmId); + processAlarm(tenantId, edgeNotificationMsg); break; case RELATION: - EntityRelation entityRelation = mapper.convertValue(edgeNotificationMsg.getEntityBody(), EntityRelation.class); - processRelation(tenantId, edgeEventAction, entityRelation); + processRelation(tenantId, edgeNotificationMsg); break; default: log.debug("Edge event type [{}] is not designed to be pushed to edge", edgeEventType); @@ -192,17 +171,69 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { } } - private void processAlarm(TenantId tenantId, ActionType edgeActionType, EntityId alarmId) { - ListenableFuture alarmFuture = alarmService.findAlarmByIdAsync(tenantId, new AlarmId(alarmId.getId())); + private void processEntities(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) { + ActionType edgeEventActionType = ActionType.valueOf(edgeNotificationMsg.getEdgeEventAction()); + EdgeEventType edgeEventType = EdgeEventType.valueOf(edgeNotificationMsg.getEdgeEventType()); + EntityId entityId = EntityIdFactory.getByEdgeEventTypeAndUuid(edgeEventType, new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB())); + switch (edgeEventActionType) { + // TODO: voba - ADDED is not required for CE version ? + // case ADDED: + case UPDATED: + ListenableFuture> edgeIdsFuture = findRelatedEdgeIdsByEntityId(tenantId, entityId); + Futures.transform(edgeIdsFuture, edgeIds -> { + if (edgeIds != null && !edgeIds.isEmpty()) { + for (EdgeId edgeId : edgeIds) { + try { + saveEdgeEvent(tenantId, edgeId, edgeEventType, edgeEventActionType, entityId, null); + if (edgeEventType.equals(EdgeEventType.RULE_CHAIN)) { + RuleChainMetaData ruleChainMetaData = ruleChainService.loadRuleChainMetaData(tenantId, new RuleChainId(entityId.getId())); + saveEdgeEvent(tenantId, edgeId, EdgeEventType.RULE_CHAIN_METADATA, edgeEventActionType, ruleChainMetaData.getRuleChainId(), null); + } + } catch (Exception e) { + log.error("[{}] Failed to push event to edge, edgeId [{}], edgeEventType [{}], edgeEventActionType [{}], entityId [{}]", + tenantId, edgeId, edgeEventType, edgeEventActionType, entityId, e); + } + } + } + return null; + }, dbCallbackExecutorService); + break; + case DELETED: + TextPageData edgesByTenantId = edgeService.findEdgesByTenantId(tenantId, new TextPageLink(Integer.MAX_VALUE)); + if (edgesByTenantId != null && edgesByTenantId.getData() != null && !edgesByTenantId.getData().isEmpty()) { + for (Edge edge : edgesByTenantId.getData()) { + saveEdgeEvent(tenantId, edge.getId(), edgeEventType, edgeEventActionType, entityId, null); + } + } + break; + case ASSIGNED_TO_EDGE: + case UNASSIGNED_FROM_EDGE: + EdgeId edgeId = new EdgeId(new UUID(edgeNotificationMsg.getEdgeIdMSB(), edgeNotificationMsg.getEdgeIdLSB())); + saveEdgeEvent(tenantId, edgeId, edgeEventType, edgeEventActionType, entityId, null); + break; + case RELATIONS_DELETED: + // TODO: voba - add support for relations deleted + break; + } + } + + private void processAlarm(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) { + AlarmId alarmId = new AlarmId(new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB())); + ListenableFuture alarmFuture = alarmService.findAlarmByIdAsync(tenantId, alarmId); Futures.transform(alarmFuture, alarm -> { if (alarm != null) { EdgeEventType edgeEventType = getEdgeQueueTypeByEntityType(alarm.getOriginator().getEntityType()); if (edgeEventType != null) { - ListenableFuture> relatedEdgeIdsEntityIdFuture = findRelatedEdgeIdsEntityId(tenantId, alarm.getOriginator()); - Futures.transform(relatedEdgeIdsEntityIdFuture, relatedEdgeIdsEntityId -> { - if (relatedEdgeIdsEntityId != null) { - for (EdgeId edgeId : relatedEdgeIdsEntityId) { - saveEdgeEvent(tenantId, edgeId, EdgeEventType.ALARM, edgeActionType, alarmId, null); + ListenableFuture> relatedEdgeIdsByEntityIdFuture = findRelatedEdgeIdsByEntityId(tenantId, alarm.getOriginator()); + Futures.transform(relatedEdgeIdsByEntityIdFuture, relatedEdgeIdsByEntityId -> { + if (relatedEdgeIdsByEntityId != null) { + for (EdgeId edgeId : relatedEdgeIdsByEntityId) { + saveEdgeEvent(tenantId, + edgeId, + EdgeEventType.ALARM, + ActionType.valueOf(edgeNotificationMsg.getEdgeEventAction()), + alarmId, + null); } } return null; @@ -213,10 +244,11 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { }, dbCallbackExecutorService); } - private void processRelation(TenantId tenantId, ActionType edgeActionType, EntityRelation entityRelation) { + private void processRelation(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) { + EntityRelation entityRelation = mapper.convertValue(edgeNotificationMsg.getEntityBody(), EntityRelation.class); List>> futures = new ArrayList<>(); - futures.add(findRelatedEdgeIdsEntityId(tenantId, entityRelation.getTo())); - futures.add(findRelatedEdgeIdsEntityId(tenantId, entityRelation.getFrom())); + futures.add(findRelatedEdgeIdsByEntityId(tenantId, entityRelation.getTo())); + futures.add(findRelatedEdgeIdsByEntityId(tenantId, entityRelation.getFrom())); ListenableFuture>> combinedFuture = Futures.allAsList(futures); Futures.transform(combinedFuture, listOfListsEdgeIds -> { Set uniqueEdgeIds = new HashSet<>(); @@ -229,14 +261,19 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { } if (!uniqueEdgeIds.isEmpty()) { for (EdgeId edgeId : uniqueEdgeIds) { - saveEdgeEvent(tenantId, edgeId, EdgeEventType.RELATION, edgeActionType, null, mapper.valueToTree(entityRelation)); + saveEdgeEvent(tenantId, + edgeId, + EdgeEventType.RELATION, + ActionType.valueOf(edgeNotificationMsg.getEdgeEventAction()), + null, + mapper.valueToTree(entityRelation)); } } return null; }, dbCallbackExecutorService); } - private ListenableFuture> findRelatedEdgeIdsEntityId(TenantId tenantId, EntityId entityId) { + private ListenableFuture> findRelatedEdgeIdsByEntityId(TenantId tenantId, EntityId entityId) { switch (entityId.getEntityType()) { case DEVICE: case ASSET: 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 a3d9bb7593..341142e34d 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 @@ -360,122 +360,193 @@ public final class EdgeGrpcSession implements Closeable { private void processDeviceCRUD(EdgeEvent edgeEvent, UpdateMsgType msgType) { DeviceId deviceId = new DeviceId(edgeEvent.getEntityId()); - ListenableFuture deviceFuture = ctx.getDeviceService().findDeviceByIdAsync(edgeEvent.getTenantId(), deviceId); - Futures.addCallback(deviceFuture, - new FutureCallback() { - @Override - public void onSuccess(@Nullable Device device) { - if (device != null) { - EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() - .setDeviceUpdateMsg(ctx.getDeviceUpdateMsgConstructor().constructDeviceUpdatedMsg(msgType, device)) - .build(); - outputStream.onNext(ResponseMsg.newBuilder() - .setEntityUpdateMsg(entityUpdateMsg) - .build()); - } - } + switch (msgType) { + case ENTITY_CREATED_RPC_MESSAGE: + case ENTITY_UPDATED_RPC_MESSAGE: + case DEVICE_CONFLICT_RPC_MESSAGE: + ListenableFuture deviceFuture = ctx.getDeviceService().findDeviceByIdAsync(edgeEvent.getTenantId(), deviceId); + Futures.addCallback(deviceFuture, + new FutureCallback() { + @Override + public void onSuccess(@Nullable Device device) { + if (device != null) { + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setDeviceUpdateMsg(ctx.getDeviceUpdateMsgConstructor().constructDeviceUpdatedMsg(msgType, device)) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + } + } + + @Override + public void onFailure(Throwable t) { + log.warn("Can't processDeviceCRUD, edgeEvent [{}]", edgeEvent, t); + } + }, ctx.getDbCallbackExecutor()); + break; + case ENTITY_DELETED_RPC_MESSAGE: + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setDeviceUpdateMsg(ctx.getDeviceUpdateMsgConstructor().constructDeviceDeleteMsg(deviceId)) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + } + - @Override - public void onFailure(Throwable t) { - log.warn("Can't processDeviceCRUD, edgeEvent [{}]", edgeEvent, t); - } - }, ctx.getDbCallbackExecutor()); } private void processAssetCRUD(EdgeEvent edgeEvent, UpdateMsgType msgType) { AssetId assetId = new AssetId(edgeEvent.getEntityId()); - ListenableFuture assetFuture = ctx.getAssetService().findAssetByIdAsync(edgeEvent.getTenantId(), assetId); - Futures.addCallback(assetFuture, - new FutureCallback() { - @Override - public void onSuccess(@Nullable Asset asset) { - if (asset != null) { - EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() - .setAssetUpdateMsg(ctx.getAssetUpdateMsgConstructor().constructAssetUpdatedMsg(msgType, asset)) - .build(); - outputStream.onNext(ResponseMsg.newBuilder() - .setEntityUpdateMsg(entityUpdateMsg) - .build()); - } - } + switch (msgType) { + case ENTITY_CREATED_RPC_MESSAGE: + case ENTITY_UPDATED_RPC_MESSAGE: + case DEVICE_CONFLICT_RPC_MESSAGE: + ListenableFuture assetFuture = ctx.getAssetService().findAssetByIdAsync(edgeEvent.getTenantId(), assetId); + Futures.addCallback(assetFuture, + new FutureCallback() { + @Override + public void onSuccess(@Nullable Asset asset) { + if (asset != null) { + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setAssetUpdateMsg(ctx.getAssetUpdateMsgConstructor().constructAssetUpdatedMsg(msgType, asset)) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + } + } - @Override - public void onFailure(Throwable t) { - log.warn("Can't processAssetCRUD, edgeEvent [{}]", edgeEvent, t); - } - }, ctx.getDbCallbackExecutor()); + @Override + public void onFailure(Throwable t) { + log.warn("Can't processAssetCRUD, edgeEvent [{}]", edgeEvent, t); + } + }, ctx.getDbCallbackExecutor()); + break; + case ENTITY_DELETED_RPC_MESSAGE: + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setAssetUpdateMsg(ctx.getAssetUpdateMsgConstructor().constructAssetDeleteMsg(assetId)) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + break; + } } private void processEntityViewCRUD(EdgeEvent edgeEvent, UpdateMsgType msgType) { EntityViewId entityViewId = new EntityViewId(edgeEvent.getEntityId()); - ListenableFuture entityViewFuture = ctx.getEntityViewService().findEntityViewByIdAsync(edgeEvent.getTenantId(), entityViewId); - Futures.addCallback(entityViewFuture, - new FutureCallback() { - @Override - public void onSuccess(@Nullable EntityView entityView) { - if (entityView != null) { - EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() - .setEntityViewUpdateMsg(ctx.getEntityViewUpdateMsgConstructor().constructEntityViewUpdatedMsg(msgType, entityView)) - .build(); - outputStream.onNext(ResponseMsg.newBuilder() - .setEntityUpdateMsg(entityUpdateMsg) - .build()); - } - } + switch (msgType) { + case ENTITY_CREATED_RPC_MESSAGE: + case ENTITY_UPDATED_RPC_MESSAGE: + case DEVICE_CONFLICT_RPC_MESSAGE: + ListenableFuture entityViewFuture = ctx.getEntityViewService().findEntityViewByIdAsync(edgeEvent.getTenantId(), entityViewId); + Futures.addCallback(entityViewFuture, + new FutureCallback() { + @Override + public void onSuccess(@Nullable EntityView entityView) { + if (entityView != null) { + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setEntityViewUpdateMsg(ctx.getEntityViewUpdateMsgConstructor().constructEntityViewUpdatedMsg(msgType, entityView)) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + } + } - @Override - public void onFailure(Throwable t) { - log.warn("Can't processEntityViewCRUD, edgeEvent [{}]", edgeEvent, t); - } - }, ctx.getDbCallbackExecutor()); + @Override + public void onFailure(Throwable t) { + log.warn("Can't processEntityViewCRUD, edgeEvent [{}]", edgeEvent, t); + } + }, ctx.getDbCallbackExecutor()); + break; + case ENTITY_DELETED_RPC_MESSAGE: + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setEntityViewUpdateMsg(ctx.getEntityViewUpdateMsgConstructor().constructEntityViewDeleteMsg(entityViewId)) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + break; + } } private void processDashboardCRUD(EdgeEvent edgeEvent, UpdateMsgType msgType) { DashboardId dashboardId = new DashboardId(edgeEvent.getEntityId()); - ListenableFuture dashboardFuture = ctx.getDashboardService().findDashboardByIdAsync(edgeEvent.getTenantId(), dashboardId); - Futures.addCallback(dashboardFuture, - new FutureCallback() { - @Override - public void onSuccess(@Nullable Dashboard dashboard) { - if (dashboard != null) { - EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() - .setDashboardUpdateMsg(ctx.getDashboardUpdateMsgConstructor().constructDashboardUpdatedMsg(msgType, dashboard)) - .build(); - outputStream.onNext(ResponseMsg.newBuilder() - .setEntityUpdateMsg(entityUpdateMsg) - .build()); - } - } + switch (msgType) { + case ENTITY_CREATED_RPC_MESSAGE: + case ENTITY_UPDATED_RPC_MESSAGE: + case DEVICE_CONFLICT_RPC_MESSAGE: + ListenableFuture dashboardFuture = ctx.getDashboardService().findDashboardByIdAsync(edgeEvent.getTenantId(), dashboardId); + Futures.addCallback(dashboardFuture, + new FutureCallback() { + @Override + public void onSuccess(@Nullable Dashboard dashboard) { + if (dashboard != null) { + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setDashboardUpdateMsg(ctx.getDashboardUpdateMsgConstructor().constructDashboardUpdatedMsg(msgType, dashboard)) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + } + } - @Override - public void onFailure(Throwable t) { - log.warn("Can't processDashboardCRUD, edgeEvent [{}]", edgeEvent, t); - } - }, ctx.getDbCallbackExecutor()); + @Override + public void onFailure(Throwable t) { + log.warn("Can't processDashboardCRUD, edgeEvent [{}]", edgeEvent, t); + } + }, ctx.getDbCallbackExecutor()); + break; + case ENTITY_DELETED_RPC_MESSAGE: + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setDashboardUpdateMsg(ctx.getDashboardUpdateMsgConstructor().constructDashboardDeleteMsg(dashboardId)) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + break; + } } private void processRuleChainCRUD(EdgeEvent edgeEvent, UpdateMsgType msgType) { RuleChainId ruleChainId = new RuleChainId(edgeEvent.getEntityId()); - ListenableFuture ruleChainFuture = ctx.getRuleChainService().findRuleChainByIdAsync(edgeEvent.getTenantId(), ruleChainId); - Futures.addCallback(ruleChainFuture, - new FutureCallback() { - @Override - public void onSuccess(@Nullable RuleChain ruleChain) { - if (ruleChain != null) { - EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() - .setRuleChainUpdateMsg(ctx.getRuleChainUpdateMsgConstructor().constructRuleChainUpdatedMsg(edge.getRootRuleChainId(), msgType, ruleChain)) - .build(); - outputStream.onNext(ResponseMsg.newBuilder() - .setEntityUpdateMsg(entityUpdateMsg) - .build()); - } - } + switch (msgType) { + case ENTITY_CREATED_RPC_MESSAGE: + case ENTITY_UPDATED_RPC_MESSAGE: + case DEVICE_CONFLICT_RPC_MESSAGE: + ListenableFuture ruleChainFuture = ctx.getRuleChainService().findRuleChainByIdAsync(edgeEvent.getTenantId(), ruleChainId); + Futures.addCallback(ruleChainFuture, + new FutureCallback() { + @Override + public void onSuccess(@Nullable RuleChain ruleChain) { + if (ruleChain != null) { + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setRuleChainUpdateMsg(ctx.getRuleChainUpdateMsgConstructor().constructRuleChainUpdatedMsg(edge.getRootRuleChainId(), msgType, ruleChain)) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + } + } - @Override - public void onFailure(Throwable t) { - log.warn("Can't processRuleChainCRUD, edgeEvent [{}]", edgeEvent, t); - } - }, ctx.getDbCallbackExecutor()); + @Override + public void onFailure(Throwable t) { + log.warn("Can't processRuleChainCRUD, edgeEvent [{}]", edgeEvent, t); + } + }, ctx.getDbCallbackExecutor()); + break; + case ENTITY_DELETED_RPC_MESSAGE: + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setRuleChainUpdateMsg(ctx.getRuleChainUpdateMsgConstructor().constructRuleChainDeleteMsg(ruleChainId)) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + break; + } } private void processRuleChainMetadataCRUD(EdgeEvent edgeEvent, UpdateMsgType msgType) { @@ -509,26 +580,40 @@ public final class EdgeGrpcSession implements Closeable { private void processUserCRUD(EdgeEvent edgeEvent, UpdateMsgType msgType) { UserId userId = new UserId(edgeEvent.getEntityId()); - ListenableFuture userFuture = ctx.getUserService().findUserByIdAsync(edgeEvent.getTenantId(), userId); - Futures.addCallback(userFuture, - new FutureCallback() { - @Override - public void onSuccess(@Nullable User user) { - if (user != null) { - EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() - .setUserUpdateMsg(ctx.getUserUpdateMsgConstructor().constructUserUpdatedMsg(msgType, user)) - .build(); - outputStream.onNext(ResponseMsg.newBuilder() - .setEntityUpdateMsg(entityUpdateMsg) - .build()); - } - } + switch (msgType) { + case ENTITY_CREATED_RPC_MESSAGE: + case ENTITY_UPDATED_RPC_MESSAGE: + case DEVICE_CONFLICT_RPC_MESSAGE: + ListenableFuture userFuture = ctx.getUserService().findUserByIdAsync(edgeEvent.getTenantId(), userId); + Futures.addCallback(userFuture, + new FutureCallback() { + @Override + public void onSuccess(@Nullable User user) { + if (user != null) { + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setUserUpdateMsg(ctx.getUserUpdateMsgConstructor().constructUserUpdatedMsg(msgType, user)) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + } + } - @Override - public void onFailure(Throwable t) { - log.warn("Can't processUserCRUD, edgeEvent [{}]", edgeEvent, t); - } - }, ctx.getDbCallbackExecutor()); + @Override + public void onFailure(Throwable t) { + log.warn("Can't processUserCRUD, edgeEvent [{}]", edgeEvent, t); + } + }, ctx.getDbCallbackExecutor()); + break; + case ENTITY_DELETED_RPC_MESSAGE: + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setUserUpdateMsg(ctx.getUserUpdateMsgConstructor().constructUserDeleteMsg(userId)) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + break; + } } private void processRelationCRUD(EdgeEvent edgeEvent, UpdateMsgType msgType) { @@ -567,9 +652,9 @@ public final class EdgeGrpcSession implements Closeable { private UpdateMsgType getResponseMsgType(ActionType actionType) { switch (actionType) { - case ADDED: - return UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE; case UPDATED: + return UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE; + case ADDED: case ASSIGNED_TO_EDGE: return ENTITY_CREATED_RPC_MESSAGE; case DELETED: diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetUpdateMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetUpdateMsgConstructor.java index 30af762756..c404fe5c41 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetUpdateMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetUpdateMsgConstructor.java @@ -18,6 +18,7 @@ package org.thingsboard.server.service.edge.rpc.constructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.gen.edge.AssetUpdateMsg; import org.thingsboard.server.gen.edge.UpdateMsgType; @@ -38,4 +39,10 @@ public class AssetUpdateMsgConstructor { 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/DashboardUpdateMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DashboardUpdateMsgConstructor.java index 3826cc25f0..6bd24f123b 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DashboardUpdateMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DashboardUpdateMsgConstructor.java @@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.Dashboard; +import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.util.mapping.JacksonUtil; import org.thingsboard.server.gen.edge.DashboardUpdateMsg; @@ -42,4 +43,11 @@ public class DashboardUpdateMsgConstructor { 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/DeviceUpdateMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceUpdateMsgConstructor.java index 791abc8cf7..8d7fbc4c25 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceUpdateMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceUpdateMsgConstructor.java @@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; 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.dao.device.DeviceCredentialsService; import org.thingsboard.server.gen.edge.DeviceUpdateMsg; @@ -58,4 +59,11 @@ public class DeviceUpdateMsgConstructor { } 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(); + } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityViewUpdateMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityViewUpdateMsgConstructor.java index d52bc02321..75301c7ed9 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityViewUpdateMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityViewUpdateMsgConstructor.java @@ -18,6 +18,7 @@ package org.thingsboard.server.service.edge.rpc.constructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.EntityView; +import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.gen.edge.EdgeEntityType; import org.thingsboard.server.gen.edge.EntityViewUpdateMsg; import org.thingsboard.server.gen.edge.UpdateMsgType; @@ -50,4 +51,10 @@ public class EntityViewUpdateMsgConstructor { return builder.build(); } + 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/RuleChainUpdateMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainUpdateMsgConstructor.java index 204ea18bcd..52e487d973 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainUpdateMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainUpdateMsgConstructor.java @@ -19,7 +19,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.rule.NodeConnectionInfo; import org.thingsboard.server.common.data.rule.RuleChain; @@ -126,7 +125,6 @@ public class RuleChainUpdateMsgConstructor { .build(); } - private RuleNodeProto constructNode(RuleNode node) throws JsonProcessingException { return RuleNodeProto.newBuilder() .setIdMSB(node.getId().getId().getMostSignificantBits()) @@ -139,4 +137,11 @@ public class RuleChainUpdateMsgConstructor { .build(); } + 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/UserUpdateMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/UserUpdateMsgConstructor.java index 06744530dc..b994d8fdc4 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/UserUpdateMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/UserUpdateMsgConstructor.java @@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; 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.dao.user.UserService; import org.thingsboard.server.dao.util.mapping.JacksonUtil; @@ -61,4 +62,11 @@ public class UserUpdateMsgConstructor { } 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(); + } } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java index 60c6b24918..733fabc3f9 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java @@ -70,7 +70,6 @@ public interface EdgeService { ListenableFuture> findEdgeTypesByTenantId(TenantId tenantId); - void assignDefaultRuleChainsToEdge(TenantId tenantId, EdgeId edgeId); ListenableFuture> findEdgesByTenantIdAndRuleChainId(TenantId tenantId, RuleChainId ruleChainId, TimePageLink pageLink); diff --git a/common/queue/src/main/proto/queue.proto b/common/queue/src/main/proto/queue.proto index 54a8148fbe..921a096df0 100644 --- a/common/queue/src/main/proto/queue.proto +++ b/common/queue/src/main/proto/queue.proto @@ -356,14 +356,16 @@ message FromDeviceRPCResponseProto { message EdgeNotificationMsgProto { int64 tenantIdMSB = 1; int64 tenantIdLSB = 2; - string edgeEventType = 3; - string edgeEventAction = 4; - int64 entityIdMSB = 5; - int64 entityIdLSB = 6; - string entityType = 7; - string entityBody = 8; - PostTelemetryMsg postTelemetryMsg = 9; - PostAttributeMsg postAttributesMsg = 10; + int64 edgeIdMSB = 3; + int64 edgeIdLSB = 4; + string edgeEventType = 5; + string edgeEventAction = 6; + int64 entityIdMSB = 7; + int64 entityIdLSB = 8; + string entityType = 9; + string entityBody = 10; + PostTelemetryMsg postTelemetryMsg = 11; + PostAttributeMsg postAttributesMsg = 12; } /** From f5ab5d7a25406899fff3abb88b3c5e352d3be73b Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Fri, 19 Jun 2020 10:05:27 +0300 Subject: [PATCH 52/52] Revert particular methods --- .../actors/ruleChain/DefaultTbContext.java | 22 +--------- .../server/controller/BaseController.java | 18 ++++++++ .../ComponentDescriptorController.java | 43 ++++++++++++++++--- .../AnnotationComponentDiscoveryService.java | 32 ++++++++++++-- .../component/ComponentDiscoveryService.java | 4 ++ .../rule/engine/api/TbContext.java | 4 -- .../engine/action/TbAbstractAlarmNode.java | 8 +--- 7 files changed, 91 insertions(+), 40 deletions(-) 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 c4ccd485ab..b304f2a67e 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 @@ -249,28 +249,8 @@ class DefaultTbContext implements TbContext { } public TbMsg entityCreatedMsg(E entity, I id, RuleNodeId ruleNodeId) { - return entityCRUDMsg(entity, id, ruleNodeId, DataConstants.ENTITY_CREATED); - } - - public TbMsg entityCRUDMsg(E entity, I id, RuleNodeId ruleNodeId, String actionType) { - try { - return TbMsg.newMsg(actionType, id, getActionMetaData(ruleNodeId), mapper.writeValueAsString(mapper.valueToTree(entity))); - } catch (JsonProcessingException | IllegalArgumentException e) { - throw new RuntimeException("Failed to process " + id.getEntityType().name().toLowerCase() + " " + actionType + " msg: " + e); - } - } - - public TbMsg alarmUpdatedMsg(Alarm alarm, RuleNodeId ruleNodeId) { - return entityCRUDMsg(alarm, alarm.getId(), ruleNodeId, DataConstants.ENTITY_UPDATED); - } - - public TbMsg alarmClearedMsg(Alarm alarm, RuleNodeId ruleNodeId) { - return entityCRUDMsg(alarm, alarm.getId(), ruleNodeId, DataConstants.ALARM_CLEAR); - } - - public TbMsg alarmMsg(E entity, I id, RuleNodeId ruleNodeId, String actionType) { try { - return TbMsg.newMsg(actionType, id, getActionMetaData(ruleNodeId), mapper.writeValueAsString(mapper.valueToTree(entity))); + return TbMsg.newMsg(DataConstants.ENTITY_CREATED, id, getActionMetaData(ruleNodeId), mapper.writeValueAsString(mapper.valueToTree(entity))); } catch (JsonProcessingException | IllegalArgumentException e) { throw new RuntimeException("Failed to process " + id.getEntityType().name().toLowerCase() + " created msg: " + e); } 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 f71c98d63d..c7c53f7cc5 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -530,6 +530,24 @@ public abstract class BaseController { } } + ComponentDescriptor checkComponentDescriptorByClazz(String clazz) throws ThingsboardException { + try { + log.debug("[{}] Lookup component descriptor", clazz); + return checkNotNull(componentDescriptorService.getComponent(clazz)); + } catch (Exception e) { + throw handleException(e, false); + } + } + + List checkComponentDescriptorsByType(ComponentType type, RuleChainType ruleChainType) throws ThingsboardException { + try { + log.debug("[{}] Lookup component descriptors", type); + return componentDescriptorService.getComponents(type, ruleChainType); + } catch (Exception e) { + throw handleException(e, false); + } + } + List checkComponentDescriptorsByTypes(Set types, RuleChainType ruleChainType) throws ThingsboardException { try { log.debug("[{}] Lookup component descriptors", types); diff --git a/application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java b/application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java index dc52546ed8..592bcfb94a 100644 --- a/application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java +++ b/application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.controller; +import org.apache.commons.lang3.StringUtils; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; @@ -25,8 +26,8 @@ import org.springframework.web.bind.annotation.RestController; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.plugin.ComponentDescriptor; import org.thingsboard.server.common.data.plugin.ComponentType; -import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.common.data.rule.RuleChainType; +import org.thingsboard.server.queue.util.TbCoreComponent; import java.util.HashSet; import java.util.List; @@ -37,24 +38,56 @@ import java.util.Set; @RequestMapping("/api") public class ComponentDescriptorController extends BaseController { + @PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN')") + @RequestMapping(value = "/component/{componentDescriptorClazz:.+}", method = RequestMethod.GET) + @ResponseBody + public ComponentDescriptor getComponentDescriptorByClazz(@PathVariable("componentDescriptorClazz") String strComponentDescriptorClazz) throws ThingsboardException { + checkParameter("strComponentDescriptorClazz", strComponentDescriptorClazz); + try { + return checkComponentDescriptorByClazz(strComponentDescriptorClazz); + } catch (Exception e) { + throw handleException(e); + } + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN')") + @RequestMapping(value = "/components/{componentType}/{ruleChainType}", method = RequestMethod.GET) + @ResponseBody + public List getComponentDescriptorsByType(@PathVariable(value = "ruleChainType", required = false) String strRuleChainType, + @PathVariable("componentType") String strComponentType) throws ThingsboardException { + checkParameter("componentType", strComponentType); + try { + return checkComponentDescriptorsByType(ComponentType.valueOf(strComponentType), getRuleChainType(strRuleChainType)); + } catch (Exception e) { + throw handleException(e); + } + } @PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN')") @RequestMapping(value = "/components/{ruleChainType}", params = {"componentTypes"}, method = RequestMethod.GET) @ResponseBody - public List getComponentDescriptorsByTypes(@PathVariable("ruleChainType") String strRuleChainType, + public List getComponentDescriptorsByTypes(@PathVariable(value = "ruleChainType", required = false) String strRuleChainType, @RequestParam("componentTypes") String[] strComponentTypes) throws ThingsboardException { checkArrayParameter("componentTypes", strComponentTypes); - checkParameter("ruleChainType", strRuleChainType); try { - RuleChainType ruleChainType = RuleChainType.valueOf(strRuleChainType); Set componentTypes = new HashSet<>(); for (String strComponentType : strComponentTypes) { componentTypes.add(ComponentType.valueOf(strComponentType)); } - return checkComponentDescriptorsByTypes(componentTypes, ruleChainType); + return checkComponentDescriptorsByTypes(componentTypes, getRuleChainType(strRuleChainType)); } catch (Exception e) { throw handleException(e); } } + private RuleChainType getRuleChainType(String strRuleChainType) { + RuleChainType ruleChainType; + if (StringUtils.isEmpty(strRuleChainType)) { + ruleChainType = RuleChainType.CORE; + } else { + ruleChainType = RuleChainType.valueOf(strRuleChainType); + } + return ruleChainType; + } + } diff --git a/application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java b/application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java index 43231768f9..77e2afcd88 100644 --- a/application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java +++ b/application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java @@ -40,7 +40,6 @@ import javax.annotation.PostConstruct; import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -65,7 +64,7 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe private Map components = new HashMap<>(); - private Map> systemComponentsMap = new HashMap<>(); + private Map> coreComponentsMap = new HashMap<>(); private Map> edgeComponentsMap = new HashMap<>(); @@ -117,7 +116,7 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe private void putComponentIntoMaps(ComponentType type, RuleNode ruleNodeAnnotation, ComponentDescriptor component) { if (ruleChainTypeContainsArray(RuleChainType.CORE, ruleNodeAnnotation.ruleChainTypes())) { - systemComponentsMap.computeIfAbsent(type, k -> new ArrayList<>()).add(component); + coreComponentsMap.computeIfAbsent(type, k -> new ArrayList<>()).add(component); } if (ruleChainTypeContainsArray(RuleChainType.EDGE, ruleNodeAnnotation.ruleChainTypes())) { edgeComponentsMap.computeIfAbsent(type, k -> new ArrayList<>()).add(component); @@ -223,10 +222,30 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe log.info("Found following definitions: {}", components.values()); } + @Override + public List getComponents(ComponentType type, RuleChainType ruleChainType) { + if (RuleChainType.CORE.equals(ruleChainType)) { + if (coreComponentsMap.containsKey(type)) { + return Collections.unmodifiableList(coreComponentsMap.get(type)); + } else { + return Collections.emptyList(); + } + } else if (RuleChainType.EDGE.equals(ruleChainType)) { + if (edgeComponentsMap.containsKey(type)) { + return Collections.unmodifiableList(edgeComponentsMap.get(type)); + } else { + return Collections.emptyList(); + } + } else { + log.error("Unsupported rule chain type {}", ruleChainType); + throw new RuntimeException("Unsupported rule chain type " + ruleChainType); + } + } + @Override public List getComponents(Set types, RuleChainType ruleChainType) { if (RuleChainType.CORE.equals(ruleChainType)) { - return getComponents(types, systemComponentsMap); + return getComponents(types, coreComponentsMap); } else if (RuleChainType.EDGE.equals(ruleChainType)) { return getComponents(types, edgeComponentsMap); } else { @@ -235,6 +254,11 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe } } + @Override + public Optional getComponent(String clazz) { + return Optional.ofNullable(components.get(clazz)); + } + private List getComponents(Set types, Map> componentsMap) { List result = new ArrayList<>(); types.stream().filter(componentsMap::containsKey).forEach(type -> { diff --git a/application/src/main/java/org/thingsboard/server/service/component/ComponentDiscoveryService.java b/application/src/main/java/org/thingsboard/server/service/component/ComponentDiscoveryService.java index 01cf130fcb..643c82f04f 100644 --- a/application/src/main/java/org/thingsboard/server/service/component/ComponentDiscoveryService.java +++ b/application/src/main/java/org/thingsboard/server/service/component/ComponentDiscoveryService.java @@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.data.rule.RuleChainType; import java.util.List; +import java.util.Optional; import java.util.Set; /** @@ -29,6 +30,9 @@ public interface ComponentDiscoveryService { void discoverComponents(); + List getComponents(ComponentType type, RuleChainType ruleChainType); + List getComponents(Set types, RuleChainType ruleChainType); + Optional getComponent(String clazz); } 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 766d1b95ac..8de0068561 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 @@ -145,10 +145,6 @@ public interface TbContext { // TODO: Does this changes the message? TbMsg alarmCreatedMsg(Alarm alarm, RuleNodeId ruleNodeId); - TbMsg alarmUpdatedMsg(Alarm alarm, RuleNodeId ruleNodeId); - - TbMsg alarmClearedMsg(Alarm alarm, RuleNodeId ruleNodeId); - /* * * METHODS TO PROCESS THE MESSAGES diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractAlarmNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractAlarmNode.java index 7b3dbcdb38..b3344105bb 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractAlarmNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractAlarmNode.java @@ -65,13 +65,9 @@ public abstract class TbAbstractAlarmNode ctx.tellNext(toAlarmMsg(ctx, alarmResult, msg), "Created"), throwable -> ctx.tellFailure(toAlarmMsg(ctx, alarmResult, msg), throwable)); } else if (alarmResult.isUpdated) { - ctx.enqueue(ctx.alarmUpdatedMsg(alarmResult.alarm, ctx.getSelfId()), - () -> ctx.tellNext(toAlarmMsg(ctx, alarmResult, msg), "Updated"), - throwable -> ctx.tellFailure(toAlarmMsg(ctx, alarmResult, msg), throwable)); + ctx.tellNext(toAlarmMsg(ctx, alarmResult, msg), "Updated"); } else if (alarmResult.isCleared) { - ctx.enqueue(ctx.alarmClearedMsg(alarmResult.alarm, ctx.getSelfId()), - () -> ctx.tellNext(toAlarmMsg(ctx, alarmResult, msg), "Cleared"), - throwable -> ctx.tellFailure(toAlarmMsg(ctx, alarmResult, msg), throwable)); + ctx.tellNext(toAlarmMsg(ctx, alarmResult, msg), "Cleared"); } else { ctx.tellSuccess(msg); }