diff --git a/.gitignore b/.gitignore index da8569baa3..c0628fd2e3 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,4 @@ pom.xml.versionsBackup **/.env .instance_id rebuild-docker.sh +.run/ \ No newline at end of file diff --git a/application/src/main/data/json/demo/rule_chains/root_rule_chain.json b/application/src/main/data/json/demo/rule_chains/root_rule_chain.json index 97ad46c756..067685d775 100644 --- a/application/src/main/data/json/demo/rule_chains/root_rule_chain.json +++ b/application/src/main/data/json/demo/rule_chains/root_rule_chain.json @@ -2,6 +2,7 @@ "ruleChain": { "additionalInfo": null, "name": "Root Rule Chain", + "type": "CORE", "firstRuleNodeId": null, "root": true, "debugMode": false, diff --git a/application/src/main/data/upgrade/2.6.0/schema_update.cql b/application/src/main/data/upgrade/2.6.0/schema_update.cql index 7e1a99f8db..a6744c52f9 100644 --- a/application/src/main/data/upgrade/2.6.0/schema_update.cql +++ b/application/src/main/data/upgrade/2.6.0/schema_update.cql @@ -14,15 +14,54 @@ -- limitations under the License. -- +DROP MATERIALIZED VIEW IF EXISTS thingsboard.rule_chain_by_tenant_and_search_text; + +DROP TABLE IF EXISTS thingsboard.rule_chain; + +CREATE TABLE IF NOT EXISTS thingsboard.rule_chain ( + id uuid, + tenant_id uuid, + name text, + type text, + search_text text, + first_rule_node_id uuid, + root boolean, + debug_mode boolean, + configuration text, + additional_info text, + PRIMARY KEY (id, tenant_id, type) +); + +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.rule_chain_by_tenant_and_search_text AS + SELECT * + from thingsboard.rule_chain + WHERE tenant_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL AND type IS NOT NULL + PRIMARY KEY ( tenant_id, search_text, id, type ) + WITH CLUSTERING ORDER BY ( search_text ASC, id DESC ); + +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.rule_chain_by_tenant_by_type_and_search_text AS + SELECT * + from thingsboard.rule_chain + WHERE tenant_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL AND type IS NOT NULL + PRIMARY KEY ( tenant_id, type, search_text, id ) + WITH CLUSTERING ORDER BY ( type ASC, search_text ASC, id DESC ); + CREATE TABLE IF NOT EXISTS thingsboard.edge ( id timeuuid, tenant_id timeuuid, customer_id timeuuid, + root_rule_chain_id timeuuid, + type text, name text, + label text, search_text text, + routing_key text, + secret text, + edge_license_key text, + cloud_endpoint text, configuration text, additional_info text, - PRIMARY KEY (id, tenant_id) + PRIMARY KEY (id, tenant_id, customer_id, type) ); CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.edge_by_tenant_and_name AS @@ -32,6 +71,13 @@ CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.edge_by_tenant_and_name AS 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_routing_key AS + SELECT * + from thingsboard.edge + WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND routing_key IS NOT NULL AND id IS NOT NULL + PRIMARY KEY ( routing_key, tenant_id, id, customer_id, type) + WITH CLUSTERING ORDER BY ( tenant_id DESC, id DESC, customer_id DESC); + CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.edge_by_tenant_and_search_text AS SELECT * from thingsboard.edge @@ -60,4 +106,22 @@ CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.edge_by_customer_by_type_and_ 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 +CREATE TABLE IF NOT EXISTS thingsboard.edge_event ( + id timeuuid, + tenant_id timeuuid, + edge_id timeuuid, + edge_event_type text, + edge_event_action text, + edge_event_uid text, + entity_id timeuuid, + body text, + PRIMARY KEY ((tenant_id, edge_id), edge_event_type, edge_event_uid) +); + +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.edge_event_by_id AS + SELECT * + FROM thingsboard.edge_event + WHERE tenant_id IS NOT NULL AND edge_id IS NOT NULL AND edge_event_type IS NOT NULL + AND id IS NOT NULL AND edge_event_uid IS NOT NULL + PRIMARY KEY ((tenant_id, edge_id), id, edge_event_type, edge_event_uid) + WITH CLUSTERING ORDER BY (id ASC); \ No newline at end of file diff --git a/application/src/main/data/upgrade/2.6.0/schema_update.sql b/application/src/main/data/upgrade/2.6.0/schema_update.sql index 5d2c368d1d..b3bfd07620 100644 --- a/application/src/main/data/upgrade/2.6.0/schema_update.sql +++ b/application/src/main/data/upgrade/2.6.0/schema_update.sql @@ -25,6 +25,22 @@ CREATE TABLE IF NOT EXISTS edge ( label varchar(255), routing_key varchar(255), secret varchar(255), + edge_license_key varchar(30), + cloud_endpoint 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 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), + edge_event_uid varchar(255), + entity_id varchar(31), + edge_event_action varchar(255), + body varchar(10000000), + tenant_id varchar(31), + ts bigint NOT NULL ); diff --git a/application/src/main/data/upgrade/2.6.0/schema_update_ttl.sql b/application/src/main/data/upgrade/2.6.0/schema_update_ttl.sql new file mode 100644 index 0000000000..7c1075059f --- /dev/null +++ b/application/src/main/data/upgrade/2.6.0/schema_update_ttl.sql @@ -0,0 +1,32 @@ +-- +-- 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. +-- + +CREATE OR REPLACE PROCEDURE cleanup_edge_events_by_ttl(IN ttl bigint, INOUT deleted bigint) + LANGUAGE plpgsql AS +$$ +DECLARE + ttl_ts bigint; + ttl_deleted_count bigint DEFAULT 0; +BEGIN + IF ttl > 0 THEN + ttl_ts := (EXTRACT(EPOCH FROM current_timestamp) * 1000 - ttl::bigint * 1000)::bigint; + EXECUTE format( + 'WITH deleted AS (DELETE FROM edge_event WHERE ts < %L::bigint RETURNING *) SELECT count(*) FROM deleted', ttl_ts) into ttl_deleted_count; + END IF; + RAISE NOTICE 'Edge events removed by ttl: %', ttl_deleted_count; + deleted := ttl_deleted_count; +END +$$; 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 3bed7e0417..50e837769a 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java @@ -32,7 +32,6 @@ import org.springframework.data.redis.core.RedisTemplate; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import org.thingsboard.rule.engine.api.MailService; -import org.thingsboard.rule.engine.api.RuleEngineDeviceProfileCache; import org.thingsboard.server.actors.service.ActorService; import org.thingsboard.server.actors.tenant.DebugTbRateLimits; import org.thingsboard.server.common.data.DataConstants; @@ -45,6 +44,7 @@ import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.common.msg.tools.TbRateLimits; +import org.thingsboard.server.common.transport.util.DataDecodingEncodingService; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.audit.AuditLogService; @@ -68,7 +68,7 @@ import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.service.component.ComponentDiscoveryService; -import org.thingsboard.server.common.transport.util.DataDecodingEncodingService; +import org.thingsboard.server.service.edge.rpc.EdgeRpcService; import org.thingsboard.server.service.executors.DbCallbackExecutorService; import org.thingsboard.server.service.executors.ExternalCallExecutorService; import org.thingsboard.server.service.executors.SharedEventLoopGroupService; @@ -196,10 +196,6 @@ public class ActorSystemContext { @Getter private EntityViewService entityViewService; - @Autowired - @Getter - private EdgeService edgeService; - @Autowired @Getter private TelemetrySubscriptionService tsSubService; @@ -274,9 +270,16 @@ public class ActorSystemContext { private TbCoreDeviceRpcService tbCoreDeviceRpcService; @Lazy - @Autowired - @Getter - private EdgeEventService edgeEventService; + @Autowired(required = false) + @Getter private EdgeService edgeService; + + @Lazy + @Autowired(required = false) + @Getter private EdgeEventService edgeEventService; + + @Lazy + @Autowired(required = false) + @Getter private EdgeRpcService edgeRpcService; @Value("${actors.session.max_concurrent_sessions_per_device:1}") @Getter 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 2c663f9a1a..abe07f0e51 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 @@ -32,10 +32,13 @@ import org.thingsboard.server.actors.service.DefaultActorService; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.TenantProfile; +import org.thingsboard.server.common.data.edge.Edge; 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.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.msg.MsgType; @@ -48,6 +51,7 @@ import org.thingsboard.server.common.msg.queue.PartitionChangeMsg; import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg; import org.thingsboard.server.common.msg.queue.RuleEngineException; import org.thingsboard.server.common.msg.queue.ServiceType; +import org.thingsboard.server.service.edge.rpc.EdgeRpcService; import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper; import java.util.List; @@ -211,7 +215,18 @@ public class TenantActor extends RuleChainManagerActor { } private void onComponentLifecycleMsg(ComponentLifecycleMsg msg) { - if (isRuleEngineForCurrentTenant) { + if (msg.getEntityId().getEntityType() == EntityType.EDGE) { + EdgeId edgeId = new EdgeId(msg.getEntityId().getId()); + EdgeRpcService edgeRpcService = systemContext.getEdgeRpcService(); + if (msg.getEvent() == ComponentLifecycleEvent.DELETED) { + edgeRpcService.deleteEdge(edgeId); + } else { + Edge edge = systemContext.getEdgeService().findEdgeById(tenantId, edgeId); + if (msg.getEvent() == ComponentLifecycleEvent.UPDATED) { + edgeRpcService.updateEdge(edge); + } + } + } else if (isRuleEngineForCurrentTenant) { TbActorRef target = getEntityActorRef(msg.getEntityId()); if (target != null) { if (msg.getEntityId().getEntityType() == EntityType.RULE_CHAIN) { diff --git a/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java b/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java index a96843ce2c..113861e31d 100644 --- a/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java +++ b/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java @@ -69,7 +69,7 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt public static final String FORM_BASED_LOGIN_ENTRY_POINT = "/api/auth/login"; public static final String PUBLIC_LOGIN_ENTRY_POINT = "/api/auth/login/public"; public static final String TOKEN_REFRESH_ENTRY_POINT = "/api/auth/token"; - protected static final String[] NON_TOKEN_BASED_AUTH_ENTRY_POINTS = new String[] {"/index.html", "/assets/**", "/static/**", "/api/noauth/**", "/webjars/**"}; + protected static final String[] NON_TOKEN_BASED_AUTH_ENTRY_POINTS = new String[] {"/index.html", "/assets/**", "/static/**", "/api/noauth/**", "/webjars/**", "/api/license/**"}; public static final String TOKEN_BASED_AUTH_ENTRY_POINT = "/api/**"; public static final String WS_TOKEN_BASED_AUTH_ENTRY_POINT = "/api/ws/**"; 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 a8990df089..d3d079900a 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java @@ -94,7 +94,8 @@ public class AlarmController extends BaseController { getCurrentUser().getCustomerId(), alarm.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); - sendNotificationMsgToEdgeService(getTenantId(), savedAlarm.getId(), alarm.getId() == null ? ActionType.ADDED : ActionType.UPDATED); + sendNotificationMsgToEdgeService(getTenantId(), savedAlarm.getId(), + alarm.getId() == null ? ActionType.ADDED : ActionType.UPDATED); return savedAlarm; } catch (Exception 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 181eef6e64..24b52b1ec6 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AssetController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AssetController.java @@ -97,17 +97,18 @@ public class AssetController extends BaseController { try { asset.setTenantId(getCurrentUser().getTenantId()); - checkEntity(asset.getId(), asset, Resource.ASSET); + checkEntity(asset.getId(), asset, Resource.ASSET); Asset savedAsset = checkNotNull(assetService.saveAsset(asset)); - sendNotificationMsgToEdgeService(savedAsset.getTenantId(), null, - 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); + if (asset.getId() != null) { + sendNotificationMsgToEdgeService(savedAsset.getTenantId(), savedAsset.getId(), ActionType.UPDATED); + } + return savedAsset; } catch (Exception e) { logEntityAction(emptyId(EntityType.ASSET), asset, @@ -130,7 +131,7 @@ public class AssetController extends BaseController { asset.getCustomerId(), ActionType.DELETED, null, strAssetId); - sendNotificationMsgToEdgeService(getTenantId(), null, assetId, EdgeEventType.ASSET, ActionType.DELETED); + sendNotificationMsgToEdgeService(getTenantId(), assetId, ActionType.DELETED); } catch (Exception e) { logEntityAction(emptyId(EntityType.ASSET), null, @@ -160,6 +161,9 @@ public class AssetController extends BaseController { savedAsset.getCustomerId(), ActionType.ASSIGNED_TO_CUSTOMER, null, strAssetId, strCustomerId, customer.getName()); + sendNotificationMsgToEdgeService(savedAsset.getTenantId(), savedAsset.getId(), + customerId, ActionType.ASSIGNED_TO_CUSTOMER); + return savedAsset; } catch (Exception e) { @@ -191,6 +195,9 @@ public class AssetController extends BaseController { asset.getCustomerId(), ActionType.UNASSIGNED_FROM_CUSTOMER, null, strAssetId, customer.getId().toString(), customer.getName()); + sendNotificationMsgToEdgeService(savedAsset.getTenantId(), savedAsset.getId(), + customer.getId(), ActionType.UNASSIGNED_FROM_CUSTOMER); + return savedAsset; } catch (Exception e) { @@ -425,7 +432,7 @@ public class AssetController extends BaseController { savedAsset.getCustomerId(), ActionType.ASSIGNED_TO_EDGE, null, strAssetId, strEdgeId, edge.getName()); - sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedAsset.getId(), EdgeEventType.ASSET, ActionType.ASSIGNED_TO_EDGE); + sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedAsset.getId(), ActionType.ASSIGNED_TO_EDGE); return savedAsset; } catch (Exception e) { @@ -458,8 +465,7 @@ public class AssetController extends BaseController { asset.getCustomerId(), ActionType.UNASSIGNED_FROM_EDGE, null, strAssetId, edge.getId().toString(), edge.getName()); - sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedAsset.getId(), - EdgeEventType.ASSET, ActionType.UNASSIGNED_FROM_EDGE); + sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedAsset.getId(), ActionType.UNASSIGNED_FROM_EDGE); return savedAsset; } catch (Exception e) { diff --git a/application/src/main/java/org/thingsboard/server/controller/AuthController.java b/application/src/main/java/org/thingsboard/server/controller/AuthController.java index 9f6c6f3b44..4d586c06ad 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AuthController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AuthController.java @@ -35,6 +35,7 @@ import org.springframework.web.bind.annotation.RestController; import org.thingsboard.rule.engine.api.MailService; import org.thingsboard.server.common.data.User; 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.TenantId; @@ -124,6 +125,9 @@ public class AuthController extends BaseController { } userCredentials.setPassword(passwordEncoder.encode(newPassword)); userService.replaceUserCredentials(securityUser.getTenantId(), userCredentials); + + sendNotificationMsgToEdgeService(getTenantId(), userCredentials.getUserId(), ActionType.CREDENTIALS_UPDATED); + } catch (Exception e) { throw handleException(e); } @@ -231,6 +235,8 @@ public class AuthController extends BaseController { } } + sendNotificationMsgToEdgeService(user.getTenantId(), user.getId(), ActionType.CREDENTIALS_UPDATED); + JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser); JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser); 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 4c9ab5079f..a44d7c0f51 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -34,6 +34,7 @@ import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceInfo; import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.EntityViewInfo; @@ -94,7 +95,6 @@ import org.thingsboard.server.dao.device.ClaimDevicesService; import org.thingsboard.server.dao.device.DeviceCredentialsService; import org.thingsboard.server.dao.device.DeviceProfileService; 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; @@ -114,6 +114,8 @@ 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.edge.rpc.EdgeGrpcService; +import org.thingsboard.server.service.edge.rpc.init.SyncEdgeService; import org.thingsboard.server.service.profile.TbDeviceProfileCache; import org.thingsboard.server.service.queue.TbClusterService; import org.thingsboard.server.service.security.model.SecurityUser; @@ -205,9 +207,6 @@ public abstract class BaseController { @Autowired protected EntityViewService entityViewService; - @Autowired - protected EdgeService edgeService; - @Autowired protected TelemetrySubscriptionService tsSubService; @@ -226,16 +225,25 @@ public abstract class BaseController { @Autowired protected TbDeviceProfileCache deviceProfileCache; - @Autowired + @Autowired(required = false) + protected EdgeService edgeService; + + @Autowired(required = false) protected EdgeNotificationService edgeNotificationService; - @Autowired - protected EdgeEventService edgeEventService; + @Autowired(required = false) + protected SyncEdgeService syncEdgeService; + + @Autowired(required = false) + protected EdgeGrpcService edgeGrpcService; @Value("${server.log_controller_error_stack_trace}") @Getter private boolean logControllerErrorStackTrace; + @Value("${edges.rpc.enabled}") + @Getter + private boolean edgesSupportEnabled; @ExceptionHandler(ThingsboardException.class) public void handleThingsboardException(ThingsboardException ex, HttpServletResponse response) { @@ -774,8 +782,7 @@ public abstract class BaseController { String strTenantName = extractParameter(String.class, 1, additionalInfo); metaData.putValue("assignedToTenantId", strTenantId); metaData.putValue("assignedToTenantName", strTenantName); - } - if (actionType == ActionType.ASSIGNED_TO_EDGE) { + } else if (actionType == ActionType.ASSIGNED_TO_EDGE) { String strEdgeId = extractParameter(String.class, 1, additionalInfo); metaData.putValue("assignedEdgeId", strEdgeId); } else if (actionType == ActionType.UNASSIGNED_FROM_EDGE) { @@ -852,31 +859,66 @@ public abstract class BaseController { } return null; } - protected void sendNotificationMsgToEdgeService(TenantId tenantId, EntityRelation relation, ActionType edgeEventAction) { + + protected void sendNotificationMsgToEdgeService(TenantId tenantId, EdgeId edgeId, CustomerId customerId, ActionType action) { + if (!edgesSupportEnabled) { + return; + } try { - sendNotificationMsgToEdgeService(tenantId, null, null, json.writeValueAsString(relation), EdgeEventType.RELATION, edgeEventAction); + sendNotificationMsgToEdgeService(tenantId, edgeId, null, json.writeValueAsString(customerId), EdgeEventType.EDGE, action); } catch (Exception e) { - log.warn("Failed to push relation to core: {}", relation, e); + log.warn("Failed to push assign/unassign to/from customer to core: {}", customerId, e); } } - protected void sendNotificationMsgToEdgeService(TenantId tenantId, EntityId entityId, ActionType edgeEventAction) { - EdgeEventType edgeEventType = edgeEventService.getEdgeEventTypeByEntityType(entityId.getEntityType()); - if (edgeEventType != null) { - sendNotificationMsgToEdgeService(tenantId, null, entityId, null, edgeEventType, edgeEventAction); + protected void sendNotificationMsgToEdgeService(TenantId tenantId, EntityId entityId, CustomerId customerId, ActionType action) { + if (!edgesSupportEnabled) { + return; + } + EdgeEventType type = EdgeUtils.getEdgeEventTypeByEntityType(entityId.getEntityType()); + try { + if (type != null) { + sendNotificationMsgToEdgeService(tenantId, null, entityId, json.writeValueAsString(customerId), type, action); + } + } catch (Exception e) { + log.warn("Failed to push assign/unassign to/from customer to core: {}", customerId, e); + } + } + + protected void sendNotificationMsgToEdgeService(TenantId tenantId, EntityRelation relation, ActionType action) { + if (!edgesSupportEnabled) { + return; + } + try { + if (!relation.getFrom().getEntityType().equals(EntityType.EDGE) && + !relation.getTo().getEntityType().equals(EntityType.EDGE)) { + sendNotificationMsgToEdgeService(tenantId, null, null, json.writeValueAsString(relation), EdgeEventType.RELATION, action); + } + } catch (Exception e) { + log.warn("Failed to push relation to core: {}", relation, e); } } - protected void sendNotificationMsgToEdgeService(TenantId tenantId, EdgeId edgeId, EntityId entityId, EdgeEventType edgeEventType, ActionType edgeEventAction) { - sendNotificationMsgToEdgeService(tenantId, edgeId, entityId, null, edgeEventType, edgeEventAction); + protected void sendNotificationMsgToEdgeService(TenantId tenantId, EntityId entityId, ActionType action) { + sendNotificationMsgToEdgeService(tenantId, null, entityId, action); + } + + protected void sendNotificationMsgToEdgeService(TenantId tenantId, EdgeId edgeId, EntityId entityId, ActionType action) { + if (!edgesSupportEnabled) { + return; + } + EdgeEventType type = EdgeUtils.getEdgeEventTypeByEntityType(entityId.getEntityType()); + if (type != null) { + sendNotificationMsgToEdgeService(tenantId, edgeId, entityId, null, type, action); + } } - private void sendNotificationMsgToEdgeService(TenantId tenantId, EdgeId edgeId, EntityId entityId, String entityBody, EdgeEventType edgeEventType, ActionType edgeEventAction) { + private void sendNotificationMsgToEdgeService(TenantId tenantId, EdgeId edgeId, EntityId entityId, String body, EdgeEventType type, ActionType action) { 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()); + builder.setType(type.name()); + builder.setAction(action.name()); if (entityId != null) { builder.setEntityIdMSB(entityId.getId().getMostSignificantBits()); builder.setEntityIdLSB(entityId.getId().getLeastSignificantBits()); @@ -886,8 +928,8 @@ public abstract class BaseController { builder.setEdgeIdMSB(edgeId.getId().getMostSignificantBits()); builder.setEdgeIdLSB(edgeId.getId().getLeastSignificantBits()); } - if (entityBody != null) { - builder.setEntityBody(entityBody); + if (body != null) { + builder.setBody(body); } TransportProtos.EdgeNotificationMsgProto msg = builder.build(); tbClusterService.pushMsgToCore(tenantId, entityId != null ? entityId : tenantId, diff --git a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java index ff299e5c1f..09362e554f 100644 --- a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java +++ b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java @@ -108,6 +108,10 @@ public class CustomerController extends BaseController { savedCustomer.getId(), customer.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); + if (customer.getId() != null) { + sendNotificationMsgToEdgeService(savedCustomer.getTenantId(), savedCustomer.getId(),ActionType.UPDATED); + } + return savedCustomer; } catch (Exception e) { @@ -132,6 +136,7 @@ public class CustomerController extends BaseController { customer.getId(), ActionType.DELETED, null, strCustomerId); + sendNotificationMsgToEdgeService(getTenantId(), customerId, ActionType.DELETED); } catch (Exception e) { logEntityAction(emptyId(EntityType.CUSTOMER), 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 20694503fe..05e7178695 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java @@ -31,10 +31,8 @@ import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.DashboardInfo; import org.thingsboard.server.common.data.EntityType; 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,8 +114,9 @@ public class DashboardController extends BaseController { null, dashboard.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); - sendNotificationMsgToEdgeService(savedDashboard.getTenantId(), null, savedDashboard.getId(), - EdgeEventType.DASHBOARD, savedDashboard.getId() == null ? ActionType.ADDED : ActionType.UPDATED); + if (dashboard.getId() != null) { + sendNotificationMsgToEdgeService(savedDashboard.getTenantId(), savedDashboard.getId(), ActionType.UPDATED); + } return savedDashboard; } catch (Exception e) { @@ -142,7 +141,7 @@ public class DashboardController extends BaseController { null, ActionType.DELETED, null, strDashboardId); - sendNotificationMsgToEdgeService(getTenantId(), null, dashboardId, EdgeEventType.DASHBOARD, ActionType.DELETED); + sendNotificationMsgToEdgeService(getTenantId(), dashboardId, ActionType.DELETED); } catch (Exception e) { logEntityAction(emptyId(EntityType.DASHBOARD), @@ -174,6 +173,7 @@ public class DashboardController extends BaseController { customerId, ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, strCustomerId, customer.getName()); + sendNotificationMsgToEdgeService(savedDashboard.getTenantId(), savedDashboard.getId(), customerId, ActionType.ASSIGNED_TO_CUSTOMER); return savedDashboard; } catch (Exception e) { @@ -205,6 +205,8 @@ public class DashboardController extends BaseController { customerId, ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, customer.getId().toString(), customer.getName()); + sendNotificationMsgToEdgeService(savedDashboard.getTenantId(), savedDashboard.getId(), customerId, ActionType.UNASSIGNED_FROM_CUSTOMER); + return savedDashboard; } catch (Exception e) { @@ -260,6 +262,7 @@ public class DashboardController extends BaseController { logEntityAction(dashboardId, savedDashboard, customerId, ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle()); + sendNotificationMsgToEdgeService(savedDashboard.getTenantId(), savedDashboard.getId(), customerId, ActionType.ASSIGNED_TO_CUSTOMER); } for (CustomerId customerId : removedCustomerIds) { ShortCustomerInfo customerInfo = dashboard.getAssignedCustomerInfo(customerId); @@ -267,7 +270,7 @@ public class DashboardController extends BaseController { logEntityAction(dashboardId, dashboard, customerId, ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle()); - + sendNotificationMsgToEdgeService(savedDashboard.getTenantId(), savedDashboard.getId(), customerId, ActionType.UNASSIGNED_FROM_CUSTOMER); } return savedDashboard; } @@ -311,6 +314,7 @@ public class DashboardController extends BaseController { logEntityAction(dashboardId, savedDashboard, customerId, ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle()); + sendNotificationMsgToEdgeService(savedDashboard.getTenantId(), savedDashboard.getId(), customerId, ActionType.ASSIGNED_TO_CUSTOMER); } return savedDashboard; } @@ -354,7 +358,7 @@ public class DashboardController extends BaseController { logEntityAction(dashboardId, dashboard, customerId, ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle()); - + sendNotificationMsgToEdgeService(savedDashboard.getTenantId(), savedDashboard.getId(), customerId, ActionType.UNASSIGNED_FROM_CUSTOMER); } return savedDashboard; } @@ -501,8 +505,7 @@ public class DashboardController extends BaseController { null, ActionType.ASSIGNED_TO_EDGE, null, strDashboardId, strEdgeId, edge.getName()); - sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedDashboard.getId(), - EdgeEventType.DASHBOARD, ActionType.ASSIGNED_TO_EDGE); + sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedDashboard.getId(), ActionType.ASSIGNED_TO_EDGE); return savedDashboard; } catch (Exception e) { @@ -534,8 +537,7 @@ public class DashboardController extends BaseController { null, ActionType.UNASSIGNED_FROM_EDGE, null, strDashboardId, edge.getId().toString(), edge.getName()); - sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedDashboard.getId(), - EdgeEventType.DASHBOARD, ActionType.UNASSIGNED_FROM_EDGE); + sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedDashboard.getId(), 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 d9ce528f97..e9b41adf06 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java @@ -44,7 +44,6 @@ 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.EdgeEventType; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.CustomerId; @@ -126,8 +125,9 @@ public class DeviceController extends BaseController { tbClusterService.pushMsgToCore(new DeviceNameOrTypeUpdateMsg(savedDevice.getTenantId(), savedDevice.getId(), savedDevice.getName(), savedDevice.getType()), null); - sendNotificationMsgToEdgeService(savedDevice.getTenantId(), null, savedDevice.getId(), - EdgeEventType.DEVICE, device.getId() == null ? ActionType.ADDED : ActionType.UPDATED); + if (device.getId() != null) { + sendNotificationMsgToEdgeService(savedDevice.getTenantId(), savedDevice.getId(), ActionType.UPDATED); + } logEntityAction(savedDevice.getId(), savedDevice, savedDevice.getCustomerId(), @@ -160,7 +160,7 @@ public class DeviceController extends BaseController { device.getCustomerId(), ActionType.DELETED, null, strDeviceId); - sendNotificationMsgToEdgeService(getTenantId(), null, deviceId, EdgeEventType.DEVICE, ActionType.DELETED); + sendNotificationMsgToEdgeService(getTenantId(), deviceId, ActionType.DELETED); deviceStateService.onDeviceDeleted(device); } catch (Exception e) { @@ -192,6 +192,9 @@ public class DeviceController extends BaseController { savedDevice.getCustomerId(), ActionType.ASSIGNED_TO_CUSTOMER, null, strDeviceId, strCustomerId, customer.getName()); + sendNotificationMsgToEdgeService(savedDevice.getTenantId(), savedDevice.getId(), + customerId, ActionType.ASSIGNED_TO_CUSTOMER); + return savedDevice; } catch (Exception e) { logEntityAction(emptyId(EntityType.DEVICE), null, @@ -220,6 +223,9 @@ public class DeviceController extends BaseController { device.getCustomerId(), ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDeviceId, customer.getId().toString(), customer.getName()); + sendNotificationMsgToEdgeService(savedDevice.getTenantId(), savedDevice.getId(), + customer.getId(), ActionType.UNASSIGNED_FROM_CUSTOMER); + return savedDevice; } catch (Exception e) { logEntityAction(emptyId(EntityType.DEVICE), null, @@ -285,7 +291,7 @@ public class DeviceController extends BaseController { tbClusterService.pushMsgToCore(new DeviceCredentialsUpdateNotificationMsg(getCurrentUser().getTenantId(), deviceCredentials.getDeviceId()), null); - sendNotificationMsgToEdgeService(getTenantId(), null, device.getId(), EdgeEventType.DEVICE, ActionType.CREDENTIALS_UPDATED); + sendNotificationMsgToEdgeService(getTenantId(), device.getId(), ActionType.CREDENTIALS_UPDATED); logEntityAction(device.getId(), device, device.getCustomerId(), @@ -646,7 +652,7 @@ public class DeviceController extends BaseController { savedDevice.getCustomerId(), ActionType.ASSIGNED_TO_EDGE, null, strDeviceId, strEdgeId, edge.getName()); - sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedDevice.getId(), EdgeEventType.DEVICE, ActionType.ASSIGNED_TO_EDGE); + sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedDevice.getId(), ActionType.ASSIGNED_TO_EDGE); return savedDevice; } catch (Exception e) { @@ -677,7 +683,7 @@ public class DeviceController extends BaseController { device.getCustomerId(), ActionType.UNASSIGNED_FROM_EDGE, null, strDeviceId, edge.getId().toString(), edge.getName()); - sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedDevice.getId(), EdgeEventType.DEVICE, ActionType.UNASSIGNED_FROM_EDGE); + sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedDevice.getId(), ActionType.UNASSIGNED_FROM_EDGE); return savedDevice; } catch (Exception e) { 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 51c14283c2..7734c48094 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EdgeController.java @@ -15,7 +15,6 @@ */ 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; @@ -34,6 +33,7 @@ import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeInfo; import org.thingsboard.server.common.data.edge.EdgeSearchQuery; +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; @@ -41,10 +41,13 @@ import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.model.ModelConstants; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.EdgeGrpcSession; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; @@ -54,6 +57,7 @@ import java.util.List; import java.util.stream.Collectors; @RestController +@TbCoreComponent @RequestMapping("/api") public class EdgeController extends BaseController { @@ -102,6 +106,9 @@ public class EdgeController extends BaseController { edgeService.assignDefaultRuleChainsToEdge(tenantId, savedEdge.getId()); } + tbClusterService.onEntityStateChange(savedEdge.getTenantId(), savedEdge.getId(), + created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); + logEntityAction(savedEdge.getId(), savedEdge, null, created ? ActionType.ADDED : ActionType.UPDATED, null); return savedEdge; } catch (Exception e) { @@ -121,6 +128,9 @@ public class EdgeController extends BaseController { Edge edge = checkEdgeId(edgeId, Operation.DELETE); edgeService.deleteEdge(getTenantId(), edgeId); + tbClusterService.onEntityStateChange(getTenantId(), edgeId, + ComponentLifecycleEvent.DELETED); + logEntityAction(edgeId, edge, null, ActionType.DELETED, null, strEdgeId); @@ -169,10 +179,16 @@ public class EdgeController extends BaseController { Edge savedEdge = checkNotNull(edgeService.assignEdgeToCustomer(getCurrentUser().getTenantId(), edgeId, customerId)); + tbClusterService.onEntityStateChange(getTenantId(), edgeId, + ComponentLifecycleEvent.UPDATED); + logEntityAction(edgeId, savedEdge, savedEdge.getCustomerId(), ActionType.ASSIGNED_TO_CUSTOMER, null, strEdgeId, strCustomerId, customer.getName()); + sendNotificationMsgToEdgeService(savedEdge.getTenantId(), savedEdge.getId(), + customerId, ActionType.ASSIGNED_TO_CUSTOMER); + return savedEdge; } catch (Exception e) { logEntityAction(emptyId(EntityType.EDGE), null, @@ -197,10 +213,16 @@ public class EdgeController extends BaseController { Edge savedEdge = checkNotNull(edgeService.unassignEdgeFromCustomer(getCurrentUser().getTenantId(), edgeId)); + tbClusterService.onEntityStateChange(getTenantId(), edgeId, + ComponentLifecycleEvent.UPDATED); + logEntityAction(edgeId, edge, edge.getCustomerId(), ActionType.UNASSIGNED_FROM_CUSTOMER, null, strEdgeId, customer.getId().toString(), customer.getName()); + sendNotificationMsgToEdgeService(savedEdge.getTenantId(), savedEdge.getId(), + customer.getId(), ActionType.UNASSIGNED_FROM_CUSTOMER); + return savedEdge; } catch (Exception e) { logEntityAction(emptyId(EntityType.EDGE), null, @@ -310,6 +332,8 @@ public class EdgeController extends BaseController { Edge updatedEdge = edgeNotificationService.setEdgeRootRuleChain(getTenantId(), edge, ruleChainId); + tbClusterService.onEntityStateChange(updatedEdge.getTenantId(), updatedEdge.getId(), ComponentLifecycleEvent.UPDATED); + logEntityAction(updatedEdge.getId(), updatedEdge, null, ActionType.UPDATED, null); return updatedEdge; @@ -413,4 +437,41 @@ public class EdgeController extends BaseController { } } + @PreAuthorize("hasAuthority('TENANT_ADMIN')") + @RequestMapping(value = "/edge/sync", method = RequestMethod.POST) + public void syncEdge(@RequestBody EdgeId edgeId) throws ThingsboardException { + try { + edgeId = checkNotNull(edgeId); + if (isEdgesSupportEnabled()) { + EdgeGrpcSession session = edgeGrpcService.getEdgeGrpcSessionById(edgeId); + Edge edge = session.getEdge(); + syncEdgeService.sync(edge); + } else { + throw new ThingsboardException("Edges support disabled", ThingsboardErrorCode.GENERAL); + } + } catch (Exception e) { + throw handleException(e); + } + } + + @RequestMapping(value = "/license/checkInstance", method = RequestMethod.POST) + @ResponseBody + public Object checkInstance(@RequestBody Object request) throws ThingsboardException { + try { + return edgeService.checkInstance(request); + } catch (Exception e) { + throw new ThingsboardException(e, ThingsboardErrorCode.SUBSCRIPTION_VIOLATION); + } + } + + @RequestMapping(value = "/license/activateInstance", params = {"licenseSecret", "releaseDate"}, method = RequestMethod.POST) + @ResponseBody + public Object activateInstance(@RequestParam String licenseSecret, + @RequestParam String releaseDate) throws ThingsboardException { + try { + return edgeService.activateInstance(licenseSecret, releaseDate); + } catch (Exception e) { + throw new ThingsboardException(e, ThingsboardErrorCode.SUBSCRIPTION_VIOLATION); + } + } } diff --git a/application/src/main/java/org/thingsboard/server/controller/EdgeEventController.java b/application/src/main/java/org/thingsboard/server/controller/EdgeEventController.java new file mode 100644 index 0000000000..2e6e9b22ce --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/controller/EdgeEventController.java @@ -0,0 +1,71 @@ +/** + * 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.controller; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; +import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.EdgeId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.TimePageLink; +import org.thingsboard.server.dao.edge.EdgeEventService; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.security.permission.Operation; + +@Slf4j +@RestController +@TbCoreComponent +@RequestMapping("/api") +public class EdgeEventController extends BaseController { + + @Autowired + private EdgeEventService edgeEventService; + + public static final String EDGE_ID = "edgeId"; + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/edge/{edgeId}/events", method = RequestMethod.GET) + @ResponseBody + public PageData getEdgeEvents( + @PathVariable(EDGE_ID) String strEdgeId, + @RequestParam int pageSize, + @RequestParam int page, + @RequestParam(required = false) String textSearch, + @RequestParam(required = false) String sortProperty, + @RequestParam(required = false) String sortOrder, + @RequestParam(required = false) Long startTime, + @RequestParam(required = false) Long endTime) throws ThingsboardException { + checkParameter(EDGE_ID, strEdgeId); + try { + TenantId tenantId = getCurrentUser().getTenantId(); + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); + checkEdgeId(edgeId, Operation.READ); + TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); + return checkNotNull(edgeEventService.findEdgeEvents(tenantId, edgeId, pageLink, false)); + } catch (Exception e) { + throw handleException(e); + } + } +} 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 38060c7731..03e9ace3fa 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java @@ -24,9 +24,7 @@ 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; @@ -132,8 +130,6 @@ 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 91e5d98c19..7cc21aa7eb 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java @@ -161,8 +161,10 @@ public class EntityViewController extends BaseController { logEntityAction(savedEntityView.getId(), savedEntityView, null, entityView.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); - sendNotificationMsgToEdgeService(getTenantId(), null, savedEntityView.getId(), - EdgeEventType.ENTITY_VIEW, entityView.getId() == null ? ActionType.ADDED : ActionType.UPDATED); + if (entityView.getId() != null) { + sendNotificationMsgToEdgeService(savedEntityView.getTenantId(), savedEntityView.getId(), ActionType.UPDATED); + } + return savedEntityView; } catch (Exception e) { logEntityAction(emptyId(EntityType.ENTITY_VIEW), entityView, null, @@ -362,7 +364,7 @@ public class EntityViewController extends BaseController { logEntityAction(entityViewId, entityView, entityView.getCustomerId(), ActionType.DELETED, null, strEntityViewId); - sendNotificationMsgToEdgeService(getTenantId(), null, entityViewId, EdgeEventType.ENTITY_VIEW, ActionType.DELETED); + sendNotificationMsgToEdgeService(getTenantId(), entityViewId, ActionType.DELETED); } catch (Exception e) { logEntityAction(emptyId(EntityType.ENTITY_VIEW), null, @@ -403,6 +405,10 @@ public class EntityViewController extends BaseController { logEntityAction(entityViewId, savedEntityView, savedEntityView.getCustomerId(), ActionType.ASSIGNED_TO_CUSTOMER, null, strEntityViewId, strCustomerId, customer.getName()); + + sendNotificationMsgToEdgeService(savedEntityView.getTenantId(), savedEntityView.getId(), + customerId, ActionType.ASSIGNED_TO_CUSTOMER); + return savedEntityView; } catch (Exception e) { logEntityAction(emptyId(EntityType.ENTITY_VIEW), null, @@ -429,6 +435,9 @@ public class EntityViewController extends BaseController { entityView.getCustomerId(), ActionType.UNASSIGNED_FROM_CUSTOMER, null, strEntityViewId, customer.getId().toString(), customer.getName()); + sendNotificationMsgToEdgeService(savedEntityView.getTenantId(), savedEntityView.getId(), + customer.getId(), ActionType.UNASSIGNED_FROM_CUSTOMER); + return savedEntityView; } catch (Exception e) { logEntityAction(emptyId(EntityType.ENTITY_VIEW), null, @@ -620,8 +629,7 @@ public class EntityViewController extends BaseController { savedEntityView.getCustomerId(), ActionType.ASSIGNED_TO_EDGE, null, strEntityViewId, strEdgeId, edge.getName()); - sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedEntityView.getId(), - EdgeEventType.ENTITY_VIEW, ActionType.ASSIGNED_TO_EDGE); + sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedEntityView.getId(), ActionType.ASSIGNED_TO_EDGE); return savedEntityView; } catch (Exception e) { @@ -651,8 +659,7 @@ public class EntityViewController extends BaseController { entityView.getCustomerId(), ActionType.UNASSIGNED_FROM_EDGE, null, strEntityViewId, edge.getId().toString(), edge.getName()); - sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedEntityView.getId(), - EdgeEventType.ENTITY_VIEW, ActionType.UNASSIGNED_FROM_EDGE); + sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedEntityView.getId(), 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 0332edd735..20b3fa60ea 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java +++ b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java @@ -152,9 +152,9 @@ public class RuleChainController extends BaseController { created ? ActionType.ADDED : ActionType.UPDATED, null); if (RuleChainType.EDGE.equals(savedRuleChain.getType())) { - sendNotificationMsgToEdgeService(savedRuleChain.getTenantId(), null, - savedRuleChain.getId(), EdgeEventType.RULE_CHAIN, - savedRuleChain.getId() == null ? ActionType.ADDED : ActionType.UPDATED); + if (!created) { + sendNotificationMsgToEdgeService(savedRuleChain.getTenantId(), savedRuleChain.getId(), ActionType.UPDATED); + } } return savedRuleChain; @@ -254,9 +254,7 @@ public class RuleChainController extends BaseController { if (RuleChainType.EDGE.equals(ruleChain.getType())) { sendNotificationMsgToEdgeService(ruleChain.getTenantId(), - null, - ruleChain.getId(), EdgeEventType.RULE_CHAIN, - ActionType.UPDATED); + ruleChain.getId(), ActionType.UPDATED); } return savedRuleChainMetaData; @@ -322,8 +320,7 @@ public class RuleChainController extends BaseController { ActionType.DELETED, null, strRuleChainId); if (RuleChainType.EDGE.equals(ruleChain.getType())) { - sendNotificationMsgToEdgeService(ruleChain.getTenantId(), null, - ruleChain.getId(), EdgeEventType.RULE_CHAIN, ActionType.DELETED); + sendNotificationMsgToEdgeService(ruleChain.getTenantId(), ruleChain.getId(), ActionType.DELETED); } } catch (Exception e) { @@ -485,8 +482,7 @@ public class RuleChainController extends BaseController { null, ActionType.ASSIGNED_TO_EDGE, null, strRuleChainId, strEdgeId, edge.getName()); - sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedRuleChain.getId(), - EdgeEventType.RULE_CHAIN, ActionType.ASSIGNED_TO_EDGE); + sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedRuleChain.getId(), ActionType.ASSIGNED_TO_EDGE); return savedRuleChain; } catch (Exception e) { @@ -518,8 +514,7 @@ public class RuleChainController extends BaseController { null, ActionType.UNASSIGNED_FROM_EDGE, null, strRuleChainId, edge.getId().toString(), edge.getName()); - sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedRuleChain.getId(), - EdgeEventType.RULE_CHAIN, ActionType.UNASSIGNED_FROM_EDGE); + sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedRuleChain.getId(), ActionType.UNASSIGNED_FROM_EDGE); return savedRuleChain; } catch (Exception e) { 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 fce56b509e..47c0d735fb 100644 --- a/application/src/main/java/org/thingsboard/server/controller/UserController.java +++ b/application/src/main/java/org/thingsboard/server/controller/UserController.java @@ -162,6 +162,9 @@ public class UserController extends BaseController { savedUser.getCustomerId(), user.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); + sendNotificationMsgToEdgeService(getTenantId(), savedUser.getId(), + user.getId() == null ? ActionType.ADDED : ActionType.UPDATED); + return savedUser; } catch (Exception e) { @@ -237,6 +240,8 @@ public class UserController extends BaseController { user.getCustomerId(), ActionType.DELETED, null, strUserId); + sendNotificationMsgToEdgeService(getTenantId(), user.getId(), ActionType.DELETED); + } catch (Exception e) { logEntityAction(emptyId(EntityType.USER), null, diff --git a/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java b/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java index 2b7f88a512..ae93e38f43 100644 --- a/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java +++ b/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.controller; +import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.PathVariable; @@ -25,6 +26,7 @@ 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.audit.ActionType; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.WidgetTypeId; @@ -37,6 +39,7 @@ import org.thingsboard.server.service.security.permission.Resource; import java.util.List; +@Slf4j @RestController @TbCoreComponent @RequestMapping("/api") @@ -67,8 +70,12 @@ public class WidgetTypeController extends BaseController { } checkEntity(widgetType.getId(), widgetType, Resource.WIDGET_TYPE); + WidgetType savedWidgetType = widgetTypeService.saveWidgetType(widgetType); - return checkNotNull(widgetTypeService.saveWidgetType(widgetType)); + sendNotificationMsgToEdgeService(getTenantId(), savedWidgetType.getId(), + widgetType.getId() == null ? ActionType.ADDED : ActionType.UPDATED); + + return checkNotNull(savedWidgetType); } catch (Exception e) { throw handleException(e); } @@ -83,6 +90,9 @@ public class WidgetTypeController extends BaseController { WidgetTypeId widgetTypeId = new WidgetTypeId(toUUID(strWidgetTypeId)); checkWidgetTypeId(widgetTypeId, Operation.DELETE); widgetTypeService.deleteWidgetType(getCurrentUser().getTenantId(), widgetTypeId); + + sendNotificationMsgToEdgeService(getTenantId(), widgetTypeId, ActionType.DELETED); + } catch (Exception e) { throw handleException(e); } diff --git a/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java b/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java index 0eed69d50a..9179bc43c6 100644 --- a/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java +++ b/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java @@ -25,6 +25,7 @@ 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.audit.ActionType; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.WidgetsBundleId; @@ -68,7 +69,12 @@ public class WidgetsBundleController extends BaseController { } checkEntity(widgetsBundle.getId(), widgetsBundle, Resource.WIDGETS_BUNDLE); - return checkNotNull(widgetsBundleService.saveWidgetsBundle(widgetsBundle)); + WidgetsBundle savedWidgetsBundle = widgetsBundleService.saveWidgetsBundle(widgetsBundle); + + sendNotificationMsgToEdgeService(getTenantId(), savedWidgetsBundle.getId(), + widgetsBundle.getId() == null ? ActionType.ADDED : ActionType.UPDATED); + + return checkNotNull(savedWidgetsBundle); } catch (Exception e) { throw handleException(e); } @@ -83,6 +89,9 @@ public class WidgetsBundleController extends BaseController { WidgetsBundleId widgetsBundleId = new WidgetsBundleId(toUUID(strWidgetsBundleId)); checkWidgetsBundleId(widgetsBundleId, Operation.DELETE); widgetsBundleService.deleteWidgetsBundle(getTenantId(), widgetsBundleId); + + sendNotificationMsgToEdgeService(getTenantId(), widgetsBundleId, ActionType.DELETED); + } catch (Exception e) { throw handleException(e); } diff --git a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java index e139f9a821..f8026d6278 100644 --- a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java +++ b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java @@ -27,6 +27,7 @@ import org.springframework.security.authentication.LockedException; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.access.AccessDeniedHandler; import org.springframework.stereotype.Component; +import org.springframework.web.client.HttpClientErrorException; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.msg.tools.TbRateLimitsException; @@ -66,7 +67,12 @@ public class ThingsboardErrorResponseHandler implements AccessDeniedHandler { response.setContentType(MediaType.APPLICATION_JSON_VALUE); if (exception instanceof ThingsboardException) { - handleThingsboardException((ThingsboardException) exception, response); + ThingsboardException thingsboardException = (ThingsboardException) exception; + if (thingsboardException.getErrorCode() == ThingsboardErrorCode.SUBSCRIPTION_VIOLATION) { + handleSubscriptionException((ThingsboardException) exception, response); + } else { + handleThingsboardException((ThingsboardException) exception, response); + } } else if (exception instanceof TbRateLimitsException) { handleRateLimitException(response, (TbRateLimitsException) exception); } else if (exception instanceof AccessDeniedException) { @@ -126,6 +132,11 @@ public class ThingsboardErrorResponseHandler implements AccessDeniedHandler { ThingsboardErrorCode.TOO_MANY_REQUESTS, HttpStatus.TOO_MANY_REQUESTS)); } + private void handleSubscriptionException(ThingsboardException subscriptionException, HttpServletResponse response) throws IOException { + response.setStatus(HttpStatus.FORBIDDEN.value()); + mapper.writeValue(response.getWriter(), + (new ObjectMapper()).readValue(((HttpClientErrorException) subscriptionException.getCause()).getResponseBodyAsByteArray(), Object.class)); + } private void handleAccessDeniedException(HttpServletResponse response) throws IOException { response.setStatus(HttpStatus.FORBIDDEN.value()); diff --git a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java index 7bcf38080e..2b4fae6045 100644 --- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java +++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java @@ -167,7 +167,17 @@ public class ThingsboardInstallService { databaseTsUpgradeService.upgradeDatabase("2.5.0"); } case "2.5.1": - log.info("Upgrading ThingsBoard from version 2.5.1 to 3.0.0 ..."); + log.info("Upgrading ThingsBoard from version 2.5.1 to 2.5.5 ..."); + case "2.5.5": + log.info("Upgrading ThingsBoard from version 2.5.5 to 2.6.0 ..."); + if (databaseTsUpgradeService != null) { + databaseTsUpgradeService.upgradeDatabase("2.5.5"); + } + databaseEntitiesUpgradeService.upgradeDatabase("2.5.5"); + + dataUpdateService.updateData("2.5.5"); + case "2.6.0": + log.info("Upgrading ThingsBoard from version 2.6.0 to 3.0.0 ..."); case "3.0.1": log.info("Upgrading ThingsBoard from version 3.0.1 to 3.1.0 ..."); databaseEntitiesUpgradeService.upgradeDatabase("3.0.1"); 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 a13d5ed79c..e20f434042 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,39 +15,42 @@ */ package org.thingsboard.server.service.edge; +import com.fasterxml.jackson.core.JsonProcessingException; 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 lombok.extern.slf4j.Slf4j; +import org.checkerframework.checker.nullness.qual.Nullable; 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.User; import org.thingsboard.server.common.data.alarm.Alarm; 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.CustomerId; 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.PageData; import org.thingsboard.server.common.data.page.PageLink; 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.RuleChainMetaData; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo; import org.thingsboard.server.common.msg.queue.TbCallback; 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.relation.RelationService; import org.thingsboard.server.dao.rule.RuleChainService; +import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.executors.DbCallbackExecutorService; @@ -56,14 +59,12 @@ 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 @@ -76,13 +77,13 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { private EdgeService edgeService; @Autowired - private RuleChainService ruleChainService; + private AlarmService alarmService; @Autowired - private AlarmService alarmService; + private UserService userService; @Autowired - private RelationService relationService; + private RuleChainService ruleChainService; @Autowired private EdgeEventService edgeEventService; @@ -106,7 +107,7 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { @Override public PageData findEdgeEvents(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink) { - return edgeEventService.findEdgeEvents(tenantId, edgeId, pageLink); + return edgeEventService.findEdgeEvents(tenantId, edgeId, pageLink, true); } @Override @@ -119,22 +120,22 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { private void saveEdgeEvent(TenantId tenantId, EdgeId edgeId, - EdgeEventType edgeEventType, - ActionType edgeEventAction, + EdgeEventType type, + ActionType action, EntityId entityId, - JsonNode entityBody) { - log.debug("Pushing edge event to edge queue. tenantId [{}], edgeId [{}], edgeEventType [{}], edgeEventAction[{}], entityId [{}], entityBody [{}]", - tenantId, edgeId, edgeEventType, edgeEventAction, entityId, entityBody); + JsonNode body) { + log.debug("Pushing edge event to edge queue. tenantId [{}], edgeId [{}], type [{}], action[{}], entityId [{}], body [{}]", + tenantId, edgeId, type, action, entityId, body); EdgeEvent edgeEvent = new EdgeEvent(); edgeEvent.setEdgeId(edgeId); edgeEvent.setTenantId(tenantId); - edgeEvent.setEdgeEventType(edgeEventType); - edgeEvent.setEdgeEventAction(edgeEventAction.name()); + edgeEvent.setType(type); + edgeEvent.setAction(action.name()); if (entityId != null) { edgeEvent.setEntityId(entityId.getId()); } - edgeEvent.setEntityBody(entityBody); + edgeEvent.setBody(body); edgeEventService.saveAsync(edgeEvent); } @@ -142,16 +143,25 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { public void pushNotificationToEdge(TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg, TbCallback callback) { try { 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: + EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType()); + switch (type) { + case EDGE: + processEdge(tenantId, edgeNotificationMsg); + break; + case USER: case ASSET: case DEVICE: case ENTITY_VIEW: case DASHBOARD: case RULE_CHAIN: - processEntities(tenantId, edgeNotificationMsg); + processEntity(tenantId, edgeNotificationMsg); + break; + case CUSTOMER: + processCustomer(tenantId, edgeNotificationMsg); + break; + case WIDGETS_BUNDLE: + case WIDGET_TYPE: + processWidgetBundleOrWidgetType(tenantId, edgeNotificationMsg); break; case ALARM: processAlarm(tenantId, edgeNotificationMsg); @@ -160,7 +170,7 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { processRelation(tenantId, edgeNotificationMsg); break; default: - log.debug("Edge event type [{}] is not designed to be pushed to edge", edgeEventType); + log.debug("Edge event type [{}] is not designed to be pushed to edge", type); } } catch (Exception e) { callback.onFailure(e); @@ -170,67 +180,219 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { } } - 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: + private void processEdge(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) { + try { + ActionType actionType = ActionType.valueOf(edgeNotificationMsg.getAction()); + EdgeId edgeId = new EdgeId(new UUID(edgeNotificationMsg.getEdgeIdMSB(), edgeNotificationMsg.getEdgeIdLSB())); + ListenableFuture edgeFuture; + switch (actionType) { + case ASSIGNED_TO_CUSTOMER: + CustomerId customerId = mapper.readValue(edgeNotificationMsg.getBody(), CustomerId.class); + edgeFuture = edgeService.findEdgeByIdAsync(tenantId, edgeId); + Futures.addCallback(edgeFuture, new FutureCallback() { + @Override + public void onSuccess(@Nullable Edge edge) { + if (edge != null && !customerId.isNullUid()) { + saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.CUSTOMER, ActionType.ADDED, customerId, null); + PageData pageData = userService.findCustomerUsers(tenantId, customerId, new PageLink(Integer.MAX_VALUE)); + if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) { + log.trace("[{}] [{}] user(s) are going to be added to edge.", edge.getId(), pageData.getData().size()); + for (User user : pageData.getData()) { + saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.USER, ActionType.ADDED, user.getId(), null); + } + } + } + } + + @Override + public void onFailure(Throwable t) { + log.error("Can't find edge by id [{}]", edgeNotificationMsg, t); + } + }, dbCallbackExecutorService); + break; + case UNASSIGNED_FROM_CUSTOMER: + CustomerId customerIdToDelete = mapper.readValue(edgeNotificationMsg.getBody(), CustomerId.class); + edgeFuture = edgeService.findEdgeByIdAsync(tenantId, edgeId); + Futures.addCallback(edgeFuture, new FutureCallback() { + @Override + public void onSuccess(@Nullable Edge edge) { + if (edge != null && !customerIdToDelete.isNullUid()) { + saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.CUSTOMER, ActionType.DELETED, customerIdToDelete, null); + } + } + + @Override + public void onFailure(Throwable t) { + log.error("Can't find edge by id [{}]", edgeNotificationMsg, t); + } + }, dbCallbackExecutorService); + break; + } + } catch (Exception e) { + log.error("Exception during processing edge event", e); + } + } + + private void processWidgetBundleOrWidgetType(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) { + ActionType actionType = ActionType.valueOf(edgeNotificationMsg.getAction()); + EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType()); + EntityId entityId = EntityIdFactory.getByEdgeEventTypeAndUuid(type, new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB())); + switch (actionType) { + 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); + case DELETED: + PageData edgesByTenantId = edgeService.findEdgesByTenantId(tenantId, new PageLink(Integer.MAX_VALUE)); + if (edgesByTenantId != null && edgesByTenantId.getData() != null && !edgesByTenantId.getData().isEmpty()) { + for (Edge edge : edgesByTenantId.getData()) { + saveEdgeEvent(tenantId, edge.getId(), type, actionType, entityId, null); + } + } + break; + } + } + + private void processCustomer(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) { + ActionType actionType = ActionType.valueOf(edgeNotificationMsg.getAction()); + EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType()); + EntityId entityId = EntityIdFactory.getByEdgeEventTypeAndUuid(type, new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB())); + PageData edgesByTenantId = edgeService.findEdgesByTenantId(tenantId, new PageLink(Integer.MAX_VALUE)); + if (edgesByTenantId != null && edgesByTenantId.getData() != null && !edgesByTenantId.getData().isEmpty()) { + for (Edge edge : edgesByTenantId.getData()) { + switch (actionType) { + case UPDATED: + if (!edge.getCustomerId().isNullUid() && edge.getCustomerId().equals(entityId)) { + saveEdgeEvent(tenantId, edge.getId(), type, actionType, entityId, null); + } + break; + case DELETED: + saveEdgeEvent(tenantId, edge.getId(), type, actionType, entityId, null); + break; + } + } + } + } + + private void processEntity(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) { + ActionType actionType = ActionType.valueOf(edgeNotificationMsg.getAction()); + EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType()); + EntityId entityId = EntityIdFactory.getByEdgeEventTypeAndUuid(type, + new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB())); + ListenableFuture> edgeIdsFuture; + switch (actionType) { + case ADDED: // used only for USER entity + case UPDATED: + case CREDENTIALS_UPDATED: + edgeIdsFuture = edgeService.findRelatedEdgeIdsByEntityId(tenantId, entityId); + Futures.addCallback(edgeIdsFuture, new FutureCallback>() { + @Override + public void onSuccess(@Nullable List edgeIds) { + if (edgeIds != null && !edgeIds.isEmpty()) { + for (EdgeId edgeId : edgeIds) { + saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, null); + } + } + } + @Override + public void onFailure(Throwable throwable) { + log.error("Failed to find related edge ids [{}]", edgeNotificationMsg, throwable); + } + }, dbCallbackExecutorService); + break; + case ASSIGNED_TO_CUSTOMER: + case UNASSIGNED_FROM_CUSTOMER: + edgeIdsFuture = edgeService.findRelatedEdgeIdsByEntityId(tenantId, entityId); + Futures.addCallback(edgeIdsFuture, new FutureCallback>() { + @Override + public void onSuccess(@Nullable List edgeIds) { + if (edgeIds != null && !edgeIds.isEmpty()) { + for (EdgeId edgeId : edgeIds) { + try { + CustomerId customerId = mapper.readValue(edgeNotificationMsg.getBody(), CustomerId.class); + ListenableFuture future = edgeService.findEdgeByIdAsync(tenantId, edgeId); + Futures.addCallback(future, new FutureCallback() { + @Override + public void onSuccess(@Nullable Edge edge) { + if (edge != null && edge.getCustomerId() != null && + !edge.getCustomerId().isNullUid() && edge.getCustomerId().equals(customerId)) { + saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, null); + } + } + @Override + public void onFailure(Throwable throwable) { + log.error("Failed to find edge by id [{}]", edgeNotificationMsg, throwable); + } + }, dbCallbackExecutorService); + } catch (Exception e) { + log.error("Can't parse customer id from entity body [{}]", edgeNotificationMsg, e); } - } catch (Exception e) { - log.error("[{}] Failed to push event to edge, edgeId [{}], edgeEventType [{}], edgeEventActionType [{}], entityId [{}]", - tenantId, edgeId, edgeEventType, edgeEventActionType, entityId, e); } } } - return null; + + @Override + public void onFailure(Throwable throwable) { + log.error("Failed to find related edge ids [{}]", edgeNotificationMsg, throwable); + } }, dbCallbackExecutorService); break; case DELETED: PageData edgesByTenantId = edgeService.findEdgesByTenantId(tenantId, new PageLink(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); + saveEdgeEvent(tenantId, edge.getId(), type, actionType, 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 + saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, null); + if (type.equals(EdgeEventType.RULE_CHAIN)) { + updateDependentRuleChains(tenantId, new RuleChainId(entityId.getId()), edgeId); + } break; } } + private void updateDependentRuleChains(TenantId tenantId, RuleChainId processingRuleChainId, EdgeId edgeId) { + PageData pageData = ruleChainService.findRuleChainsByTenantIdAndEdgeId(tenantId, edgeId, new TimePageLink(Integer.MAX_VALUE)); + if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) { + for (RuleChain ruleChain : pageData.getData()) { + if (!ruleChain.getId().equals(processingRuleChainId)) { + List connectionInfos = + ruleChainService.loadRuleChainMetaData(ruleChain.getTenantId(), ruleChain.getId()).getRuleChainConnections(); + if (connectionInfos != null && !connectionInfos.isEmpty()) { + for (RuleChainConnectionInfo connectionInfo : connectionInfos) { + if (connectionInfo.getTargetRuleChainId().equals(processingRuleChainId)) { + saveEdgeEvent(tenantId, + edgeId, + EdgeEventType.RULE_CHAIN_METADATA, + ActionType.UPDATED, + ruleChain.getId(), + null); + } + } + } + } + } + } + } + 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> relatedEdgeIdsByEntityIdFuture = findRelatedEdgeIdsByEntityId(tenantId, alarm.getOriginator()); + EdgeEventType type = getEdgeQueueTypeByEntityType(alarm.getOriginator().getEntityType()); + if (type != null) { + ListenableFuture> relatedEdgeIdsByEntityIdFuture = edgeService.findRelatedEdgeIdsByEntityId(tenantId, alarm.getOriginator()); Futures.transform(relatedEdgeIdsByEntityIdFuture, relatedEdgeIdsByEntityId -> { if (relatedEdgeIdsByEntityId != null) { for (EdgeId edgeId : relatedEdgeIdsByEntityId) { saveEdgeEvent(tenantId, edgeId, EdgeEventType.ALARM, - ActionType.valueOf(edgeNotificationMsg.getEdgeEventAction()), + ActionType.valueOf(edgeNotificationMsg.getAction()), alarmId, null); } @@ -243,67 +405,38 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { }, dbCallbackExecutorService); } - private void processRelation(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) { - EntityRelation entityRelation = mapper.convertValue(edgeNotificationMsg.getEntityBody(), EntityRelation.class); - List>> futures = new ArrayList<>(); - 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<>(); - if (listOfListsEdgeIds != null && !listOfListsEdgeIds.isEmpty()) { - for (List listOfListsEdgeId : listOfListsEdgeIds) { - if (listOfListsEdgeId != null) { - uniqueEdgeIds.addAll(listOfListsEdgeId); + private void processRelation(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) throws JsonProcessingException { + EntityRelation relation = mapper.readValue(edgeNotificationMsg.getBody(), EntityRelation.class); + if (!relation.getFrom().getEntityType().equals(EntityType.EDGE) && + !relation.getTo().getEntityType().equals(EntityType.EDGE)) { + List>> futures = new ArrayList<>(); + futures.add(edgeService.findRelatedEdgeIdsByEntityId(tenantId, relation.getTo())); + futures.add(edgeService.findRelatedEdgeIdsByEntityId(tenantId, relation.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); + } } } - } - if (!uniqueEdgeIds.isEmpty()) { - for (EdgeId edgeId : uniqueEdgeIds) { - saveEdgeEvent(tenantId, - edgeId, - EdgeEventType.RELATION, - ActionType.valueOf(edgeNotificationMsg.getEdgeEventAction()), - null, - mapper.valueToTree(entityRelation)); - } - } - return null; - }, dbCallbackExecutorService); - } - - private ListenableFuture> findRelatedEdgeIdsByEntityId(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(); + if (!uniqueEdgeIds.isEmpty()) { + for (EdgeId edgeId : uniqueEdgeIds) { + saveEdgeEvent(tenantId, + edgeId, + EdgeEventType.RELATION, + ActionType.valueOf(edgeNotificationMsg.getAction()), + null, + mapper.valueToTree(relation)); } - }, dbCallbackExecutorService); - case DASHBOARD: - return convertToEdgeIds(edgeService.findEdgesByTenantIdAndDashboardId(tenantId, new DashboardId(entityId.getId()))); - case RULE_CHAIN: - return convertToEdgeIds(edgeService.findEdgesByTenantIdAndRuleChainId(tenantId, new RuleChainId(entityId.getId()))); - default: - return Futures.immediateFuture(Collections.emptyList()); + } + return null; + }, dbCallbackExecutorService); } } - private ListenableFuture> convertToEdgeIds(ListenableFuture> future) { - return Futures.transform(future, edges -> { - if (edges != null && !edges.isEmpty()) { - return edges.stream().map(IdBased::getId).collect(Collectors.toList()); - } else { - return Collections.emptyList(); - } - }, dbCallbackExecutorService); - } - private EdgeEventType getEdgeQueueTypeByEntityType(EntityType entityType) { switch (entityType) { case DEVICE: @@ -313,7 +446,7 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { case ENTITY_VIEW: return EdgeEventType.ENTITY_VIEW; default: - log.info("Unsupported entity type: [{}]", entityType); + log.debug("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 32f38d2621..c90cc99965 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 @@ -33,22 +33,35 @@ 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.dao.widget.WidgetTypeService; +import org.thingsboard.server.dao.widget.WidgetsBundleService; +import org.thingsboard.server.queue.discovery.PartitionService; +import org.thingsboard.server.queue.util.TbCoreComponent; 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.AdminSettingsMsgConstructor; +import org.thingsboard.server.service.edge.rpc.constructor.AlarmMsgConstructor; +import org.thingsboard.server.service.edge.rpc.constructor.AssetMsgConstructor; +import org.thingsboard.server.service.edge.rpc.constructor.CustomerMsgConstructor; +import org.thingsboard.server.service.edge.rpc.constructor.DashboardMsgConstructor; +import org.thingsboard.server.service.edge.rpc.constructor.DeviceMsgConstructor; import org.thingsboard.server.service.edge.rpc.constructor.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; -import org.thingsboard.server.service.edge.rpc.constructor.UserUpdateMsgConstructor; +import org.thingsboard.server.service.edge.rpc.constructor.EntityViewMsgConstructor; +import org.thingsboard.server.service.edge.rpc.constructor.RelationMsgConstructor; +import org.thingsboard.server.service.edge.rpc.constructor.RuleChainMsgConstructor; +import org.thingsboard.server.service.edge.rpc.constructor.UserMsgConstructor; +import org.thingsboard.server.service.edge.rpc.constructor.WidgetTypeMsgConstructor; +import org.thingsboard.server.service.edge.rpc.constructor.WidgetsBundleMsgConstructor; import org.thingsboard.server.service.edge.rpc.init.SyncEdgeService; +import org.thingsboard.server.service.edge.rpc.processor.AlarmProcessor; +import org.thingsboard.server.service.edge.rpc.processor.DeviceProcessor; +import org.thingsboard.server.service.edge.rpc.processor.RelationProcessor; +import org.thingsboard.server.service.edge.rpc.processor.TelemetryProcessor; import org.thingsboard.server.service.executors.DbCallbackExecutorService; import org.thingsboard.server.service.queue.TbClusterService; import org.thingsboard.server.service.state.DeviceStateService; @Component +@TbCoreComponent @Data public class EdgeContextComponent { @@ -56,6 +69,9 @@ public class EdgeContextComponent { @Autowired private EdgeService edgeService; + @Autowired + private PartitionService partitionService; + @Lazy @Autowired private EdgeNotificationService edgeNotificationService; @@ -108,6 +124,14 @@ public class EdgeContextComponent { @Autowired private ActorService actorService; + @Lazy + @Autowired + private WidgetsBundleService widgetsBundleService; + + @Lazy + @Autowired + private WidgetTypeService widgetTypeService; + @Lazy @Autowired private DeviceStateService deviceStateService; @@ -122,40 +146,72 @@ public class EdgeContextComponent { @Lazy @Autowired - private RuleChainUpdateMsgConstructor ruleChainUpdateMsgConstructor; + private RuleChainMsgConstructor ruleChainMsgConstructor; + + @Lazy + @Autowired + private AlarmMsgConstructor alarmMsgConstructor; + + @Lazy + @Autowired + private DeviceMsgConstructor deviceMsgConstructor; + + @Lazy + @Autowired + private AssetMsgConstructor assetMsgConstructor; + + @Lazy + @Autowired + private EntityViewMsgConstructor entityViewMsgConstructor; @Lazy @Autowired - private AlarmUpdateMsgConstructor alarmUpdateMsgConstructor; + private DashboardMsgConstructor dashboardMsgConstructor; @Lazy @Autowired - private DeviceUpdateMsgConstructor deviceUpdateMsgConstructor; + private CustomerMsgConstructor customerMsgConstructor; @Lazy @Autowired - private AssetUpdateMsgConstructor assetUpdateMsgConstructor; + private UserMsgConstructor userMsgConstructor; @Lazy @Autowired - private EntityViewUpdateMsgConstructor entityViewUpdateMsgConstructor; + private RelationMsgConstructor relationMsgConstructor; @Lazy @Autowired - private DashboardUpdateMsgConstructor dashboardUpdateMsgConstructor; + private WidgetsBundleMsgConstructor widgetsBundleMsgConstructor; @Lazy @Autowired - private UserUpdateMsgConstructor userUpdateMsgConstructor; + private WidgetTypeMsgConstructor widgetTypeMsgConstructor; @Lazy @Autowired - private RelationUpdateMsgConstructor relationUpdateMsgConstructor; + private AdminSettingsMsgConstructor adminSettingsMsgConstructor; @Lazy @Autowired private EntityDataMsgConstructor entityDataMsgConstructor; + @Lazy + @Autowired + private AlarmProcessor alarmProcessor; + + @Lazy + @Autowired + private DeviceProcessor deviceProcessor; + + @Lazy + @Autowired + private RelationProcessor relationProcessor; + + @Lazy + @Autowired + private TelemetryProcessor telemetryProcessor; + @Lazy @Autowired private EdgeEventStorageSettings edgeEventStorageSettings; diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java index cb90fa4345..936a805ea0 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java @@ -17,38 +17,47 @@ package org.thingsboard.server.service.edge.rpc; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.io.Resources; +import com.google.common.util.concurrent.FutureCallback; import io.grpc.Server; -import io.grpc.ServerBuilder; +import io.grpc.netty.NettyServerBuilder; import io.grpc.stub.StreamObserver; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.DataConstants; +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.kv.BasicTsKvEntry; +import org.thingsboard.server.common.data.kv.BooleanDataEntry; +import org.thingsboard.server.common.data.kv.LongDataEntry; import org.thingsboard.server.gen.edge.EdgeRpcServiceGrpc; import org.thingsboard.server.gen.edge.RequestMsg; import org.thingsboard.server.gen.edge.ResponseMsg; import org.thingsboard.server.service.edge.EdgeContextComponent; +import org.thingsboard.server.service.state.DefaultDeviceStateService; +import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import java.io.File; import java.io.IOException; +import java.util.Collections; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; @Service @Slf4j @ConditionalOnProperty(prefix = "edges.rpc", value = "enabled", havingValue = "true") -public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase { +public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase implements EdgeRpcService { private final Map sessions = new ConcurrentHashMap<>(); - private static final ObjectMapper objectMapper = new ObjectMapper(); + private static final ObjectMapper mapper = new ObjectMapper(); @Value("${edges.rpc.port}") private int rpcPort; @@ -58,10 +67,17 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase { private String certFileResource; @Value("${edges.rpc.ssl.private_key}") private String privateKeyResource; + @Value("${edges.state.persistToTelemetry:false}") + private boolean persistToTelemetry; + @Value("${edges.rpc.client_max_keep_alive_time_sec}") + private int clientMaxKeepAliveTimeSec; @Autowired private EdgeContextComponent ctx; + @Autowired + private TelemetrySubscriptionService tsSubService; + private Server server; private ExecutorService executor; @@ -69,7 +85,9 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase { @PostConstruct public void init() { log.info("Initializing Edge RPC service!"); - ServerBuilder builder = ServerBuilder.forPort(rpcPort).addService(this); + NettyServerBuilder builder = NettyServerBuilder.forPort(rpcPort) + .permitKeepAliveTime(clientMaxKeepAliveTimeSec, TimeUnit.SECONDS) + .addService(this); if (sslEnabled) { try { File certFile = new File(Resources.getResource(certFileResource).toURI()); @@ -102,22 +120,60 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase { @Override public StreamObserver handleMsgs(StreamObserver outputStream) { - return new EdgeGrpcSession(ctx, outputStream, this::onEdgeConnect, this::onEdgeDisconnect, objectMapper).getInputStream(); + return new EdgeGrpcSession(ctx, outputStream, this::onEdgeConnect, this::onEdgeDisconnect, mapper).getInputStream(); + } + + @Override + public void updateEdge(Edge edge) { + EdgeGrpcSession session = sessions.get(edge.getId()); + if (session != null && session.isConnected()) { + session.onConfigurationUpdate(edge); + } + } + + @Override + public void deleteEdge(EdgeId edgeId) { + EdgeGrpcSession session = sessions.get(edgeId); + if (session != null && session.isConnected()) { + session.close(); + sessions.remove(edgeId); + } } private void onEdgeConnect(EdgeId edgeId, EdgeGrpcSession edgeGrpcSession) { sessions.put(edgeId, edgeGrpcSession); + save(edgeId, DefaultDeviceStateService.ACTIVITY_STATE, true); + save(edgeId, DefaultDeviceStateService.LAST_CONNECT_TIME, System.currentTimeMillis()); + } + + public EdgeGrpcSession getEdgeGrpcSessionById(EdgeId edgeId) { + EdgeGrpcSession session = sessions.get(edgeId); + if (session != null && session.isConnected()) { + return session; + } else { + throw new RuntimeException("Edge is not connected"); + } } private void processHandleMessages() { executor.submit(() -> { while (!Thread.interrupted()) { try { - for (EdgeGrpcSession session : sessions.values()) { - session.processHandleMessages(); + if (sessions.size() > 0) { + for (EdgeGrpcSession session : sessions.values()) { + session.processHandleMessages(); + } + } else { + log.trace("No sessions available, sleep for the next run"); + try { + Thread.sleep(1000); + } catch (InterruptedException ignore) {} } } catch (Exception e) { log.warn("Failed to process messages handling!", e); + try { + Thread.sleep(1000); + } catch (InterruptedException ignore) {} } } }); @@ -125,6 +181,51 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase { private void onEdgeDisconnect(EdgeId edgeId) { sessions.remove(edgeId); + save(edgeId, DefaultDeviceStateService.ACTIVITY_STATE, false); + save(edgeId, DefaultDeviceStateService.LAST_DISCONNECT_TIME, System.currentTimeMillis()); } + private void save(EdgeId edgeId, String key, long value) { + if (persistToTelemetry) { + tsSubService.saveAndNotify( + TenantId.SYS_TENANT_ID, edgeId, + Collections.singletonList(new BasicTsKvEntry(System.currentTimeMillis(), new LongDataEntry(key, value))), + new AttributeSaveCallback(edgeId, key, value)); + } else { + tsSubService.saveAttrAndNotify(TenantId.SYS_TENANT_ID, edgeId, DataConstants.SERVER_SCOPE, key, value, new AttributeSaveCallback(edgeId, key, value)); + } + } + + private void save(EdgeId edgeId, String key, boolean value) { + if (persistToTelemetry) { + tsSubService.saveAndNotify( + TenantId.SYS_TENANT_ID, edgeId, + Collections.singletonList(new BasicTsKvEntry(System.currentTimeMillis(), new BooleanDataEntry(key, value))), + new AttributeSaveCallback(edgeId, key, value)); + } else { + tsSubService.saveAttrAndNotify(TenantId.SYS_TENANT_ID, edgeId, DataConstants.SERVER_SCOPE, key, value, new AttributeSaveCallback(edgeId, key, value)); + } + } + + private static class AttributeSaveCallback implements FutureCallback { + private final EdgeId edgeId; + private final String key; + private final Object value; + + AttributeSaveCallback(EdgeId edgeId, String key, Object value) { + this.edgeId = edgeId; + this.key = key; + this.value = value; + } + + @Override + public void onSuccess(@javax.annotation.Nullable Void result) { + log.trace("[{}] Successfully updated attribute [{}] with value [{}]", edgeId, key, value); + } + + @Override + public void onFailure(Throwable t) { + log.warn("[{}] Failed to update attribute [{}] with value [{}]", edgeId, key, value, t); + } + } } 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 892523ac06..305fdef310 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 @@ -18,33 +18,32 @@ package org.thingsboard.server.service.edge.rpc; import com.datastax.oss.driver.api.core.uuid.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.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.checkerframework.checker.nullness.qual.Nullable; +import org.thingsboard.server.common.data.AdminSettings; +import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.DataConstants; 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.HasCustomerId; 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.EdgeEvent; +import org.thingsboard.server.common.data.edge.EdgeEventType; import org.thingsboard.server.common.data.id.AlarmId; 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; @@ -53,70 +52,83 @@ 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.id.WidgetTypeId; +import org.thingsboard.server.common.data.id.WidgetsBundleId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; -import org.thingsboard.server.common.data.kv.DataType; import org.thingsboard.server.common.data.kv.LongDataEntry; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.SortOrder; 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.security.DeviceCredentials; -import org.thingsboard.server.common.data.security.DeviceCredentialsType; -import org.thingsboard.server.common.msg.TbMsg; -import org.thingsboard.server.common.msg.TbMsgMetaData; -import org.thingsboard.server.common.msg.session.SessionMsgType; +import org.thingsboard.server.common.data.security.UserCredentials; +import org.thingsboard.server.common.data.widget.WidgetType; +import org.thingsboard.server.common.data.widget.WidgetsBundle; import org.thingsboard.server.common.transport.util.JsonUtils; +import org.thingsboard.server.gen.edge.AdminSettingsUpdateMsg; import org.thingsboard.server.gen.edge.AlarmUpdateMsg; +import org.thingsboard.server.gen.edge.AssetUpdateMsg; +import org.thingsboard.server.gen.edge.AttributesRequestMsg; 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.DashboardUpdateMsg; +import org.thingsboard.server.gen.edge.DeviceCredentialsRequestMsg; +import org.thingsboard.server.gen.edge.DeviceCredentialsUpdateMsg; +import org.thingsboard.server.gen.edge.DeviceRpcCallMsg; import org.thingsboard.server.gen.edge.DeviceUpdateMsg; import org.thingsboard.server.gen.edge.DownlinkMsg; +import org.thingsboard.server.gen.edge.DownlinkResponseMsg; import org.thingsboard.server.gen.edge.EdgeConfiguration; +import org.thingsboard.server.gen.edge.EdgeUpdateMsg; import org.thingsboard.server.gen.edge.EntityDataProto; -import org.thingsboard.server.gen.edge.EntityUpdateMsg; +import org.thingsboard.server.gen.edge.EntityViewUpdateMsg; +import org.thingsboard.server.gen.edge.RelationRequestMsg; +import org.thingsboard.server.gen.edge.RelationUpdateMsg; import org.thingsboard.server.gen.edge.RequestMsg; import org.thingsboard.server.gen.edge.RequestMsgType; import org.thingsboard.server.gen.edge.ResponseMsg; 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.UplinkMsg; import org.thingsboard.server.gen.edge.UplinkResponseMsg; -import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.gen.edge.UserCredentialsRequestMsg; +import org.thingsboard.server.gen.edge.UserCredentialsUpdateMsg; +import org.thingsboard.server.gen.edge.WidgetTypeUpdateMsg; +import org.thingsboard.server.gen.edge.WidgetsBundleUpdateMsg; import org.thingsboard.server.service.edge.EdgeContextComponent; import java.io.Closeable; -import java.io.IOException; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.UUID; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; import java.util.function.BiConsumer; import java.util.function.Consumer; -import static org.thingsboard.server.gen.edge.UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE; - @Slf4j @Data public final class EdgeGrpcSession implements Closeable { - private static final ReentrantLock deviceCreationLock = new ReentrantLock(); - - private final Gson gson = new Gson(); + private static final ReentrantLock responseMsgLock = new ReentrantLock(); private static final String QUEUE_START_TS_ATTR_KEY = "queueStartTs"; private final UUID sessionId; private final BiConsumer sessionOpenListener; private final Consumer sessionCloseListener; - private final ObjectMapper objectMapper; + private final ObjectMapper mapper; private EdgeContextComponent ctx; private Edge edge; @@ -124,14 +136,16 @@ public final class EdgeGrpcSession implements Closeable { private StreamObserver outputStream; private boolean connected; + private CountDownLatch latch; + EdgeGrpcSession(EdgeContextComponent ctx, StreamObserver outputStream, BiConsumer sessionOpenListener, - Consumer sessionCloseListener, ObjectMapper objectMapper) { + Consumer sessionCloseListener, ObjectMapper mapper) { this.sessionId = UUID.randomUUID(); this.ctx = ctx; this.outputStream = outputStream; this.sessionOpenListener = sessionOpenListener; this.sessionCloseListener = sessionCloseListener; - this.objectMapper = objectMapper; + this.mapper = mapper; initInputStream(); } @@ -147,15 +161,17 @@ public final class EdgeGrpcSession implements Closeable { if (ConnectResponseCode.ACCEPTED != responseMsg.getResponseCode()) { outputStream.onError(new RuntimeException(responseMsg.getErrorMsg())); } - if (ConnectResponseCode.ACCEPTED == responseMsg.getResponseCode()) { - ctx.getSyncEdgeService().sync(ctx, edge, outputStream); - } + } + if (!connected && requestMsg.getMsgType().equals(RequestMsgType.SYNC_REQUEST_RPC_MESSAGE)) { + connected = true; + ctx.getSyncEdgeService().sync(edge); } if (connected) { if (requestMsg.getMsgType().equals(RequestMsgType.UPLINK_RPC_MESSAGE) && requestMsg.hasUplinkMsg()) { - outputStream.onNext(ResponseMsg.newBuilder() - .setUplinkResponseMsg(processUplinkMsg(requestMsg.getUplinkMsg())) - .build()); + onUplinkMsg(requestMsg.getUplinkMsg()); + } + if (requestMsg.getMsgType().equals(RequestMsgType.UPLINK_RPC_MESSAGE) && requestMsg.hasDownlinkResponseMsg()) { + onDownlinkResponse(requestMsg.getDownlinkResponseMsg()); } } } @@ -167,71 +183,232 @@ public final class EdgeGrpcSession implements Closeable { @Override public void onCompleted() { - sessionCloseListener.accept(edge.getId()); - outputStream.onCompleted(); + connected = false; + if (edge != null) { + sessionCloseListener.accept(edge.getId()); + } + try { + outputStream.onCompleted(); + } catch (Exception ignored) {} } }; } + private void onUplinkMsg(UplinkMsg uplinkMsg) { + ListenableFuture> future = processUplinkMsg(uplinkMsg); + Futures.addCallback(future, new FutureCallback>() { + @Override + public void onSuccess(@Nullable List result) { + UplinkResponseMsg uplinkResponseMsg = UplinkResponseMsg.newBuilder().setSuccess(true).build(); + sendResponseMsg(ResponseMsg.newBuilder() + .setUplinkResponseMsg(uplinkResponseMsg) + .build()); + } + + @Override + public void onFailure(Throwable t) { + UplinkResponseMsg uplinkResponseMsg = UplinkResponseMsg.newBuilder().setSuccess(false).setErrorMsg(t.getMessage()).build(); + sendResponseMsg(ResponseMsg.newBuilder() + .setUplinkResponseMsg(uplinkResponseMsg) + .build()); + } + }, MoreExecutors.directExecutor()); + } + + private void onDownlinkResponse(DownlinkResponseMsg msg) { + try { + if (msg.getSuccess()) { + log.debug("[{}] Msg has been processed successfully! {}", edge.getRoutingKey(), msg); + } else { + log.error("[{}] Msg processing failed! Error msg: {}", edge.getRoutingKey(), msg.getErrorMsg()); + } + latch.countDown(); + } catch (Exception e) { + log.error("Can't process downlink response message [{}]", msg, e); + } + } + + private void sendResponseMsg(ResponseMsg responseMsg) { + if (isConnected()) { + try { + responseMsgLock.lock(); + outputStream.onNext(responseMsg); + } catch (Exception e) { + log.error("Failed to send response message [{}]", responseMsg, e); + connected = false; + sessionCloseListener.accept(edge.getId()); + } finally { + responseMsgLock.unlock(); + } + } + } + + void onConfigurationUpdate(Edge edge) { + try { + this.edge = edge; + EdgeUpdateMsg edgeConfig = EdgeUpdateMsg.newBuilder() + .setConfiguration(constructEdgeConfigProto(edge)).build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEdgeUpdateMsg(edgeConfig) + .build()); + } catch (Exception e) { + log.error("Failed to construct proto objects!", e); + } + } + void processHandleMessages() throws ExecutionException, InterruptedException { - Long queueStartTs = getQueueStartTs().get(); - TimePageLink pageLink = new TimePageLink(ctx.getEdgeEventStorageSettings().getMaxReadRecordsCount(), 0, "", new SortOrder("createdTime"), queueStartTs, null); - PageData pageData; - UUID ifOffset = null; - do { - 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 (EdgeEvent edgeEvent : pageData.getData()) { - log.trace("[{}] Processing edge event [{}]", this.sessionId, edgeEvent); + if (isConnected()) { + Integer queueStartTs = getQueueStartTs().get(); + TimePageLink pageLink = new TimePageLink( + ctx.getEdgeEventStorageSettings().getMaxReadRecordsCount(), + queueStartTs, + null, + new SortOrder("createdTime", SortOrder.Direction.ASC)); + PageData pageData; + UUID ifOffset = null; + boolean success = true; + do { + 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()); + List downlinkMsgsPack = convertToDownlinkMsgsPack(pageData.getData()); + log.trace("[{}] downlink msg(s) are going to be send.", downlinkMsgsPack.size()); + + latch = new CountDownLatch(downlinkMsgsPack.size()); + for (DownlinkMsg downlinkMsg : downlinkMsgsPack) { + sendResponseMsg(ResponseMsg.newBuilder() + .setDownlinkMsg(downlinkMsg) + .build()); + } + + ifOffset = pageData.getData().get(pageData.getData().size() - 1).getUuidId(); + + success = latch.await(10, TimeUnit.SECONDS); + if (!success) { + log.warn("Failed to deliver the batch: {}", downlinkMsgsPack); + } + } + if (isConnected() && (!success || pageData.hasNext())) { try { - UpdateMsgType msgType = getResponseMsgType(ActionType.valueOf(edgeEvent.getEdgeEventAction())); - if (msgType == null) { - processTelemetryMessage(edgeEvent); - } else { - processEntityCRUDMessage(edgeEvent, msgType); - if (ENTITY_CREATED_RPC_MESSAGE.equals(msgType)) { - pushEntityAttributesToEdge(edgeEvent); - } - } - } catch (Exception e) { - log.error("Exception during processing records from queue", e); + Thread.sleep(ctx.getEdgeEventStorageSettings().getSleepIntervalBetweenBatches()); + } catch (InterruptedException e) { + log.error("Error during sleep between batches", e); + } + if (success) { + pageLink = pageLink.nextPageLink(); } - ifOffset = edgeEvent.getUuidId(); } + } while (isConnected() && (!success || pageData.hasNext())); + + if (ifOffset != null) { + Long newStartTs = Uuids.unixTimestamp(ifOffset); + updateQueueStartTs(newStartTs); } - if (isConnected() && pageData.hasNext()) { - pageLink = pageLink.nextPageLink(); - try { - Thread.sleep(ctx.getEdgeEventStorageSettings().getSleepIntervalBetweenBatches()); - } catch (InterruptedException e) { - log.error("Error during sleep between batches", e); - } + try { + Thread.sleep(ctx.getEdgeEventStorageSettings().getNoRecordsSleepInterval()); + } catch (InterruptedException e) { + log.error("Error during sleep", e); } - } while (isConnected() && pageData.hasNext()); + } + } - if (ifOffset != null) { - Long newStartTs = Uuids.unixTimestamp(ifOffset); - updateQueueStartTs(newStartTs); + private List convertToDownlinkMsgsPack(List edgeEvents) { + List result = new ArrayList<>(); + for (EdgeEvent edgeEvent : edgeEvents) { + log.trace("Processing edge event [{}]", edgeEvent); + try { + DownlinkMsg downlinkMsg = null; + ActionType action = ActionType.valueOf(edgeEvent.getAction()); + switch (action) { + case UPDATED: + case ADDED: + case DELETED: + case ASSIGNED_TO_EDGE: + case UNASSIGNED_FROM_EDGE: + case ALARM_ACK: + case ALARM_CLEAR: + case CREDENTIALS_UPDATED: + case RELATION_ADD_OR_UPDATE: + case RELATION_DELETED: + case ASSIGNED_TO_CUSTOMER: + case UNASSIGNED_FROM_CUSTOMER: + downlinkMsg = processEntityMessage(edgeEvent, action); + break; + case ATTRIBUTES_UPDATED: + case ATTRIBUTES_DELETED: + case TIMESERIES_UPDATED: + downlinkMsg = processTelemetryMessage(edgeEvent); + break; + case CREDENTIALS_REQUEST: + downlinkMsg = processCredentialsRequestMessage(edgeEvent); + break; + case ENTITY_EXISTS_REQUEST: + downlinkMsg = processEntityExistsRequestMessage(edgeEvent); + break; + case RPC_CALL: + downlinkMsg = processRpcCallMsg(edgeEvent); + break; + } + if (downlinkMsg != null) { + result.add(downlinkMsg); + } + } catch (Exception e) { + log.error("Exception during processing records from queue", e); + } } - try { - Thread.sleep(ctx.getEdgeEventStorageSettings().getNoRecordsSleepInterval()); - } catch (InterruptedException e) { - log.error("Error during sleep", e); + return result; + } + + private DownlinkMsg processEntityExistsRequestMessage(EdgeEvent edgeEvent) { + DownlinkMsg downlinkMsg = null; + if (EdgeEventType.DEVICE.equals(edgeEvent.getType())) { + DeviceId deviceId = new DeviceId(edgeEvent.getEntityId()); + Device device = ctx.getDeviceService().findDeviceById(edge.getTenantId(), deviceId); + CustomerId customerId = getCustomerIdIfEdgeAssignedToCustomer(device); + DeviceUpdateMsg d = ctx.getDeviceMsgConstructor().constructDeviceUpdatedMsg(UpdateMsgType.DEVICE_CONFLICT_RPC_MESSAGE, device, customerId); + downlinkMsg = DownlinkMsg.newBuilder() + .addAllDeviceUpdateMsg(Collections.singletonList(d)) + .build(); } + return downlinkMsg; } - private ListenableFuture getQueueStartTs() { + private DownlinkMsg processRpcCallMsg(EdgeEvent edgeEvent) { + log.trace("Executing processRpcCall, edgeEvent [{}]", edgeEvent); + DeviceRpcCallMsg deviceRpcCallMsg = + ctx.getDeviceMsgConstructor().constructDeviceRpcCallMsg(edgeEvent.getBody()); + return DownlinkMsg.newBuilder() + .addAllDeviceRpcCallMsg(Collections.singletonList(deviceRpcCallMsg)) + .build(); + } + + private DownlinkMsg processCredentialsRequestMessage(EdgeEvent edgeEvent) { + DownlinkMsg downlinkMsg = null; + if (EdgeEventType.DEVICE.equals(edgeEvent.getType())) { + DeviceId deviceId = new DeviceId(edgeEvent.getEntityId()); + DeviceCredentialsRequestMsg deviceCredentialsRequestMsg = DeviceCredentialsRequestMsg.newBuilder() + .setDeviceIdMSB(deviceId.getId().getMostSignificantBits()) + .setDeviceIdLSB(deviceId.getId().getLeastSignificantBits()) + .build(); + DownlinkMsg.Builder builder = DownlinkMsg.newBuilder() + .addAllDeviceCredentialsRequestMsg(Collections.singletonList(deviceCredentialsRequestMsg)); + downlinkMsg = builder.build(); + } + return downlinkMsg; + } + + + 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; + return attributeKvEntry.getLongValue().isPresent() ? attributeKvEntry.getLongValue().get().intValue() : 0; } else { - return 0L; + return 0; } - }, MoreExecutors.directExecutor()); + }, ctx.getDbCallbackExecutor()); } private void updateQueueStartTs(Long newStartTs) { @@ -240,12 +417,10 @@ public final class EdgeGrpcSession implements Closeable { ctx.getAttributesService().save(edge.getTenantId(), edge.getId(), DataConstants.SERVER_SCOPE, attributes); } - private void pushEntityAttributesToEdge(EdgeEvent edgeEvent) throws IOException { + private DownlinkMsg processTelemetryMessage(EdgeEvent edgeEvent) { + log.trace("Executing processTelemetryMessage, edgeEvent [{}]", edgeEvent); EntityId entityId = null; - switch (edgeEvent.getEdgeEventType()) { - case EDGE: - entityId = edge.getId(); - break; + switch (edgeEvent.getType()) { case DEVICE: entityId = new DeviceId(edgeEvent.getEntityId()); break; @@ -258,417 +433,414 @@ public final class EdgeGrpcSession implements Closeable { case DASHBOARD: entityId = new DashboardId(edgeEvent.getEntityId()); break; - } - if (entityId != null) { - final EntityId finalEntityId = entityId; - ListenableFuture> ssAttrFuture = ctx.getAttributesService().findAll(edge.getTenantId(), entityId, DataConstants.SERVER_SCOPE); - Futures.transform(ssAttrFuture, ssAttributes -> { - if (ssAttributes != null && !ssAttributes.isEmpty()) { - try { - ObjectNode entityNode = objectMapper.createObjectNode(); - for (AttributeKvEntry attr : ssAttributes) { - if (attr.getDataType() == DataType.BOOLEAN && attr.getBooleanValue().isPresent()) { - entityNode.put(attr.getKey(), attr.getBooleanValue().get()); - } else if (attr.getDataType() == DataType.DOUBLE && attr.getDoubleValue().isPresent()) { - entityNode.put(attr.getKey(), attr.getDoubleValue().get()); - } else if (attr.getDataType() == DataType.LONG && attr.getLongValue().isPresent()) { - entityNode.put(attr.getKey(), attr.getLongValue().get()); - } else { - entityNode.put(attr.getKey(), attr.getValueAsString()); - } - } - 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(value).build()); - } catch (Exception e) { - log.error("[{}] Failed to send attribute updates to the edge", edge.getName(), e); - } - } - return null; - }, MoreExecutors.directExecutor()); - ListenableFuture> shAttrFuture = ctx.getAttributesService().findAll(edge.getTenantId(), entityId, DataConstants.SHARED_SCOPE); - ListenableFuture> clAttrFuture = ctx.getAttributesService().findAll(edge.getTenantId(), entityId, DataConstants.CLIENT_SCOPE); - } - } - - private void processTelemetryMessage(EdgeEvent edgeEvent) throws IOException { - log.trace("Executing processTelemetryMessage, edgeEvent [{}]", edgeEvent); - EntityId entityId = null; - switch (edgeEvent.getEdgeEventType()) { - case DEVICE: - entityId = new DeviceId(edgeEvent.getEntityId()); - break; - case ASSET: - entityId = new AssetId(edgeEvent.getEntityId()); + case TENANT: + entityId = new TenantId(edgeEvent.getEntityId()); break; - case ENTITY_VIEW: - entityId = new EntityViewId(edgeEvent.getEntityId()); + case CUSTOMER: + entityId = new CustomerId(edgeEvent.getEntityId()); break; - } + DownlinkMsg downlinkMsg = null; if (entityId != null) { - log.debug("Sending telemetry data msg, entityId [{}], body [{}]", edgeEvent.getEntityId(), edgeEvent.getEntityBody()); - DownlinkMsg downlinkMsg; + log.debug("Sending telemetry data msg, entityId [{}], body [{}]", edgeEvent.getEntityId(), edgeEvent.getBody()); try { - ActionType actionType = ActionType.valueOf(edgeEvent.getEdgeEventAction()); - downlinkMsg = constructEntityDataProtoMsg(entityId, actionType, JsonUtils.parse(objectMapper.writeValueAsString(edgeEvent.getEntityBody()))); - outputStream.onNext(ResponseMsg.newBuilder() - .setDownlinkMsg(downlinkMsg) - .build()); + ActionType actionType = ActionType.valueOf(edgeEvent.getAction()); + downlinkMsg = constructEntityDataProtoMsg(entityId, actionType, JsonUtils.parse(mapper.writeValueAsString(edgeEvent.getBody()))); } catch (Exception e) { - log.warn("Can't send telemetry data msg, entityId [{}], body [{}]", edgeEvent.getEntityId(), edgeEvent.getEntityBody(), e); + log.warn("Can't send telemetry data msg, entityId [{}], body [{}]", edgeEvent.getEntityId(), edgeEvent.getBody(), e); } - } + return downlinkMsg; } - private void processEntityCRUDMessage(EdgeEvent edgeEvent, UpdateMsgType msgType) { - log.trace("Executing processEntityCRUDMessage, edgeEvent [{}], msgType [{}]", edgeEvent, msgType); - switch (edgeEvent.getEdgeEventType()) { + private DownlinkMsg processEntityMessage(EdgeEvent edgeEvent, ActionType action) { + UpdateMsgType msgType = getResponseMsgType(ActionType.valueOf(edgeEvent.getAction())); + log.trace("Executing processEntityMessage, edgeEvent [{}], action [{}], msgType [{}]", edgeEvent, action, msgType); + switch (edgeEvent.getType()) { case EDGE: // TODO: voba - add edge update logic - break; + return null; case DEVICE: - processDeviceCRUD(edgeEvent, msgType); - break; + return processDevice(edgeEvent, msgType, action); case ASSET: - processAssetCRUD(edgeEvent, msgType); - break; + return processAsset(edgeEvent, msgType, action); case ENTITY_VIEW: - processEntityViewCRUD(edgeEvent, msgType); - break; + return processEntityView(edgeEvent, msgType, action); case DASHBOARD: - processDashboardCRUD(edgeEvent, msgType); - break; + return processDashboard(edgeEvent, msgType, action); + case CUSTOMER: + return processCustomer(edgeEvent, msgType, action); case RULE_CHAIN: - processRuleChainCRUD(edgeEvent, msgType); - break; + return processRuleChain(edgeEvent, msgType, action); case RULE_CHAIN_METADATA: - processRuleChainMetadataCRUD(edgeEvent, msgType); - break; + return processRuleChainMetadata(edgeEvent, msgType); case ALARM: - processAlarmCRUD(edgeEvent, msgType); - break; + return processAlarm(edgeEvent, msgType); case USER: - processUserCRUD(edgeEvent, msgType); - break; + return processUser(edgeEvent, msgType, action); case RELATION: - processRelationCRUD(edgeEvent, msgType); - break; + return processRelation(edgeEvent, msgType); + case WIDGETS_BUNDLE: + return processWidgetsBundle(edgeEvent, msgType, action); + case WIDGET_TYPE: + return processWidgetType(edgeEvent, msgType, action); + case ADMIN_SETTINGS: + return processAdminSettings(edgeEvent); + default: + log.warn("Unsupported edge event type [{}]", edgeEvent); + return null; } } - private void processDeviceCRUD(EdgeEvent edgeEvent, UpdateMsgType msgType) { + private DownlinkMsg processDevice(EdgeEvent edgeEvent, UpdateMsgType msgType, ActionType edgeActionType) { DeviceId deviceId = new DeviceId(edgeEvent.getEntityId()); - 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()); + DownlinkMsg downlinkMsg = null; + switch (edgeActionType) { + case ADDED: + case UPDATED: + case ASSIGNED_TO_EDGE: + case ASSIGNED_TO_CUSTOMER: + case UNASSIGNED_FROM_CUSTOMER: + Device device = ctx.getDeviceService().findDeviceById(edgeEvent.getTenantId(), deviceId); + if (device != null) { + CustomerId customerId = getCustomerIdIfEdgeAssignedToCustomer(device); + DeviceUpdateMsg deviceUpdateMsg = + ctx.getDeviceMsgConstructor().constructDeviceUpdatedMsg(msgType, device, customerId); + downlinkMsg = DownlinkMsg.newBuilder() + .addAllDeviceUpdateMsg(Collections.singletonList(deviceUpdateMsg)) + .build(); + } break; - case ENTITY_DELETED_RPC_MESSAGE: - EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() - .setDeviceUpdateMsg(ctx.getDeviceUpdateMsgConstructor().constructDeviceDeleteMsg(deviceId)) + case DELETED: + case UNASSIGNED_FROM_EDGE: + DeviceUpdateMsg deviceUpdateMsg = + ctx.getDeviceMsgConstructor().constructDeviceDeleteMsg(deviceId); + downlinkMsg = DownlinkMsg.newBuilder() + .addAllDeviceUpdateMsg(Collections.singletonList(deviceUpdateMsg)) .build(); - outputStream.onNext(ResponseMsg.newBuilder() - .setEntityUpdateMsg(entityUpdateMsg) - .build()); + break; + case CREDENTIALS_UPDATED: + DeviceCredentials deviceCredentials = ctx.getDeviceCredentialsService().findDeviceCredentialsByDeviceId(edge.getTenantId(), deviceId); + if (deviceCredentials != null) { + DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg = + ctx.getDeviceMsgConstructor().constructDeviceCredentialsUpdatedMsg(deviceCredentials); + downlinkMsg = DownlinkMsg.newBuilder() + .addAllDeviceCredentialsUpdateMsg(Collections.singletonList(deviceCredentialsUpdateMsg)) + .build(); + } + break; } - - + return downlinkMsg; } - private void processAssetCRUD(EdgeEvent edgeEvent, UpdateMsgType msgType) { + private DownlinkMsg processAsset(EdgeEvent edgeEvent, UpdateMsgType msgType, ActionType action) { AssetId assetId = new AssetId(edgeEvent.getEntityId()); - 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()); + DownlinkMsg downlinkMsg = null; + switch (action) { + case ADDED: + case UPDATED: + case ASSIGNED_TO_EDGE: + case ASSIGNED_TO_CUSTOMER: + case UNASSIGNED_FROM_CUSTOMER: + Asset asset = ctx.getAssetService().findAssetById(edgeEvent.getTenantId(), assetId); + if (asset != null) { + CustomerId customerId = getCustomerIdIfEdgeAssignedToCustomer(asset); + AssetUpdateMsg assetUpdateMsg = + ctx.getAssetMsgConstructor().constructAssetUpdatedMsg(msgType, asset, customerId); + downlinkMsg = DownlinkMsg.newBuilder() + .addAllAssetUpdateMsg(Collections.singletonList(assetUpdateMsg)) + .build(); + } break; - case ENTITY_DELETED_RPC_MESSAGE: - EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() - .setAssetUpdateMsg(ctx.getAssetUpdateMsgConstructor().constructAssetDeleteMsg(assetId)) + case DELETED: + case UNASSIGNED_FROM_EDGE: + AssetUpdateMsg assetUpdateMsg = + ctx.getAssetMsgConstructor().constructAssetDeleteMsg(assetId); + downlinkMsg = DownlinkMsg.newBuilder() + .addAllAssetUpdateMsg(Collections.singletonList(assetUpdateMsg)) .build(); - outputStream.onNext(ResponseMsg.newBuilder() - .setEntityUpdateMsg(entityUpdateMsg) - .build()); break; } + return downlinkMsg; } - private void processEntityViewCRUD(EdgeEvent edgeEvent, UpdateMsgType msgType) { + private DownlinkMsg processEntityView(EdgeEvent edgeEvent, UpdateMsgType msgType, ActionType action) { EntityViewId entityViewId = new EntityViewId(edgeEvent.getEntityId()); - 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()); + DownlinkMsg downlinkMsg = null; + switch (action) { + case ADDED: + case UPDATED: + case ASSIGNED_TO_EDGE: + case ASSIGNED_TO_CUSTOMER: + case UNASSIGNED_FROM_CUSTOMER: + EntityView entityView = ctx.getEntityViewService().findEntityViewById(edgeEvent.getTenantId(), entityViewId); + if (entityView != null) { + CustomerId customerId = getCustomerIdIfEdgeAssignedToCustomer(entityView); + EntityViewUpdateMsg entityViewUpdateMsg = + ctx.getEntityViewMsgConstructor().constructEntityViewUpdatedMsg(msgType, entityView, customerId); + downlinkMsg = DownlinkMsg.newBuilder() + .addAllEntityViewUpdateMsg(Collections.singletonList(entityViewUpdateMsg)) + .build(); + } break; - case ENTITY_DELETED_RPC_MESSAGE: - EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() - .setEntityViewUpdateMsg(ctx.getEntityViewUpdateMsgConstructor().constructEntityViewDeleteMsg(entityViewId)) + case DELETED: + case UNASSIGNED_FROM_EDGE: + EntityViewUpdateMsg entityViewUpdateMsg = + ctx.getEntityViewMsgConstructor().constructEntityViewDeleteMsg(entityViewId); + downlinkMsg = DownlinkMsg.newBuilder() + .addAllEntityViewUpdateMsg(Collections.singletonList(entityViewUpdateMsg)) .build(); - outputStream.onNext(ResponseMsg.newBuilder() - .setEntityUpdateMsg(entityUpdateMsg) - .build()); break; } + return downlinkMsg; } - private void processDashboardCRUD(EdgeEvent edgeEvent, UpdateMsgType msgType) { + private DownlinkMsg processDashboard(EdgeEvent edgeEvent, UpdateMsgType msgType, ActionType action) { DashboardId dashboardId = new DashboardId(edgeEvent.getEntityId()); - 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()); + DownlinkMsg downlinkMsg = null; + switch (action) { + case ADDED: + case UPDATED: + case ASSIGNED_TO_EDGE: + case ASSIGNED_TO_CUSTOMER: + case UNASSIGNED_FROM_CUSTOMER: + Dashboard dashboard = ctx.getDashboardService().findDashboardById(edgeEvent.getTenantId(), dashboardId); + if (dashboard != null) { + CustomerId customerId = null; + if (!edge.getCustomerId().isNullUid() && dashboard.isAssignedToCustomer(edge.getCustomerId())) { + customerId = edge.getCustomerId(); + } + DashboardUpdateMsg dashboardUpdateMsg = + ctx.getDashboardMsgConstructor().constructDashboardUpdatedMsg(msgType, dashboard, customerId); + downlinkMsg = DownlinkMsg.newBuilder() + .addAllDashboardUpdateMsg(Collections.singletonList(dashboardUpdateMsg)) + .build(); + } break; - case ENTITY_DELETED_RPC_MESSAGE: - EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() - .setDashboardUpdateMsg(ctx.getDashboardUpdateMsgConstructor().constructDashboardDeleteMsg(dashboardId)) + case DELETED: + case UNASSIGNED_FROM_EDGE: + DashboardUpdateMsg dashboardUpdateMsg = + ctx.getDashboardMsgConstructor().constructDashboardDeleteMsg(dashboardId); + downlinkMsg = DownlinkMsg.newBuilder() + .addAllDashboardUpdateMsg(Collections.singletonList(dashboardUpdateMsg)) .build(); - outputStream.onNext(ResponseMsg.newBuilder() - .setEntityUpdateMsg(entityUpdateMsg) - .build()); break; } + return downlinkMsg; } - private void processRuleChainCRUD(EdgeEvent edgeEvent, UpdateMsgType msgType) { - RuleChainId ruleChainId = new RuleChainId(edgeEvent.getEntityId()); - 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()); + private DownlinkMsg processCustomer(EdgeEvent edgeEvent, UpdateMsgType msgType, ActionType action) { + CustomerId customerId = new CustomerId(edgeEvent.getEntityId()); + DownlinkMsg downlinkMsg = null; + switch (action) { + case ADDED: + case UPDATED: + Customer customer = ctx.getCustomerService().findCustomerById(edgeEvent.getTenantId(), customerId); + if (customer != null) { + CustomerUpdateMsg customerUpdateMsg = + ctx.getCustomerMsgConstructor().constructCustomerUpdatedMsg(msgType, customer); + downlinkMsg = DownlinkMsg.newBuilder() + .addAllCustomerUpdateMsg(Collections.singletonList(customerUpdateMsg)) + .build(); + } break; - case ENTITY_DELETED_RPC_MESSAGE: - EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() - .setRuleChainUpdateMsg(ctx.getRuleChainUpdateMsgConstructor().constructRuleChainDeleteMsg(ruleChainId)) + case DELETED: + CustomerUpdateMsg customerUpdateMsg = + ctx.getCustomerMsgConstructor().constructCustomerDeleteMsg(customerId); + downlinkMsg = DownlinkMsg.newBuilder() + .addAllCustomerUpdateMsg(Collections.singletonList(customerUpdateMsg)) .build(); - outputStream.onNext(ResponseMsg.newBuilder() - .setEntityUpdateMsg(entityUpdateMsg) - .build()); break; } + return downlinkMsg; } - private void processRuleChainMetadataCRUD(EdgeEvent edgeEvent, UpdateMsgType msgType) { + private DownlinkMsg processRuleChain(EdgeEvent edgeEvent, UpdateMsgType msgType, ActionType action) { 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()); - } - } - } + DownlinkMsg downlinkMsg = null; + switch (action) { + case ADDED: + case UPDATED: + case ASSIGNED_TO_EDGE: + RuleChain ruleChain = ctx.getRuleChainService().findRuleChainById(edgeEvent.getTenantId(), ruleChainId); + if (ruleChain != null) { + RuleChainUpdateMsg ruleChainUpdateMsg = + ctx.getRuleChainMsgConstructor().constructRuleChainUpdatedMsg(edge.getRootRuleChainId(), msgType, ruleChain); + downlinkMsg = DownlinkMsg.newBuilder() + .addAllRuleChainUpdateMsg(Collections.singletonList(ruleChainUpdateMsg)) + .build(); + } + break; + case DELETED: + case UNASSIGNED_FROM_EDGE: + downlinkMsg = DownlinkMsg.newBuilder() + .addAllRuleChainUpdateMsg(Collections.singletonList(ctx.getRuleChainMsgConstructor().constructRuleChainDeleteMsg(ruleChainId))) + .build(); + break; + } + return downlinkMsg; + } - @Override - public void onFailure(Throwable t) { - log.warn("Can't processRuleChainMetadataCRUD, edgeEvent [{}]", edgeEvent, t); - } - }, ctx.getDbCallbackExecutor()); + private DownlinkMsg processRuleChainMetadata(EdgeEvent edgeEvent, UpdateMsgType msgType) { + RuleChainId ruleChainId = new RuleChainId(edgeEvent.getEntityId()); + RuleChain ruleChain = ctx.getRuleChainService().findRuleChainById(edgeEvent.getTenantId(), ruleChainId); + DownlinkMsg downlinkMsg = null; + if (ruleChain != null) { + RuleChainMetaData ruleChainMetaData = ctx.getRuleChainService().loadRuleChainMetaData(edgeEvent.getTenantId(), ruleChainId); + RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = + ctx.getRuleChainMsgConstructor().constructRuleChainMetadataUpdatedMsg(msgType, ruleChainMetaData); + if (ruleChainMetadataUpdateMsg != null) { + downlinkMsg = DownlinkMsg.newBuilder() + .addAllRuleChainMetadataUpdateMsg(Collections.singletonList(ruleChainMetadataUpdateMsg)) + .build(); + } + } + return downlinkMsg; } - private void processUserCRUD(EdgeEvent edgeEvent, UpdateMsgType msgType) { + private DownlinkMsg processUser(EdgeEvent edgeEvent, UpdateMsgType msgType, ActionType edgeActionType) { UserId userId = new UserId(edgeEvent.getEntityId()); - 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()); + DownlinkMsg downlinkMsg = null; + switch (edgeActionType) { + case ADDED: + case UPDATED: + User user = ctx.getUserService().findUserById(edgeEvent.getTenantId(), userId); + if (user != null) { + CustomerId customerId = getCustomerIdIfEdgeAssignedToCustomer(user); + downlinkMsg = DownlinkMsg.newBuilder() + .addAllUserUpdateMsg(Collections.singletonList(ctx.getUserMsgConstructor().constructUserUpdatedMsg(msgType, user, customerId))) + .build(); + } break; - case ENTITY_DELETED_RPC_MESSAGE: - EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() - .setUserUpdateMsg(ctx.getUserUpdateMsgConstructor().constructUserDeleteMsg(userId)) + case DELETED: + downlinkMsg = DownlinkMsg.newBuilder() + .addAllUserUpdateMsg(Collections.singletonList(ctx.getUserMsgConstructor().constructUserDeleteMsg(userId))) .build(); - outputStream.onNext(ResponseMsg.newBuilder() - .setEntityUpdateMsg(entityUpdateMsg) - .build()); break; + case CREDENTIALS_UPDATED: + UserCredentials userCredentialsByUserId = ctx.getUserService().findUserCredentialsByUserId(edge.getTenantId(), userId); + if (userCredentialsByUserId != null && userCredentialsByUserId.isEnabled()) { + UserCredentialsUpdateMsg userCredentialsUpdateMsg = + ctx.getUserMsgConstructor().constructUserCredentialsUpdatedMsg(userCredentialsByUserId); + downlinkMsg = DownlinkMsg.newBuilder() + .addAllUserCredentialsUpdateMsg(Collections.singletonList(userCredentialsUpdateMsg)) + .build(); + } + } + return downlinkMsg; + } + + private CustomerId getCustomerIdIfEdgeAssignedToCustomer(HasCustomerId hasCustomerIdEntity) { + if (!edge.getCustomerId().isNullUid() && edge.getCustomerId().equals(hasCustomerIdEntity.getCustomerId())) { + return edge.getCustomerId(); + } else { + return null; } } - private void processRelationCRUD(EdgeEvent edgeEvent, UpdateMsgType msgType) { - EntityRelation entityRelation = objectMapper.convertValue(edgeEvent.getEntityBody(), EntityRelation.class); - EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() - .setRelationUpdateMsg(ctx.getRelationUpdateMsgConstructor().constructRelationUpdatedMsg(msgType, entityRelation)) + private DownlinkMsg processRelation(EdgeEvent edgeEvent, UpdateMsgType msgType) { + EntityRelation entityRelation = mapper.convertValue(edgeEvent.getBody(), EntityRelation.class); + RelationUpdateMsg r = ctx.getRelationMsgConstructor().constructRelationUpdatedMsg(msgType, entityRelation); + return DownlinkMsg.newBuilder() + .addAllRelationUpdateMsg(Collections.singletonList(r)) .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()); - } - } + } - @Override - public void onFailure(Throwable t) { - log.warn("Can't processAlarmCRUD, edgeEvent [{}]", edgeEvent, t); - } - }, ctx.getDbCallbackExecutor()); + private DownlinkMsg processAlarm(EdgeEvent edgeEvent, UpdateMsgType msgType) { + DownlinkMsg downlinkMsg = null; + try { + AlarmId alarmId = new AlarmId(edgeEvent.getEntityId()); + Alarm alarm = ctx.getAlarmService().findAlarmByIdAsync(edgeEvent.getTenantId(), alarmId).get(); + if (alarm != null) { + downlinkMsg = DownlinkMsg.newBuilder() + .addAllAlarmUpdateMsg(Collections.singletonList(ctx.getAlarmMsgConstructor().constructAlarmUpdatedMsg(edge.getTenantId(), msgType, alarm))) + .build(); + } + } catch (Exception e) { + log.error("Can't process alarm msg [{}] [{}]", edgeEvent, msgType, e); + } + return downlinkMsg; + } + + private DownlinkMsg processWidgetsBundle(EdgeEvent edgeEvent, UpdateMsgType msgType, ActionType edgeActionType) { + WidgetsBundleId widgetsBundleId = new WidgetsBundleId(edgeEvent.getEntityId()); + DownlinkMsg downlinkMsg = null; + switch (edgeActionType) { + case ADDED: + case UPDATED: + WidgetsBundle widgetsBundle = ctx.getWidgetsBundleService().findWidgetsBundleById(edgeEvent.getTenantId(), widgetsBundleId); + if (widgetsBundle != null) { + WidgetsBundleUpdateMsg widgetsBundleUpdateMsg = + ctx.getWidgetsBundleMsgConstructor().constructWidgetsBundleUpdateMsg(msgType, widgetsBundle); + downlinkMsg = DownlinkMsg.newBuilder() + .addAllWidgetsBundleUpdateMsg(Collections.singletonList(widgetsBundleUpdateMsg)) + .build(); + } + break; + case DELETED: + WidgetsBundleUpdateMsg widgetsBundleUpdateMsg = + ctx.getWidgetsBundleMsgConstructor().constructWidgetsBundleDeleteMsg(widgetsBundleId); + downlinkMsg = DownlinkMsg.newBuilder() + .addAllWidgetsBundleUpdateMsg(Collections.singletonList(widgetsBundleUpdateMsg)) + .build(); + break; + } + return downlinkMsg; + } + + private DownlinkMsg processWidgetType(EdgeEvent edgeEvent, UpdateMsgType msgType, ActionType edgeActionType) { + WidgetTypeId widgetTypeId = new WidgetTypeId(edgeEvent.getEntityId()); + DownlinkMsg downlinkMsg = null; + switch (edgeActionType) { + case ADDED: + case UPDATED: + WidgetType widgetType = ctx.getWidgetTypeService().findWidgetTypeById(edgeEvent.getTenantId(), widgetTypeId); + if (widgetType != null) { + WidgetTypeUpdateMsg widgetTypeUpdateMsg = + ctx.getWidgetTypeMsgConstructor().constructWidgetTypeUpdateMsg(msgType, widgetType); + downlinkMsg = DownlinkMsg.newBuilder() + .addAllWidgetTypeUpdateMsg(Collections.singletonList(widgetTypeUpdateMsg)) + .build(); + } + break; + case DELETED: + WidgetTypeUpdateMsg widgetTypeUpdateMsg = + ctx.getWidgetTypeMsgConstructor().constructWidgetTypeDeleteMsg(widgetTypeId); + downlinkMsg = DownlinkMsg.newBuilder() + .addAllWidgetTypeUpdateMsg(Collections.singletonList(widgetTypeUpdateMsg)) + .build(); + break; + } + return downlinkMsg; + } + + private DownlinkMsg processAdminSettings(EdgeEvent edgeEvent) { + AdminSettings adminSettings = mapper.convertValue(edgeEvent.getBody(), AdminSettings.class); + AdminSettingsUpdateMsg t = ctx.getAdminSettingsMsgConstructor().constructAdminSettingsUpdateMsg(adminSettings); + return DownlinkMsg.newBuilder() + .addAllAdminSettingsUpdateMsg(Collections.singletonList(t)) + .build(); } private UpdateMsgType getResponseMsgType(ActionType actionType) { switch (actionType) { case UPDATED: + case CREDENTIALS_UPDATED: + case ASSIGNED_TO_CUSTOMER: + case UNASSIGNED_FROM_CUSTOMER: return UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE; case ADDED: case ASSIGNED_TO_EDGE: - return ENTITY_CREATED_RPC_MESSAGE; + case RELATION_ADD_OR_UPDATE: + return UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE; case DELETED: case UNASSIGNED_FROM_EDGE: + case RELATION_DELETED: 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 + "]"); } @@ -681,274 +853,69 @@ public final class EdgeGrpcSession implements Closeable { return builder.build(); } - private UplinkResponseMsg processUplinkMsg(UplinkMsg uplinkMsg) { + private ListenableFuture> processUplinkMsg(UplinkMsg uplinkMsg) { + List> result = new ArrayList<>(); try { if (uplinkMsg.getEntityDataList() != null && !uplinkMsg.getEntityDataList().isEmpty()) { for (EntityDataProto entityData : uplinkMsg.getEntityDataList()) { - 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()); - } + result.addAll(ctx.getTelemetryProcessor().onTelemetryUpdate(edge.getTenantId(), entityData)); } } + if (uplinkMsg.getDeviceUpdateMsgList() != null && !uplinkMsg.getDeviceUpdateMsgList().isEmpty()) { for (DeviceUpdateMsg deviceUpdateMsg : uplinkMsg.getDeviceUpdateMsgList()) { - onDeviceUpdate(deviceUpdateMsg); + result.add(ctx.getDeviceProcessor().onDeviceUpdate(edge.getTenantId(), edge, deviceUpdateMsg)); + } + } + if (uplinkMsg.getDeviceCredentialsUpdateMsgList() != null && !uplinkMsg.getDeviceCredentialsUpdateMsgList().isEmpty()) { + for (DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg : uplinkMsg.getDeviceCredentialsUpdateMsgList()) { + result.add(ctx.getDeviceProcessor().onDeviceCredentialsUpdate(edge.getTenantId(), deviceCredentialsUpdateMsg)); } } if (uplinkMsg.getAlarmUpdateMsgList() != null && !uplinkMsg.getAlarmUpdateMsgList().isEmpty()) { for (AlarmUpdateMsg alarmUpdateMsg : uplinkMsg.getAlarmUpdateMsgList()) { - onAlarmUpdate(alarmUpdateMsg); + result.add(ctx.getAlarmProcessor().onAlarmUpdate(edge.getTenantId(), alarmUpdateMsg)); + } + } + if (uplinkMsg.getRelationUpdateMsgList() != null && !uplinkMsg.getRelationUpdateMsgList().isEmpty()) { + for (RelationUpdateMsg relationUpdateMsg : uplinkMsg.getRelationUpdateMsgList()) { + result.add(ctx.getRelationProcessor().onRelationUpdate(edge.getTenantId(), relationUpdateMsg)); } } if (uplinkMsg.getRuleChainMetadataRequestMsgList() != null && !uplinkMsg.getRuleChainMetadataRequestMsgList().isEmpty()) { for (RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg : uplinkMsg.getRuleChainMetadataRequestMsgList()) { - ctx.getSyncEdgeService().syncRuleChainMetadata(edge, ruleChainMetadataRequestMsg, outputStream); + result.add(ctx.getSyncEdgeService().processRuleChainMetadataRequestMsg(edge, ruleChainMetadataRequestMsg)); } } - } catch (Exception e) { - return UplinkResponseMsg.newBuilder().setSuccess(false).setErrorMsg(e.getMessage()).build(); - } - - 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())); - 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); - } + if (uplinkMsg.getAttributesRequestMsgList() != null && !uplinkMsg.getAttributesRequestMsgList().isEmpty()) { + for (AttributesRequestMsg attributesRequestMsg : uplinkMsg.getAttributesRequestMsgList()) { + result.add(ctx.getSyncEdgeService().processAttributesRequestMsg(edge, attributesRequestMsg)); } - 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()); + } + if (uplinkMsg.getRelationRequestMsgList() != null && !uplinkMsg.getRelationRequestMsgList().isEmpty()) { + for (RelationRequestMsg relationRequestMsg : uplinkMsg.getRelationRequestMsgList()) { + result.add(ctx.getSyncEdgeService().processRelationRequestMsg(edge, relationRequestMsg)); } - 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: - return ctx.getDeviceService().findDeviceByTenantIdAndName(edge.getTenantId(), entityName).getId(); - case ASSET: - return ctx.getAssetService().findAssetByTenantIdAndName(edge.getTenantId(), entityName).getId(); - case ENTITY_VIEW: - return ctx.getEntityViewService().findEntityViewByTenantIdAndName(edge.getTenantId(), entityName).getId(); - default: - return null; - } - } - - private void onAlarmUpdate(AlarmUpdateMsg alarmUpdateMsg) { - EntityId originatorId = getAlarmOriginator(alarmUpdateMsg.getOriginatorName(), org.thingsboard.server.common.data.EntityType.valueOf(alarmUpdateMsg.getOriginatorType())); - if (originatorId != null) { - try { - Alarm existentAlarm = ctx.getAlarmService().findLatestByOriginatorAndType(edge.getTenantId(), originatorId, alarmUpdateMsg.getType()).get(); - switch (alarmUpdateMsg.getMsgType()) { - case ENTITY_CREATED_RPC_MESSAGE: - case ENTITY_UPDATED_RPC_MESSAGE: - if (existentAlarm == null) { - existentAlarm = new Alarm(); - existentAlarm.setTenantId(edge.getTenantId()); - existentAlarm.setType(alarmUpdateMsg.getName()); - existentAlarm.setOriginator(originatorId); - existentAlarm.setSeverity(AlarmSeverity.valueOf(alarmUpdateMsg.getSeverity())); - existentAlarm.setStatus(AlarmStatus.valueOf(alarmUpdateMsg.getStatus())); - existentAlarm.setStartTs(alarmUpdateMsg.getStartTs()); - existentAlarm.setAckTs(alarmUpdateMsg.getAckTs()); - existentAlarm.setClearTs(alarmUpdateMsg.getClearTs()); - existentAlarm.setPropagate(alarmUpdateMsg.getPropagate()); - } - existentAlarm.setEndTs(alarmUpdateMsg.getEndTs()); - existentAlarm.setDetails(objectMapper.readTree(alarmUpdateMsg.getDetails())); - ctx.getAlarmService().createOrUpdateAlarm(existentAlarm); - break; - case ALARM_ACK_RPC_MESSAGE: - if (existentAlarm != null) { - ctx.getAlarmService().ackAlarm(edge.getTenantId(), existentAlarm.getId(), alarmUpdateMsg.getAckTs()); - } - break; - case ALARM_CLEAR_RPC_MESSAGE: - if (existentAlarm != null) { - ctx.getAlarmService().clearAlarm(edge.getTenantId(), existentAlarm.getId(), objectMapper.readTree(alarmUpdateMsg.getDetails()), alarmUpdateMsg.getAckTs()); - } - break; - case ENTITY_DELETED_RPC_MESSAGE: - if (existentAlarm != null) { - ctx.getAlarmService().deleteAlarm(edge.getTenantId(), existentAlarm.getId()); - } - break; + } + if (uplinkMsg.getUserCredentialsRequestMsgList() != null && !uplinkMsg.getUserCredentialsRequestMsgList().isEmpty()) { + for (UserCredentialsRequestMsg userCredentialsRequestMsg : uplinkMsg.getUserCredentialsRequestMsgList()) { + result.add(ctx.getSyncEdgeService().processUserCredentialsRequestMsg(edge, userCredentialsRequestMsg)); + } + } + if (uplinkMsg.getDeviceCredentialsRequestMsgList() != null && !uplinkMsg.getDeviceCredentialsRequestMsgList().isEmpty()) { + for (DeviceCredentialsRequestMsg deviceCredentialsRequestMsg : uplinkMsg.getDeviceCredentialsRequestMsgList()) { + result.add(ctx.getSyncEdgeService().processDeviceCredentialsRequestMsg(edge, deviceCredentialsRequestMsg)); + } + } + if (uplinkMsg.getDeviceRpcCallMsgList() != null && !uplinkMsg.getDeviceRpcCallMsgList().isEmpty()) { + for (DeviceRpcCallMsg deviceRpcCallMsg : uplinkMsg.getDeviceRpcCallMsgList()) { + result.add(ctx.getDeviceProcessor().processDeviceRpcCallResponseMsg(edge.getTenantId(), deviceRpcCallMsg)); } - } catch (Exception e) { - log.error("Error during finding existent alarm", e); } + } catch (Exception e) { + log.error("Can't process uplink msg [{}]", uplinkMsg, e); } + return Futures.allAsList(result); } private ConnectResponseMsg processConnect(ConnectRequestMsg request) { @@ -957,7 +924,6 @@ public final class EdgeGrpcSession implements Closeable { edge = optional.get(); try { if (edge.getSecret().equals(request.getEdgeSecret())) { - connected = true; sessionOpenListener.accept(edge.getId(), this); return ConnectResponseMsg.newBuilder() .setResponseCode(ConnectResponseCode.ACCEPTED) @@ -982,22 +948,18 @@ public final class EdgeGrpcSession implements Closeable { .setConfiguration(EdgeConfiguration.getDefaultInstance()).build(); } - private void createRelationFromEdge(EntityId entityId) { - EntityRelation relation = new EntityRelation(); - relation.setFrom(edge.getId()); - relation.setTo(entityId); - relation.setTypeGroup(RelationTypeGroup.COMMON); - relation.setType(EntityRelation.EDGE_TYPE); - ctx.getRelationService().saveRelation(edge.getTenantId(), relation); - } - private EdgeConfiguration constructEdgeConfigProto(Edge edge) throws JsonProcessingException { return EdgeConfiguration.newBuilder() + .setEdgeIdMSB(edge.getId().getId().getMostSignificantBits()) + .setEdgeIdLSB(edge.getId().getId().getLeastSignificantBits()) .setTenantIdMSB(edge.getTenantId().getId().getMostSignificantBits()) .setTenantIdLSB(edge.getTenantId().getId().getLeastSignificantBits()) .setName(edge.getName()) .setRoutingKey(edge.getRoutingKey()) .setType(edge.getType()) + .setEdgeLicenseKey(edge.getEdgeLicenseKey()) + .setCloudEndpoint(edge.getCloudEndpoint()) + .setCloudType("CE") .build(); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeRpcService.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeRpcService.java new file mode 100644 index 0000000000..2355a2e49a --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeRpcService.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.service.edge.rpc; + +import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.id.EdgeId; + +public interface EdgeRpcService { + + void updateEdge(Edge edge); + + void deleteEdge(EdgeId edgeId); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AdminSettingsMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AdminSettingsMsgConstructor.java new file mode 100644 index 0000000000..6a6b67e2e6 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AdminSettingsMsgConstructor.java @@ -0,0 +1,38 @@ +/** + * 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.AdminSettings; +import org.thingsboard.server.dao.util.mapping.JacksonUtil; +import org.thingsboard.server.gen.edge.AdminSettingsUpdateMsg; + +@Slf4j +@Component +public class AdminSettingsMsgConstructor { + + public AdminSettingsUpdateMsg constructAdminSettingsUpdateMsg(AdminSettings adminSettings) { + AdminSettingsUpdateMsg.Builder builder = AdminSettingsUpdateMsg.newBuilder() + .setKey(adminSettings.getKey()) + .setJsonValue(JacksonUtil.toString(adminSettings.getJsonValue())); + if (adminSettings.getId() != null) { + builder.setIsSystem(true); + } + return builder.build(); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AlarmUpdateMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AlarmMsgConstructor.java similarity index 98% rename from application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AlarmUpdateMsgConstructor.java rename to application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AlarmMsgConstructor.java index 91f3570c4e..3414727575 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AlarmUpdateMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AlarmMsgConstructor.java @@ -34,7 +34,7 @@ import org.thingsboard.server.gen.edge.UpdateMsgType; @Component @Slf4j -public class AlarmUpdateMsgConstructor { +public class AlarmMsgConstructor { @Autowired private DeviceService deviceService; 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/AssetMsgConstructor.java similarity index 77% rename from application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetUpdateMsgConstructor.java rename to application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetMsgConstructor.java index c404fe5c41..c6eb3d32f4 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/AssetMsgConstructor.java @@ -19,14 +19,16 @@ 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.common.data.id.CustomerId; +import org.thingsboard.server.dao.util.mapping.JacksonUtil; import org.thingsboard.server.gen.edge.AssetUpdateMsg; import org.thingsboard.server.gen.edge.UpdateMsgType; @Component @Slf4j -public class AssetUpdateMsgConstructor { +public class AssetMsgConstructor { - public AssetUpdateMsg constructAssetUpdatedMsg(UpdateMsgType msgType, Asset asset) { + public AssetUpdateMsg constructAssetUpdatedMsg(UpdateMsgType msgType, Asset asset, CustomerId customerId) { AssetUpdateMsg.Builder builder = AssetUpdateMsg.newBuilder() .setMsgType(msgType) .setIdMSB(asset.getId().getId().getMostSignificantBits()) @@ -36,6 +38,13 @@ public class AssetUpdateMsgConstructor { if (asset.getLabel() != null) { builder.setLabel(asset.getLabel()); } + if (customerId != null) { + builder.setCustomerIdMSB(customerId.getId().getMostSignificantBits()); + builder.setCustomerIdLSB(customerId.getId().getLeastSignificantBits()); + } + if (asset.getAdditionalInfo() != null) { + builder.setAdditionalInfo(JacksonUtil.toString(asset.getAdditionalInfo())); + } return builder.build(); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/CustomerMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/CustomerMsgConstructor.java new file mode 100644 index 0000000000..94687b88d4 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/CustomerMsgConstructor.java @@ -0,0 +1,72 @@ +/** + * 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.Customer; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.dao.util.mapping.JacksonUtil; +import org.thingsboard.server.gen.edge.CustomerUpdateMsg; +import org.thingsboard.server.gen.edge.UpdateMsgType; + +@Component +@Slf4j +public class CustomerMsgConstructor { + + public CustomerUpdateMsg constructCustomerUpdatedMsg(UpdateMsgType msgType, Customer customer) { + CustomerUpdateMsg.Builder builder = CustomerUpdateMsg.newBuilder() + .setMsgType(msgType) + .setIdMSB(customer.getId().getId().getMostSignificantBits()) + .setIdLSB(customer.getId().getId().getLeastSignificantBits()) + .setTitle(customer.getTitle()); + if (customer.getCountry() != null) { + builder.setCountry(customer.getCountry()); + } + if (customer.getState() != null) { + builder.setState(customer.getState()); + } + if (customer.getCity() != null) { + builder.setCity(customer.getCity()); + } + if (customer.getAddress() != null) { + builder.setAddress(customer.getAddress()); + } + if (customer.getAddress2() != null) { + builder.setAddress2(customer.getAddress2()); + } + if (customer.getZip() != null) { + builder.setZip(customer.getZip()); + } + if (customer.getPhone() != null) { + builder.setPhone(customer.getPhone()); + } + if (customer.getEmail() != null) { + builder.setEmail(customer.getEmail()); + } + if (customer.getAdditionalInfo() != null) { + builder.setAdditionalInfo(JacksonUtil.toString(customer.getAdditionalInfo())); + } + return builder.build(); + } + + public CustomerUpdateMsg constructCustomerDeleteMsg(CustomerId customerId) { + return CustomerUpdateMsg.newBuilder() + .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) + .setIdMSB(customerId.getId().getMostSignificantBits()) + .setIdLSB(customerId.getId().getLeastSignificantBits()).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DashboardUpdateMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DashboardMsgConstructor.java similarity index 81% rename from application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DashboardUpdateMsgConstructor.java rename to application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DashboardMsgConstructor.java index 6bd24f123b..a41b987c1c 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/DashboardMsgConstructor.java @@ -16,30 +16,30 @@ 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.Dashboard; +import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DashboardId; -import org.thingsboard.server.dao.dashboard.DashboardService; +import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.dao.util.mapping.JacksonUtil; import org.thingsboard.server.gen.edge.DashboardUpdateMsg; import org.thingsboard.server.gen.edge.UpdateMsgType; @Component @Slf4j -public class DashboardUpdateMsgConstructor { +public class DashboardMsgConstructor { - @Autowired - private DashboardService dashboardService; - - public DashboardUpdateMsg constructDashboardUpdatedMsg(UpdateMsgType msgType, Dashboard dashboard) { - dashboard = dashboardService.findDashboardById(dashboard.getTenantId(), dashboard.getId()); + public DashboardUpdateMsg constructDashboardUpdatedMsg(UpdateMsgType msgType, Dashboard dashboard, CustomerId customerId) { DashboardUpdateMsg.Builder builder = DashboardUpdateMsg.newBuilder() .setMsgType(msgType) .setIdMSB(dashboard.getId().getId().getMostSignificantBits()) .setIdLSB(dashboard.getId().getId().getLeastSignificantBits()) .setTitle(dashboard.getTitle()) .setConfiguration(JacksonUtil.toString(dashboard.getConfiguration())); + if (customerId != null) { + builder.setCustomerIdMSB(customerId.getId().getMostSignificantBits()); + builder.setCustomerIdLSB(customerId.getId().getLeastSignificantBits()); + } return builder.build(); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceMsgConstructor.java new file mode 100644 index 0000000000..ffc635075d --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceMsgConstructor.java @@ -0,0 +1,97 @@ +/** + * 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.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.thingsboard.rule.engine.api.RuleEngineDeviceRpcRequest; +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.security.DeviceCredentials; +import org.thingsboard.server.dao.util.mapping.JacksonUtil; +import org.thingsboard.server.gen.edge.DeviceCredentialsUpdateMsg; +import org.thingsboard.server.gen.edge.DeviceRpcCallMsg; +import org.thingsboard.server.gen.edge.DeviceUpdateMsg; +import org.thingsboard.server.gen.edge.RpcRequestMsg; +import org.thingsboard.server.gen.edge.UpdateMsgType; + +@Component +@Slf4j +public class DeviceMsgConstructor { + + protected static final ObjectMapper mapper = new ObjectMapper(); + + public DeviceUpdateMsg constructDeviceUpdatedMsg(UpdateMsgType msgType, Device device, CustomerId customerId) { + DeviceUpdateMsg.Builder builder = DeviceUpdateMsg.newBuilder() + .setMsgType(msgType) + .setIdMSB(device.getId().getId().getMostSignificantBits()) + .setIdLSB(device.getId().getId().getLeastSignificantBits()) + .setName(device.getName()) + .setType(device.getType()); + if (device.getLabel() != null) { + builder.setLabel(device.getLabel()); + } + if (customerId != null) { + builder.setCustomerIdMSB(customerId.getId().getMostSignificantBits()); + builder.setCustomerIdLSB(customerId.getId().getLeastSignificantBits()); + } + if (device.getAdditionalInfo() != null) { + builder.setAdditionalInfo(JacksonUtil.toString(device.getAdditionalInfo())); + } + return builder.build(); + } + + public DeviceCredentialsUpdateMsg constructDeviceCredentialsUpdatedMsg(DeviceCredentials deviceCredentials) { + DeviceCredentialsUpdateMsg.Builder builder = DeviceCredentialsUpdateMsg.newBuilder() + .setDeviceIdMSB(deviceCredentials.getDeviceId().getId().getMostSignificantBits()) + .setDeviceIdLSB(deviceCredentials.getDeviceId().getId().getLeastSignificantBits()); + if (deviceCredentials.getCredentialsType() != null) { + builder.setCredentialsType(deviceCredentials.getCredentialsType().name()) + .setCredentialsId(deviceCredentials.getCredentialsId()); + } + if (deviceCredentials.getCredentialsValue() != null) { + builder.setCredentialsValue(deviceCredentials.getCredentialsValue()); + } + return builder.build(); + } + + public DeviceUpdateMsg constructDeviceDeleteMsg(DeviceId deviceId) { + return DeviceUpdateMsg.newBuilder() + .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) + .setIdMSB(deviceId.getId().getMostSignificantBits()) + .setIdLSB(deviceId.getId().getLeastSignificantBits()).build(); + } + + public DeviceRpcCallMsg constructDeviceRpcCallMsg(JsonNode body) { + RuleEngineDeviceRpcRequest request = mapper.convertValue(body, RuleEngineDeviceRpcRequest.class); + RpcRequestMsg.Builder requestBuilder = RpcRequestMsg.newBuilder(); + requestBuilder.setMethod(request.getMethod()); + requestBuilder.setParams(request.getBody()); + DeviceRpcCallMsg.Builder builder = DeviceRpcCallMsg.newBuilder() + .setDeviceIdMSB(request.getDeviceId().getId().getMostSignificantBits()) + .setDeviceIdLSB(request.getDeviceId().getId().getLeastSignificantBits()) + .setRequestIdMSB(request.getRequestUUID().getMostSignificantBits()) + .setRequestIdLSB(request.getRequestUUID().getLeastSignificantBits()) + .setExpirationTime(request.getExpirationTime()) + .setOriginServiceId(request.getOriginServiceId()) + .setOneway(request.isOneway()) + .setRequestMsg(requestBuilder.build()); + return builder.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 deleted file mode 100644 index 8d7fbc4c25..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceUpdateMsgConstructor.java +++ /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. - */ -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.id.DeviceId; -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; - -@Component -@Slf4j -public class DeviceUpdateMsgConstructor { - - @Autowired - private DeviceCredentialsService deviceCredentialsService; - - public DeviceUpdateMsg constructDeviceUpdatedMsg(UpdateMsgType msgType, Device device) { - DeviceUpdateMsg.Builder builder = DeviceUpdateMsg.newBuilder() - .setMsgType(msgType) - .setIdMSB(device.getId().getId().getMostSignificantBits()) - .setIdLSB(device.getId().getId().getLeastSignificantBits()) - .setName(device.getName()) - .setType(device.getType()); - if (device.getLabel() != null) { - builder.setLabel(device.getLabel()); - } - if (msgType.equals(UpdateMsgType.ENTITY_CREATED_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) { - if (deviceCredentials.getCredentialsType() != null) { - builder.setCredentialsType(deviceCredentials.getCredentialsType().name()) - .setCredentialsId(deviceCredentials.getCredentialsId()); - } - if (deviceCredentials.getCredentialsValue() != null) { - builder.setCredentialsValue(deviceCredentials.getCredentialsValue()); - } - } - } - return builder.build(); - } - - public DeviceUpdateMsg constructDeviceDeleteMsg(DeviceId deviceId) { - return DeviceUpdateMsg.newBuilder() - .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) - .setIdMSB(deviceId.getId().getMostSignificantBits()) - .setIdLSB(deviceId.getId().getLeastSignificantBits()).build(); - } -} 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 index d9a06655c5..ffa2ada070 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityDataMsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityDataMsgConstructor.java @@ -15,13 +15,20 @@ */ package org.thingsboard.server.service.edge.rpc.constructor; +import com.google.gson.Gson; +import com.google.gson.JsonArray; import com.google.gson.JsonElement; +import com.google.gson.JsonObject; 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.AttributeDeleteMsg; import org.thingsboard.server.gen.edge.EntityDataProto; +import org.thingsboard.server.gen.transport.TransportProtos; + +import java.util.List; @Component @Slf4j @@ -35,20 +42,45 @@ public class EntityDataMsgConstructor { switch (actionType) { case TIMESERIES_UPDATED: try { - builder.setPostTelemetryMsg(JsonConverter.convertToTelemetryProto(entityData)); + JsonObject data = entityData.getAsJsonObject(); + long ts; + if (data.get("ts") != null && !data.get("ts").isJsonNull()) { + ts = data.getAsJsonPrimitive("ts").getAsLong(); + } else { + ts = System.currentTimeMillis(); + } + builder.setPostTelemetryMsg(JsonConverter.convertToTelemetryProto(data.getAsJsonObject("data"), ts)); } catch (Exception e) { log.warn("Can't convert to telemetry proto, entityData [{}]", entityData, e); } break; case ATTRIBUTES_UPDATED: try { - builder.setPostAttributesMsg(JsonConverter.convertToAttributesProto(entityData)); + JsonObject data = entityData.getAsJsonObject(); + TransportProtos.PostAttributeMsg postAttributeMsg = JsonConverter.convertToAttributesProto(data.getAsJsonObject("kv")); + if (data.has("isPostAttributes") && data.getAsJsonPrimitive("isPostAttributes").getAsBoolean()) { + builder.setPostAttributesMsg(postAttributeMsg); + } else { + builder.setAttributesUpdatedMsg(postAttributeMsg); + } + builder.setPostAttributeScope(data.getAsJsonPrimitive("scope").getAsString()); } 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: + case ATTRIBUTES_DELETED: + try { + AttributeDeleteMsg.Builder attributeDeleteMsg = AttributeDeleteMsg.newBuilder(); + attributeDeleteMsg.setScope(entityData.getAsJsonObject().getAsJsonPrimitive("scope").getAsString()); + JsonArray jsonArray = entityData.getAsJsonObject().getAsJsonArray("keys"); + List keys = new Gson().fromJson(jsonArray.toString(), List.class); + attributeDeleteMsg.addAllAttributeNames(keys); + attributeDeleteMsg.build(); + builder.setAttributeDeleteMsg(attributeDeleteMsg); + } catch (Exception e) { + log.warn("Can't convert to AttributeDeleteMsg proto, entityData [{}]", entityData, e); + } + break; } return builder.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/EntityViewMsgConstructor.java similarity index 75% rename from application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityViewUpdateMsgConstructor.java rename to application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityViewMsgConstructor.java index 75301c7ed9..abe32dec77 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/EntityViewMsgConstructor.java @@ -18,16 +18,18 @@ 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.CustomerId; import org.thingsboard.server.common.data.id.EntityViewId; +import org.thingsboard.server.dao.util.mapping.JacksonUtil; import org.thingsboard.server.gen.edge.EdgeEntityType; import org.thingsboard.server.gen.edge.EntityViewUpdateMsg; import org.thingsboard.server.gen.edge.UpdateMsgType; @Component @Slf4j -public class EntityViewUpdateMsgConstructor { +public class EntityViewMsgConstructor { - public EntityViewUpdateMsg constructEntityViewUpdatedMsg(UpdateMsgType msgType, EntityView entityView) { + public EntityViewUpdateMsg constructEntityViewUpdatedMsg(UpdateMsgType msgType, EntityView entityView, CustomerId customerId) { EdgeEntityType entityType; switch (entityView.getEntityId().getEntityType()) { case DEVICE: @@ -45,9 +47,16 @@ public class EntityViewUpdateMsgConstructor { .setIdLSB(entityView.getId().getId().getLeastSignificantBits()) .setName(entityView.getName()) .setType(entityView.getType()) - .setIdMSB(entityView.getEntityId().getId().getMostSignificantBits()) - .setIdLSB(entityView.getEntityId().getId().getLeastSignificantBits()) + .setEntityIdMSB(entityView.getEntityId().getId().getMostSignificantBits()) + .setEntityIdLSB(entityView.getEntityId().getId().getLeastSignificantBits()) .setEntityType(entityType); + if (customerId != null) { + builder.setCustomerIdMSB(customerId.getId().getMostSignificantBits()); + builder.setCustomerIdLSB(customerId.getId().getLeastSignificantBits()); + } + if (entityView.getAdditionalInfo() != null) { + builder.setAdditionalInfo(JacksonUtil.toString(entityView.getAdditionalInfo())); + } return builder.build(); } 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/RelationMsgConstructor.java similarity index 97% rename from application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RelationUpdateMsgConstructor.java rename to application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RelationMsgConstructor.java index e09b9f2e8b..5a1f7ce39d 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/RelationMsgConstructor.java @@ -24,7 +24,7 @@ import org.thingsboard.server.gen.edge.UpdateMsgType; @Component @Slf4j -public class RelationUpdateMsgConstructor { +public class RelationMsgConstructor { public RelationUpdateMsg constructRelationUpdatedMsg(UpdateMsgType msgType, EntityRelation entityRelation) { RelationUpdateMsg.Builder builder = RelationUpdateMsg.newBuilder() 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/RuleChainMsgConstructor.java similarity index 98% rename from application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainUpdateMsgConstructor.java rename to application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java index 52e487d973..22017c56c2 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/RuleChainMsgConstructor.java @@ -38,7 +38,7 @@ import java.util.List; @Component @Slf4j -public class RuleChainUpdateMsgConstructor { +public class RuleChainMsgConstructor { private static final ObjectMapper objectMapper = new ObjectMapper(); @@ -68,6 +68,8 @@ public class RuleChainUpdateMsgConstructor { .addAllRuleChainConnections(constructRuleChainConnections(ruleChainMetaData.getRuleChainConnections())); if (ruleChainMetaData.getFirstNodeIndex() != null) { builder.setFirstNodeIndex(ruleChainMetaData.getFirstNodeIndex()); + } else { + builder.setFirstNodeIndex(-1); } builder.setMsgType(msgType); return builder.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/UserMsgConstructor.java similarity index 68% rename from application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/UserUpdateMsgConstructor.java rename to application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/UserMsgConstructor.java index b994d8fdc4..b5b6b080f7 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/UserMsgConstructor.java @@ -16,31 +16,34 @@ 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.id.CustomerId; +import org.thingsboard.server.common.data.id.EntityId; 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; import org.thingsboard.server.gen.edge.UpdateMsgType; +import org.thingsboard.server.gen.edge.UserCredentialsUpdateMsg; import org.thingsboard.server.gen.edge.UserUpdateMsg; +import java.util.UUID; + @Component @Slf4j -public class UserUpdateMsgConstructor { - - @Autowired - private UserService userService; +public class UserMsgConstructor { - public UserUpdateMsg constructUserUpdatedMsg(UpdateMsgType msgType, User user) { + public UserUpdateMsg constructUserUpdatedMsg(UpdateMsgType msgType, User user, CustomerId customerId) { UserUpdateMsg.Builder builder = UserUpdateMsg.newBuilder() .setMsgType(msgType) .setIdMSB(user.getId().getId().getMostSignificantBits()) .setIdLSB(user.getId().getId().getLeastSignificantBits()) .setEmail(user.getEmail()) - .setAuthority(user.getAuthority().name()) - .setEnabled(false); + .setAuthority(user.getAuthority().name()); + if (customerId != null) { + builder.setCustomerIdMSB(customerId.getId().getMostSignificantBits()); + builder.setCustomerIdLSB(customerId.getId().getLeastSignificantBits()); + } if (user.getFirstName() != null) { builder.setFirstName(user.getFirstName()); } @@ -53,13 +56,6 @@ public class UserUpdateMsgConstructor { 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(); } @@ -69,4 +65,13 @@ public class UserUpdateMsgConstructor { .setIdMSB(userId.getId().getMostSignificantBits()) .setIdLSB(userId.getId().getLeastSignificantBits()).build(); } + + public UserCredentialsUpdateMsg constructUserCredentialsUpdatedMsg(UserCredentials userCredentials) { + UserCredentialsUpdateMsg.Builder builder = UserCredentialsUpdateMsg.newBuilder() + .setUserIdMSB(userCredentials.getUserId().getId().getMostSignificantBits()) + .setUserIdLSB(userCredentials.getUserId().getId().getLeastSignificantBits()) + .setEnabled(userCredentials.isEnabled()) + .setPassword(userCredentials.getPassword()); + return builder.build(); + } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetTypeMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetTypeMsgConstructor.java new file mode 100644 index 0000000000..e01b2afa66 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetTypeMsgConstructor.java @@ -0,0 +1,61 @@ +/** + * 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.id.TenantId; +import org.thingsboard.server.common.data.id.WidgetTypeId; +import org.thingsboard.server.common.data.widget.WidgetType; +import org.thingsboard.server.dao.util.mapping.JacksonUtil; +import org.thingsboard.server.gen.edge.UpdateMsgType; +import org.thingsboard.server.gen.edge.WidgetTypeUpdateMsg; + +@Component +@Slf4j +public class WidgetTypeMsgConstructor { + + public WidgetTypeUpdateMsg constructWidgetTypeUpdateMsg(UpdateMsgType msgType, WidgetType widgetType) { + WidgetTypeUpdateMsg.Builder builder = WidgetTypeUpdateMsg.newBuilder() + .setMsgType(msgType) + .setIdMSB(widgetType.getId().getId().getMostSignificantBits()) + .setIdLSB(widgetType.getId().getId().getLeastSignificantBits()); + if (widgetType.getBundleAlias() != null) { + builder.setBundleAlias(widgetType.getBundleAlias()); + } + if (widgetType.getAlias() != null) { + builder.setAlias(widgetType.getAlias()); + } + if (widgetType.getName() != null) { + builder.setName(widgetType.getName()); + } + if (widgetType.getDescriptor() != null) { + builder.setDescriptorJson(JacksonUtil.toString(widgetType.getDescriptor())); + } + if (widgetType.getTenantId().equals(TenantId.SYS_TENANT_ID)) { + builder.setIsSystem(true); + } + return builder.build(); + } + + public WidgetTypeUpdateMsg constructWidgetTypeDeleteMsg(WidgetTypeId widgetTypeId) { + return WidgetTypeUpdateMsg.newBuilder() + .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) + .setIdMSB(widgetTypeId.getId().getMostSignificantBits()) + .setIdLSB(widgetTypeId.getId().getLeastSignificantBits()) + .build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetsBundleMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetsBundleMsgConstructor.java new file mode 100644 index 0000000000..13dd78980c --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetsBundleMsgConstructor.java @@ -0,0 +1,54 @@ +/** + * 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.protobuf.ByteString; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.WidgetsBundleId; +import org.thingsboard.server.common.data.widget.WidgetsBundle; +import org.thingsboard.server.gen.edge.UpdateMsgType; +import org.thingsboard.server.gen.edge.WidgetsBundleUpdateMsg; + +@Component +@Slf4j +public class WidgetsBundleMsgConstructor { + + public WidgetsBundleUpdateMsg constructWidgetsBundleUpdateMsg(UpdateMsgType msgType, WidgetsBundle widgetsBundle) { + WidgetsBundleUpdateMsg.Builder builder = WidgetsBundleUpdateMsg.newBuilder() + .setMsgType(msgType) + .setIdMSB(widgetsBundle.getId().getId().getMostSignificantBits()) + .setIdLSB(widgetsBundle.getId().getId().getLeastSignificantBits()) + .setTitle(widgetsBundle.getTitle()) + .setAlias(widgetsBundle.getAlias()); + if (widgetsBundle.getImage() != null) { + builder.setImage(ByteString.copyFrom(widgetsBundle.getImage())); + } + if (widgetsBundle.getTenantId().equals(TenantId.SYS_TENANT_ID)) { + builder.setIsSystem(true); + } + return builder.build(); + } + + public WidgetsBundleUpdateMsg constructWidgetsBundleDeleteMsg(WidgetsBundleId widgetsBundleId) { + return WidgetsBundleUpdateMsg.newBuilder() + .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) + .setIdMSB(widgetsBundleId.getId().getMostSignificantBits()) + .setIdLSB(widgetsBundleId.getId().getLeastSignificantBits()) + .build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/DefaultSyncEdgeService.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/DefaultSyncEdgeService.java index 47f897f447..45f72bba0a 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 @@ -15,22 +15,44 @@ */ package org.thingsboard.server.service.edge.rpc.init; +import com.datastax.oss.driver.api.core.uuid.Uuids; +import com.fasterxml.jackson.databind.JsonNode; +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 io.grpc.stub.StreamObserver; +import com.google.common.util.concurrent.SettableFuture; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.text.WordUtils; +import org.checkerframework.checker.nullness.qual.Nullable; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.DefaultResourceLoader; import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.Dashboard; +import org.thingsboard.server.common.data.AdminSettings; 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.EntityType; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.User; 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.AdminSettingsId; +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.EntityIdFactory; 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.DataType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.TimePageLink; @@ -39,45 +61,49 @@ 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.common.data.widget.WidgetType; +import org.thingsboard.server.common.data.widget.WidgetsBundle; import org.thingsboard.server.dao.asset.AssetService; +import org.thingsboard.server.dao.attributes.AttributesService; 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.entityview.EntityViewService; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.rule.RuleChainService; +import org.thingsboard.server.dao.settings.AdminSettingsService; 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; -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.dao.widget.WidgetTypeService; +import org.thingsboard.server.dao.widget.WidgetsBundleService; +import org.thingsboard.server.gen.edge.AttributesRequestMsg; +import org.thingsboard.server.gen.edge.DeviceCredentialsRequestMsg; +import org.thingsboard.server.gen.edge.RelationRequestMsg; 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 org.thingsboard.server.gen.edge.UserCredentialsRequestMsg; +import org.thingsboard.server.service.executors.DbCallbackExecutorService; +import java.io.File; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; -import java.util.HashSet; +import java.util.HashMap; import java.util.List; -import java.util.Set; +import java.util.Map; import java.util.UUID; +import java.util.regex.Matcher; +import java.util.regex.Pattern; @Service @Slf4j public class DefaultSyncEdgeService implements SyncEdgeService { + private static final ObjectMapper mapper = new ObjectMapper(); + + @Autowired + private EdgeEventService edgeEventService; + + @Autowired + private AttributesService attributesService; + @Autowired private RuleChainService ruleChainService; @@ -100,272 +126,417 @@ public class DefaultSyncEdgeService implements SyncEdgeService { private UserService userService; @Autowired - private RuleChainUpdateMsgConstructor ruleChainUpdateMsgConstructor; - - @Autowired - private DeviceUpdateMsgConstructor deviceUpdateMsgConstructor; - - @Autowired - private AssetUpdateMsgConstructor assetUpdateMsgConstructor; - - @Autowired - private EntityViewUpdateMsgConstructor entityViewUpdateMsgConstructor; + private WidgetsBundleService widgetsBundleService; @Autowired - private DashboardUpdateMsgConstructor dashboardUpdateMsgConstructor; + private WidgetTypeService widgetTypeService; @Autowired - private UserUpdateMsgConstructor userUpdateMsgConstructor; + private AdminSettingsService adminSettingsService; @Autowired - private RelationUpdateMsgConstructor relationUpdateMsgConstructor; + private DbCallbackExecutorService dbCallbackExecutorService; @Override - public void sync(EdgeContextComponent ctx, Edge edge, StreamObserver outputStream) { - Set pushedEntityIds = new HashSet<>(); - syncUsers(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()); + public void sync(Edge edge) { + try { + syncWidgetsBundleAndWidgetTypes(edge); + syncAdminSettings(edge); + syncRuleChains(edge); + syncUsers(edge); + syncDevices(edge); + syncAssets(edge); + syncEntityViews(edge); + syncDashboards(edge); + } catch (Exception e) { + log.error("Exception during sync process", e); + } } - private ListenableFuture syncRuleChains(EdgeContextComponent ctx, Edge edge, Set pushedEntityIds, StreamObserver outputStream) { + private void syncRuleChains(Edge edge) { try { - PageData pageData = ruleChainService.findRuleChainsByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), new TimePageLink(Integer.MAX_VALUE)); + PageData pageData = + ruleChainService.findRuleChainsByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), new TimePageLink(Integer.MAX_VALUE)); 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()); + saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.RULE_CHAIN, ActionType.ADDED, ruleChain.getId(), null); } } } catch (Exception e) { log.error("Exception during loading edge rule chain(s) on sync!", e); } - return Futures.immediateFuture(null); } - private ListenableFuture syncDevices(EdgeContextComponent ctx, Edge edge, Set pushedEntityIds, StreamObserver outputStream) { + private void syncDevices(Edge edge) { try { - PageData pageData = deviceService.findDevicesByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), new TimePageLink(Integer.MAX_VALUE)); + PageData pageData = + deviceService.findDevicesByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), new TimePageLink(Integer.MAX_VALUE)); if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) { log.trace("[{}] [{}] device(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size()); for (Device device : pageData.getData()) { - DeviceUpdateMsg deviceUpdateMsg = - deviceUpdateMsgConstructor.constructDeviceUpdatedMsg( - UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, - device); - EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() - .setDeviceUpdateMsg(deviceUpdateMsg) - .build(); - outputStream.onNext(ResponseMsg.newBuilder() - .setEntityUpdateMsg(entityUpdateMsg) - .build()); - pushedEntityIds.add(device.getId()); + saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.DEVICE, ActionType.ADDED, device.getId(), null); } } } catch (Exception e) { log.error("Exception during loading edge device(s) on sync!", e); } - return Futures.immediateFuture(null); } - private ListenableFuture syncAssets(EdgeContextComponent ctx, Edge edge, Set pushedEntityIds, StreamObserver outputStream) { + private void syncAssets(Edge edge) { try { PageData pageData = assetService.findAssetsByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), new TimePageLink(Integer.MAX_VALUE)); 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 = - assetUpdateMsgConstructor.constructAssetUpdatedMsg( - UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, - asset); - EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() - .setAssetUpdateMsg(assetUpdateMsg) - .build(); - outputStream.onNext(ResponseMsg.newBuilder() - .setEntityUpdateMsg(entityUpdateMsg) - .build()); - pushedEntityIds.add(asset.getId()); + saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.ASSET, ActionType.ADDED, asset.getId(), null); } } } catch (Exception e) { log.error("Exception during loading edge asset(s) on sync!", e); } - return Futures.immediateFuture(null); } - private ListenableFuture syncEntityViews(EdgeContextComponent ctx, Edge edge, Set pushedEntityIds, StreamObserver outputStream) { + private void syncEntityViews(Edge edge) { try { PageData pageData = entityViewService.findEntityViewsByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), new TimePageLink(Integer.MAX_VALUE)); 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()); + saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.ENTITY_VIEW, ActionType.ADDED, entityView.getId(), null); } } } catch (Exception e) { log.error("Exception during loading edge entity view(s) on sync!", e); } - return Futures.immediateFuture(null); } - private ListenableFuture syncDashboards(EdgeContextComponent ctx, Edge edge, Set pushedEntityIds, StreamObserver outputStream) { + private void syncDashboards(Edge edge) { try { PageData pageData = dashboardService.findDashboardsByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), new TimePageLink(Integer.MAX_VALUE)); 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()); + saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.DASHBOARD, ActionType.ADDED, dashboardInfo.getId(), null); } } } 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) { + private void syncUsers(Edge edge) { try { PageData pageData = userService.findTenantAdmins(edge.getTenantId(), new PageLink(Integer.MAX_VALUE)); - pushUsersToEdge(pageData, edge, pushedEntityIds, outputStream); + pushUsersToEdge(pageData, edge); if (edge.getCustomerId() != null && !EntityId.NULL_UUID.equals(edge.getCustomerId().getId())) { + saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.CUSTOMER, ActionType.ADDED, edge.getCustomerId(), null); pageData = userService.findCustomerUsers(edge.getTenantId(), edge.getCustomerId(), new PageLink(Integer.MAX_VALUE)); - pushUsersToEdge(pageData, edge, pushedEntityIds, outputStream); + pushUsersToEdge(pageData, edge); } } catch (Exception e) { log.error("Exception during loading edge user(s) on sync!", e); } } - private void pushUsersToEdge(PageData pageData, Edge edge, Set pushedEntityIds, StreamObserver outputStream) { + private void syncWidgetsBundleAndWidgetTypes(Edge edge) { + List widgetsBundlesToPush = new ArrayList<>(); + List widgetTypesToPush = new ArrayList<>(); + widgetsBundlesToPush.addAll(widgetsBundleService.findAllTenantWidgetsBundlesByTenantId(edge.getTenantId())); + widgetsBundlesToPush.addAll(widgetsBundleService.findSystemWidgetsBundles(edge.getTenantId())); + try { + for (WidgetsBundle widgetsBundle: widgetsBundlesToPush) { + saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.WIDGETS_BUNDLE, ActionType.ADDED, widgetsBundle.getId(), null); + widgetTypesToPush.addAll(widgetTypeService.findWidgetTypesByTenantIdAndBundleAlias(widgetsBundle.getTenantId(), widgetsBundle.getAlias())); + } + for (WidgetType widgetType: widgetTypesToPush) { + saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.WIDGET_TYPE, ActionType.ADDED, widgetType.getId(), null); + } + } catch (Exception e) { + log.error("Exception during loading widgets bundle(s) and widget type(s) on sync!", e); + } + } + + private void syncAdminSettings(Edge edge) { + try { + AdminSettings systemMailSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "mail"); + saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.ADMIN_SETTINGS, ActionType.UPDATED, null, mapper.valueToTree(systemMailSettings)); + AdminSettings tenantMailSettings = convertToTenantAdminSettings(systemMailSettings.getKey(), (ObjectNode) systemMailSettings.getJsonValue()); + saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.ADMIN_SETTINGS, ActionType.UPDATED, null, mapper.valueToTree(tenantMailSettings)); + AdminSettings systemMailTemplates = loadMailTemplates(); + saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.ADMIN_SETTINGS, ActionType.UPDATED, null, mapper.valueToTree(systemMailTemplates)); + AdminSettings tenantMailTemplates = convertToTenantAdminSettings(systemMailTemplates.getKey(), (ObjectNode) systemMailTemplates.getJsonValue()); + saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.ADMIN_SETTINGS, ActionType.UPDATED, null, mapper.valueToTree(tenantMailTemplates)); + } catch (Exception e) { + log.error("Can't load admin settings", e); + } + } + + private AdminSettings loadMailTemplates() throws Exception { + Map mailTemplates = new HashMap<>(); + Pattern startPattern = Pattern.compile("
"); + Pattern endPattern = Pattern.compile("
"); + File[] files = new DefaultResourceLoader().getResource("classpath:/templates/").getFile().listFiles(); + for (File file: files) { + Map mailTemplate = new HashMap<>(); + String name = validateName(file.getName()); + String stringTemplate = FileUtils.readFileToString(file, StandardCharsets.UTF_8); + Matcher start = startPattern.matcher(stringTemplate); + Matcher end = endPattern.matcher(stringTemplate); + if (start.find() && end.find()) { + String body = StringUtils.substringBetween(stringTemplate, start.group(), end.group()).replaceAll("\t", ""); + String subject = StringUtils.substringBetween(body, "

", "

"); + mailTemplate.put("subject", subject); + mailTemplate.put("body", body); + mailTemplates.put(name, mailTemplate); + } else { + log.error("Can't load mail template from file {}", file.getName()); + } + } + AdminSettings adminSettings = new AdminSettings(); + adminSettings.setId(new AdminSettingsId(Uuids.timeBased())); + adminSettings.setKey("mailTemplates"); + adminSettings.setJsonValue(mapper.convertValue(mailTemplates, JsonNode.class)); + return adminSettings; + } + + private String validateName(String name) throws Exception { + StringBuilder nameBuilder = new StringBuilder(); + name = name.replace(".vm", ""); + String[] nameParts = name.split("\\."); + if (nameParts.length >= 1) { + nameBuilder.append(nameParts[0]); + for (int i = 1; i < nameParts.length; i++) { + String word = WordUtils.capitalize(nameParts[i]); + nameBuilder.append(word); + } + return nameBuilder.toString(); + } else { + throw new Exception("Error during filename validation"); + } + } + + private AdminSettings convertToTenantAdminSettings(String key, ObjectNode jsonValue) { + AdminSettings tenantMailSettings = new AdminSettings(); + jsonValue.put("useSystemMailSettings", true); + tenantMailSettings.setJsonValue(jsonValue); + tenantMailSettings.setKey(key); + return tenantMailSettings; + } + + private void pushUsersToEdge(PageData pageData, Edge edge) { 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()); + saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.USER, ActionType.ADDED, user.getId(), null); } } } - private ListenableFuture 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); - return Futures.transform(relationsListFuture, relationsList -> { - try { - Set uniqueEntityRelations = new HashSet<>(); - if (!relationsList.isEmpty()) { - for (List entityRelations : relationsList) { - if (!entityRelations.isEmpty()) { - uniqueEntityRelations.addAll(entityRelations); + @Override + public ListenableFuture processRuleChainMetadataRequestMsg(Edge edge, RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg) { + SettableFuture futureToSet = SettableFuture.create(); + if (ruleChainMetadataRequestMsg.getRuleChainIdMSB() != 0 && ruleChainMetadataRequestMsg.getRuleChainIdLSB() != 0) { + RuleChainId ruleChainId = + new RuleChainId(new UUID(ruleChainMetadataRequestMsg.getRuleChainIdMSB(), ruleChainMetadataRequestMsg.getRuleChainIdLSB())); + ListenableFuture future = saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.RULE_CHAIN_METADATA, ActionType.ADDED, ruleChainId, null); + Futures.addCallback(future, new FutureCallback() { + @Override + public void onSuccess(@Nullable EdgeEvent result) { + futureToSet.set(null); + } + + @Override + public void onFailure(Throwable t) { + log.error("Can't save edge event [{}]", ruleChainMetadataRequestMsg, t); + futureToSet.setException(t); + } + }, dbCallbackExecutorService); + } + return futureToSet; + } + + @Override + public ListenableFuture processAttributesRequestMsg(Edge edge, AttributesRequestMsg attributesRequestMsg) { + EntityId entityId = EntityIdFactory.getByTypeAndUuid( + EntityType.valueOf(attributesRequestMsg.getEntityType()), + new UUID(attributesRequestMsg.getEntityIdMSB(), attributesRequestMsg.getEntityIdLSB())); + final EdgeEventType type = getEdgeQueueTypeByEntityType(entityId.getEntityType()); + if (type != null) { + SettableFuture futureToSet = SettableFuture.create(); + ListenableFuture> ssAttrFuture = attributesService.findAll(edge.getTenantId(), entityId, DataConstants.SERVER_SCOPE); + Futures.addCallback(ssAttrFuture, new FutureCallback>() { + @Override + public void onSuccess(@Nullable List ssAttributes) { + if (ssAttributes != null && !ssAttributes.isEmpty()) { + try { + Map entityData = new HashMap<>(); + ObjectNode attributes = mapper.createObjectNode(); + for (AttributeKvEntry attr : ssAttributes) { + if (attr.getDataType() == DataType.BOOLEAN && attr.getBooleanValue().isPresent()) { + attributes.put(attr.getKey(), attr.getBooleanValue().get()); + } else if (attr.getDataType() == DataType.DOUBLE && attr.getDoubleValue().isPresent()) { + attributes.put(attr.getKey(), attr.getDoubleValue().get()); + } else if (attr.getDataType() == DataType.LONG && attr.getLongValue().isPresent()) { + attributes.put(attr.getKey(), attr.getLongValue().get()); + } else { + attributes.put(attr.getKey(), attr.getValueAsString()); + } + } + entityData.put("kv", attributes); + entityData.put("scope", DataConstants.SERVER_SCOPE); + JsonNode body = mapper.valueToTree(entityData); + log.debug("Sending attributes data msg, entityId [{}], attributes [{}]", entityId, body); + saveEdgeEvent(edge.getTenantId(), + edge.getId(), + type, + ActionType.ATTRIBUTES_UPDATED, + entityId, + body); + } catch (Exception e) { + log.error("[{}] Failed to send attribute updates to the edge", edge.getName(), e); + throw new RuntimeException("[" + edge.getName() + "] Failed to send attribute updates to the edge", e); + } } + futureToSet.set(null); } - } - if (!uniqueEntityRelations.isEmpty()) { - log.trace("[{}] [{}] relation(s) are going to be pushed to edge.", edge.getId(), uniqueEntityRelations.size()); - for (EntityRelation relation : uniqueEntityRelations) { + + @Override + public void onFailure(Throwable t) { + log.error("Can't save attributes [{}]", attributesRequestMsg, t); + futureToSet.setException(t); + } + }, dbCallbackExecutorService); + return futureToSet; + // TODO: voba - push shared attributes to edge? + // ListenableFuture> shAttrFuture = attributesService.findAll(edge.getTenantId(), entityId, DataConstants.SHARED_SCOPE); + // ListenableFuture> clAttrFuture = attributesService.findAll(edge.getTenantId(), entityId, DataConstants.CLIENT_SCOPE); + } else { + return Futures.immediateFuture(null); + } + } + + private EdgeEventType getEdgeQueueTypeByEntityType(EntityType entityType) { + switch (entityType) { + case DEVICE: + return EdgeEventType.DEVICE; + case ASSET: + return EdgeEventType.ASSET; + case ENTITY_VIEW: + return EdgeEventType.ENTITY_VIEW; + default: + return null; + } + } + + @Override + public ListenableFuture processRelationRequestMsg(Edge edge, RelationRequestMsg relationRequestMsg) { + EntityId entityId = EntityIdFactory.getByTypeAndUuid( + EntityType.valueOf(relationRequestMsg.getEntityType()), + new UUID(relationRequestMsg.getEntityIdMSB(), relationRequestMsg.getEntityIdLSB())); + + List>> futures = new ArrayList<>(); + futures.add(findRelationByQuery(edge, entityId, EntitySearchDirection.FROM)); + futures.add(findRelationByQuery(edge, entityId, EntitySearchDirection.TO)); + ListenableFuture>> relationsListFuture = Futures.allAsList(futures); + return Futures.transform(relationsListFuture, relationsList -> { + try { + if (relationsList != null && !relationsList.isEmpty()) { + for (List entityRelations : relationsList) { + log.trace("[{}] [{}] [{}] relation(s) are going to be pushed to edge.", edge.getId(), entityId, entityRelations.size()); + for (EntityRelation relation : entityRelations) { 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()); + if (!relation.getFrom().getEntityType().equals(EntityType.EDGE) && + !relation.getTo().getEntityType().equals(EntityType.EDGE)) { + saveEdgeEvent(edge.getTenantId(), + edge.getId(), + EdgeEventType.RELATION, + ActionType.ADDED, + null, + mapper.valueToTree(relation)); + } } 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()); - } else { - return Futures.immediateFuture(null); - } + } catch (Exception e) { + log.error("Exception during loading relation(s) to edge on sync!", e); + throw new RuntimeException("Exception during loading relation(s) to edge on sync!", e); + } + return null; + }, dbCallbackExecutorService); } - private ListenableFuture> syncRelations(Edge edge, EntityId entityId, EntitySearchDirection direction) { + private ListenableFuture> findRelationByQuery(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) { - RuleChainId ruleChainId = new RuleChainId(new UUID(ruleChainMetadataRequestMsg.getRuleChainIdMSB(), ruleChainMetadataRequestMsg.getRuleChainIdLSB())); - RuleChainMetaData ruleChainMetaData = ruleChainService.loadRuleChainMetaData(edge.getTenantId(), ruleChainId); - RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = - ruleChainUpdateMsgConstructor.constructRuleChainMetadataUpdatedMsg( - UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, - ruleChainMetaData); - if (ruleChainMetadataUpdateMsg != null) { - EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() - .setRuleChainMetadataUpdateMsg(ruleChainMetadataUpdateMsg) - .build(); - outputStream.onNext(ResponseMsg.newBuilder() - .setEntityUpdateMsg(entityUpdateMsg) - .build()); - } + public ListenableFuture processDeviceCredentialsRequestMsg(Edge edge, DeviceCredentialsRequestMsg deviceCredentialsRequestMsg) { + SettableFuture futureToSet = SettableFuture.create(); + if (deviceCredentialsRequestMsg.getDeviceIdMSB() != 0 && deviceCredentialsRequestMsg.getDeviceIdLSB() != 0) { + DeviceId deviceId = new DeviceId(new UUID(deviceCredentialsRequestMsg.getDeviceIdMSB(), deviceCredentialsRequestMsg.getDeviceIdLSB())); + ListenableFuture future = saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.DEVICE, ActionType.CREDENTIALS_UPDATED, deviceId, null); + Futures.addCallback(future, new FutureCallback() { + @Override + public void onSuccess(@Nullable EdgeEvent result) { + futureToSet.set(null); + } + + @Override + public void onFailure(Throwable t) { + log.error("Can't save edge event [{}]", deviceCredentialsRequestMsg, t); + futureToSet.setException(t); + } + }, dbCallbackExecutorService); + } + return futureToSet; + } + + @Override + public ListenableFuture processUserCredentialsRequestMsg(Edge edge, UserCredentialsRequestMsg userCredentialsRequestMsg) { + SettableFuture futureToSet = SettableFuture.create(); + if (userCredentialsRequestMsg.getUserIdMSB() != 0 && userCredentialsRequestMsg.getUserIdLSB() != 0) { + UserId userId = new UserId(new UUID(userCredentialsRequestMsg.getUserIdMSB(), userCredentialsRequestMsg.getUserIdLSB())); + ListenableFuture future = saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.USER, ActionType.CREDENTIALS_UPDATED, userId, null); + Futures.addCallback(future, new FutureCallback() { + @Override + public void onSuccess(@Nullable EdgeEvent result) { + futureToSet.set(null); + } + + @Override + public void onFailure(Throwable t) { + log.error("Can't save edge event [{}]", userCredentialsRequestMsg, t); + futureToSet.setException(t); + } + }, dbCallbackExecutorService); + } + return futureToSet; + } + + private ListenableFuture saveEdgeEvent(TenantId tenantId, + EdgeId edgeId, + EdgeEventType type, + ActionType action, + EntityId entityId, + JsonNode body) { + log.debug("Pushing edge event to edge queue. tenantId [{}], edgeId [{}], type [{}], action[{}], entityId [{}], body [{}]", + tenantId, edgeId, type, action, entityId, body); + + EdgeEvent edgeEvent = new EdgeEvent(); + edgeEvent.setTenantId(tenantId); + edgeEvent.setEdgeId(edgeId); + edgeEvent.setType(type); + edgeEvent.setAction(action.name()); + if (entityId != null) { + edgeEvent.setEntityId(entityId.getId()); } + edgeEvent.setBody(body); + return edgeEventService.saveAsync(edgeEvent); } } 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 84b4d9ffff..a4df4e227c 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 @@ -15,15 +15,25 @@ */ package org.thingsboard.server.service.edge.rpc.init; -import io.grpc.stub.StreamObserver; +import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.edge.Edge; -import org.thingsboard.server.gen.edge.ResponseMsg; +import org.thingsboard.server.gen.edge.AttributesRequestMsg; +import org.thingsboard.server.gen.edge.DeviceCredentialsRequestMsg; +import org.thingsboard.server.gen.edge.RelationRequestMsg; import org.thingsboard.server.gen.edge.RuleChainMetadataRequestMsg; -import org.thingsboard.server.service.edge.EdgeContextComponent; +import org.thingsboard.server.gen.edge.UserCredentialsRequestMsg; public interface SyncEdgeService { - void sync(EdgeContextComponent ctx, Edge edge, StreamObserver outputStream); + void sync(Edge edge); - void syncRuleChainMetadata(Edge edge, RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg, StreamObserver outputStream); + ListenableFuture processRuleChainMetadataRequestMsg(Edge edge, RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg); + + ListenableFuture processAttributesRequestMsg(Edge edge, AttributesRequestMsg attributesRequestMsg); + + ListenableFuture processRelationRequestMsg(Edge edge, RelationRequestMsg relationRequestMsg); + + ListenableFuture processDeviceCredentialsRequestMsg(Edge edge, DeviceCredentialsRequestMsg deviceCredentialsRequestMsg); + + ListenableFuture processUserCredentialsRequestMsg(Edge edge, UserCredentialsRequestMsg userCredentialsRequestMsg); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/AlarmProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/AlarmProcessor.java new file mode 100644 index 0000000000..c24b127e73 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/AlarmProcessor.java @@ -0,0 +1,98 @@ +/** + * 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.processor; + +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.EntityType; +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.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.gen.edge.AlarmUpdateMsg; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@Slf4j +@TbCoreComponent +public class AlarmProcessor extends BaseProcessor { + + public ListenableFuture onAlarmUpdate(TenantId tenantId, AlarmUpdateMsg alarmUpdateMsg) { + EntityId originatorId = getAlarmOriginator(tenantId, alarmUpdateMsg.getOriginatorName(), + EntityType.valueOf(alarmUpdateMsg.getOriginatorType())); + if (originatorId == null) { + return Futures.immediateFuture(null); + } + try { + Alarm existentAlarm = alarmService.findLatestByOriginatorAndType(tenantId, originatorId, alarmUpdateMsg.getType()).get(); + switch (alarmUpdateMsg.getMsgType()) { + case ENTITY_CREATED_RPC_MESSAGE: + case ENTITY_UPDATED_RPC_MESSAGE: + if (existentAlarm == null || existentAlarm.getStatus().isCleared()) { + existentAlarm = new Alarm(); + existentAlarm.setTenantId(tenantId); + existentAlarm.setType(alarmUpdateMsg.getName()); + existentAlarm.setOriginator(originatorId); + existentAlarm.setSeverity(AlarmSeverity.valueOf(alarmUpdateMsg.getSeverity())); + existentAlarm.setStartTs(alarmUpdateMsg.getStartTs()); + existentAlarm.setClearTs(alarmUpdateMsg.getClearTs()); + existentAlarm.setPropagate(alarmUpdateMsg.getPropagate()); + } + existentAlarm.setStatus(AlarmStatus.valueOf(alarmUpdateMsg.getStatus())); + existentAlarm.setAckTs(alarmUpdateMsg.getAckTs()); + existentAlarm.setEndTs(alarmUpdateMsg.getEndTs()); + existentAlarm.setDetails(mapper.readTree(alarmUpdateMsg.getDetails())); + alarmService.createOrUpdateAlarm(existentAlarm); + break; + case ALARM_ACK_RPC_MESSAGE: + if (existentAlarm != null) { + alarmService.ackAlarm(tenantId, existentAlarm.getId(), alarmUpdateMsg.getAckTs()); + } + break; + case ALARM_CLEAR_RPC_MESSAGE: + if (existentAlarm != null) { + alarmService.clearAlarm(tenantId, existentAlarm.getId(), mapper.readTree(alarmUpdateMsg.getDetails()), alarmUpdateMsg.getAckTs()); + } + break; + case ENTITY_DELETED_RPC_MESSAGE: + if (existentAlarm != null) { + alarmService.deleteAlarm(tenantId, existentAlarm.getId()); + } + break; + } + return Futures.immediateFuture(null); + } catch (Exception e) { + log.error("Failed to process alarm update msg [{}]", alarmUpdateMsg, e); + return Futures.immediateFailedFuture(new RuntimeException("Failed to process alarm update msg", e)); + } + } + + private EntityId getAlarmOriginator(TenantId tenantId, String entityName, EntityType entityType) { + switch (entityType) { + case DEVICE: + return deviceService.findDeviceByTenantIdAndName(tenantId, entityName).getId(); + case ASSET: + return assetService.findAssetByTenantIdAndName(tenantId, entityName).getId(); + case ENTITY_VIEW: + return entityViewService.findEntityViewByTenantIdAndName(tenantId, entityName).getId(); + default: + return null; + } + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseProcessor.java new file mode 100644 index 0000000000..576c8ba667 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseProcessor.java @@ -0,0 +1,113 @@ +/** + * 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.processor; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.util.concurrent.ListenableFuture; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +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.dao.alarm.AlarmService; +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.EdgeEventService; +import org.thingsboard.server.dao.entityview.EntityViewService; +import org.thingsboard.server.dao.relation.RelationService; +import org.thingsboard.server.dao.user.UserService; +import org.thingsboard.server.service.executors.DbCallbackExecutorService; +import org.thingsboard.server.service.queue.TbClusterService; +import org.thingsboard.server.service.rpc.TbRuleEngineDeviceRpcService; +import org.thingsboard.server.service.state.DeviceStateService; + +@Slf4j +public abstract class BaseProcessor { + + protected static final ObjectMapper mapper = new ObjectMapper(); + + @Autowired + protected AlarmService alarmService; + + @Autowired + protected DeviceService deviceService; + + @Autowired + protected DashboardService dashboardService; + + @Autowired + protected AssetService assetService; + + @Autowired + protected EntityViewService entityViewService; + + @Autowired + protected CustomerService customerService; + + @Autowired + protected UserService userService; + + @Autowired + protected RelationService relationService; + + @Autowired + protected DeviceCredentialsService deviceCredentialsService; + + @Autowired + protected AttributesService attributesService; + + @Autowired + protected TbClusterService tbClusterService; + + @Autowired + protected DeviceStateService deviceStateService; + + @Autowired + protected EdgeEventService edgeEventService; + + @Autowired + protected DbCallbackExecutorService dbCallbackExecutorService; + + protected ListenableFuture saveEdgeEvent(TenantId tenantId, + EdgeId edgeId, + EdgeEventType type, + ActionType action, + EntityId entityId, + JsonNode body) { + log.debug("Pushing event to edge queue. tenantId [{}], edgeId [{}], type[{}], " + + "action [{}], entityId [{}], body [{}]", + tenantId, edgeId, type, action, entityId, body); + + EdgeEvent edgeEvent = new EdgeEvent(); + edgeEvent.setTenantId(tenantId); + edgeEvent.setEdgeId(edgeId); + edgeEvent.setType(type); + edgeEvent.setAction(action.name()); + if (entityId != null) { + edgeEvent.setEntityId(entityId.getId()); + } + edgeEvent.setBody(body); + return edgeEventService.saveAsync(edgeEvent); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/DeviceProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/DeviceProcessor.java new file mode 100644 index 0000000000..0e5b88aa2a --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/DeviceProcessor.java @@ -0,0 +1,252 @@ +/** + * 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.processor; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.SettableFuture; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang.RandomStringUtils; +import org.apache.commons.lang.StringUtils; +import org.springframework.stereotype.Component; +import org.thingsboard.rule.engine.api.RpcError; +import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.Device; +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.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.TenantId; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; +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.dao.util.mapping.JacksonUtil; +import org.thingsboard.server.gen.edge.DeviceCredentialsUpdateMsg; +import org.thingsboard.server.gen.edge.DeviceRpcCallMsg; +import org.thingsboard.server.gen.edge.DeviceUpdateMsg; +import org.thingsboard.server.queue.TbQueueCallback; +import org.thingsboard.server.queue.TbQueueMsgMetadata; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.rpc.FromDeviceRpcResponse; + +import java.util.UUID; +import java.util.concurrent.locks.ReentrantLock; + +@Component +@Slf4j +@TbCoreComponent +public class DeviceProcessor extends BaseProcessor { + + private static final ReentrantLock deviceCreationLock = new ReentrantLock(); + + public ListenableFuture onDeviceUpdate(TenantId tenantId, Edge edge, DeviceUpdateMsg 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 = deviceService.findDeviceByTenantIdAndName(tenantId, deviceName); + if (device != null) { + // device with this name already exists on the cloud - update ID on the edge + if (!device.getId().equals(edgeDeviceId)) { + saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, ActionType.ENTITY_EXISTS_REQUEST, device.getId(), null); + } + } else { + Device deviceById = deviceService.findDeviceById(edge.getTenantId(), edgeDeviceId); + if (deviceById != null) { + // this ID already used by other device - create new device and update ID on the edge + device = createDevice(tenantId, edge, deviceUpdateMsg); + saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, ActionType.ENTITY_EXISTS_REQUEST, device.getId(), null); + } else { + device = createDevice(tenantId, edge, deviceUpdateMsg); + } + } + // TODO: voba - assign device only in case device is not assigned yet. Missing functionality to check this relation prior assignment + deviceService.assignDeviceToEdge(edge.getTenantId(), device.getId(), edge.getId()); + break; + case ENTITY_UPDATED_RPC_MESSAGE: + updateDevice(tenantId, edge, deviceUpdateMsg); + break; + case ENTITY_DELETED_RPC_MESSAGE: + Device deviceToDelete = deviceService.findDeviceById(tenantId, edgeDeviceId); + if (deviceToDelete != null) { + deviceService.unassignDeviceFromEdge(tenantId, edgeDeviceId, edge.getId()); + } + break; + case UNRECOGNIZED: + log.error("Unsupported msg type {}", deviceUpdateMsg.getMsgType()); + return Futures.immediateFailedFuture(new RuntimeException("Unsupported msg type " + deviceUpdateMsg.getMsgType())); + } + return Futures.immediateFuture(null); + } + + + public ListenableFuture onDeviceCredentialsUpdate(TenantId tenantId, DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg) { + log.debug("Executing onDeviceCredentialsUpdate, deviceCredentialsUpdateMsg [{}]", deviceCredentialsUpdateMsg); + DeviceId deviceId = new DeviceId(new UUID(deviceCredentialsUpdateMsg.getDeviceIdMSB(), deviceCredentialsUpdateMsg.getDeviceIdLSB())); + ListenableFuture deviceFuture = deviceService.findDeviceByIdAsync(tenantId, deviceId); + return Futures.transform(deviceFuture, device -> { + if (device != null) { + log.debug("Updating device credentials for device [{}]. New device credentials Id [{}], value [{}]", + device.getName(), deviceCredentialsUpdateMsg.getCredentialsId(), deviceCredentialsUpdateMsg.getCredentialsValue()); + try { + DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(tenantId, device.getId()); + deviceCredentials.setCredentialsType(DeviceCredentialsType.valueOf(deviceCredentialsUpdateMsg.getCredentialsType())); + deviceCredentials.setCredentialsId(deviceCredentialsUpdateMsg.getCredentialsId()); + deviceCredentials.setCredentialsValue(deviceCredentialsUpdateMsg.getCredentialsValue()); + deviceCredentialsService.updateDeviceCredentials(tenantId, deviceCredentials); + } catch (Exception e) { + log.error("Can't update device credentials for device [{}], deviceCredentialsUpdateMsg [{}]", device.getName(), deviceCredentialsUpdateMsg, e); + throw new RuntimeException(e); + } + } + return null; + }, dbCallbackExecutorService); + } + + + private void updateDevice(TenantId tenantId, Edge edge, DeviceUpdateMsg deviceUpdateMsg) { + DeviceId deviceId = new DeviceId(new UUID(deviceUpdateMsg.getIdMSB(), deviceUpdateMsg.getIdLSB())); + Device device = deviceService.findDeviceById(tenantId, deviceId); + device.setName(deviceUpdateMsg.getName()); + device.setType(deviceUpdateMsg.getType()); + device.setLabel(deviceUpdateMsg.getLabel()); + device.setAdditionalInfo(JacksonUtil.toJsonNode(deviceUpdateMsg.getAdditionalInfo())); + deviceService.saveDevice(device); + + saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, ActionType.CREDENTIALS_REQUEST, deviceId, null); + } + + private Device createDevice(TenantId tenantId, Edge edge, 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.setAdditionalInfo(JacksonUtil.toJsonNode(deviceUpdateMsg.getAdditionalInfo())); + device = deviceService.saveDevice(device); + createDeviceCredentials(device); + createRelationFromEdge(tenantId, edge.getId(), device.getId()); + deviceStateService.onDeviceAdded(device); + pushDeviceCreatedEventToRuleEngine(tenantId, edge, device); + + saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, ActionType.CREDENTIALS_REQUEST, deviceId, null); + } finally { + deviceCreationLock.unlock(); + } + return device; + } + + private void createRelationFromEdge(TenantId tenantId, EdgeId edgeId, EntityId entityId) { + EntityRelation relation = new EntityRelation(); + relation.setFrom(edgeId); + relation.setTo(entityId); + relation.setTypeGroup(RelationTypeGroup.COMMON); + relation.setType(EntityRelation.EDGE_TYPE); + relationService.saveRelation(tenantId, relation); + } + + private void createDeviceCredentials(Device device) { + DeviceCredentials deviceCredentials = new DeviceCredentials(); + deviceCredentials.setDeviceId(device.getId()); + deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN); + deviceCredentials.setCredentialsId(RandomStringUtils.randomAlphanumeric(20)); + deviceCredentialsService.createDeviceCredentials(device.getTenantId(), deviceCredentials); + } + + private void pushDeviceCreatedEventToRuleEngine(TenantId tenantId, Edge edge, Device device) { + try { + DeviceId deviceId = device.getId(); + ObjectNode entityNode = mapper.valueToTree(device); + TbMsg tbMsg = TbMsg.newMsg(DataConstants.ENTITY_CREATED, deviceId, + getActionTbMsgMetaData(edge, device.getCustomerId()), TbMsgDataType.JSON, mapper.writeValueAsString(entityNode)); + tbClusterService.pushMsgToRuleEngine(tenantId, deviceId, tbMsg, new TbQueueCallback() { + @Override + public void onSuccess(TbQueueMsgMetadata metadata) { + // TODO: voba - handle success + log.debug("Successfully send ENTITY_CREATED EVENT to rule engine [{}]", device); + } + + @Override + public void onFailure(Throwable t) { + // TODO: voba - handle failure + log.debug("Failed to send ENTITY_CREATED EVENT to rule engine [{}]", device, t); + } + + ; + }); + } catch (JsonProcessingException | IllegalArgumentException e) { + log.warn("[{}] Failed to push device action to rule engine: {}", device.getId(), DataConstants.ENTITY_CREATED, e); + } + } + + + private TbMsgMetaData getActionTbMsgMetaData(Edge edge, CustomerId customerId) { + TbMsgMetaData metaData = getTbMsgMetaData(edge); + if (customerId != null && !customerId.isNullUid()) { + metaData.putValue("customerId", customerId.toString()); + } + return metaData; + } + + + private TbMsgMetaData getTbMsgMetaData(Edge edge) { + TbMsgMetaData metaData = new TbMsgMetaData(); + metaData.putValue("edgeId", edge.getId().toString()); + metaData.putValue("edgeName", edge.getName()); + return metaData; + } + + public ListenableFuture processDeviceRpcCallResponseMsg(TenantId tenantId, DeviceRpcCallMsg deviceRpcCallMsg) { + SettableFuture futureToSet = SettableFuture.create(); + UUID uuid = new UUID(deviceRpcCallMsg.getRequestIdMSB(), deviceRpcCallMsg.getRequestIdLSB()); + FromDeviceRpcResponse response; + if (!StringUtils.isEmpty(deviceRpcCallMsg.getResponseMsg().getError())) { + response = new FromDeviceRpcResponse(uuid, null, RpcError.valueOf(deviceRpcCallMsg.getResponseMsg().getError())); + } else { + response = new FromDeviceRpcResponse(uuid, deviceRpcCallMsg.getResponseMsg().getResponse(), null); + } + TbQueueCallback callback = new TbQueueCallback() { + @Override + public void onSuccess(TbQueueMsgMetadata metadata) { + futureToSet.set(null); + } + + @Override + public void onFailure(Throwable t) { + log.error("Can't process push notification to core [{}]", deviceRpcCallMsg, t); + futureToSet.setException(t); + } + }; + tbClusterService.pushNotificationToCore(deviceRpcCallMsg.getOriginServiceId(), response, callback); + return futureToSet; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/RelationProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/RelationProcessor.java new file mode 100644 index 0000000000..21849e74cd --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/RelationProcessor.java @@ -0,0 +1,104 @@ +/** + * 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.processor; + +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; +import org.thingsboard.server.common.data.exception.ThingsboardException; +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.EntityId; +import org.thingsboard.server.common.data.id.EntityIdFactory; +import org.thingsboard.server.common.data.id.EntityViewId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; +import org.thingsboard.server.gen.edge.RelationUpdateMsg; +import org.thingsboard.server.queue.util.TbCoreComponent; + +import java.util.UUID; + +@Component +@Slf4j +@TbCoreComponent +public class RelationProcessor extends BaseProcessor { + + public ListenableFuture onRelationUpdate(TenantId tenantId, RelationUpdateMsg relationUpdateMsg) { + log.info("onRelationUpdate {}", relationUpdateMsg); + try { + EntityRelation entityRelation = new EntityRelation(); + + UUID fromUUID = new UUID(relationUpdateMsg.getFromIdMSB(), relationUpdateMsg.getFromIdLSB()); + EntityId fromId = EntityIdFactory.getByTypeAndUuid(EntityType.valueOf(relationUpdateMsg.getFromEntityType()), fromUUID); + entityRelation.setFrom(fromId); + + UUID toUUID = new UUID(relationUpdateMsg.getToIdMSB(), relationUpdateMsg.getToIdLSB()); + EntityId toId = EntityIdFactory.getByTypeAndUuid(EntityType.valueOf(relationUpdateMsg.getToEntityType()), toUUID); + entityRelation.setTo(toId); + + entityRelation.setType(relationUpdateMsg.getType()); + entityRelation.setTypeGroup(RelationTypeGroup.valueOf(relationUpdateMsg.getTypeGroup())); + entityRelation.setAdditionalInfo(mapper.readTree(relationUpdateMsg.getAdditionalInfo())); + switch (relationUpdateMsg.getMsgType()) { + case ENTITY_CREATED_RPC_MESSAGE: + case ENTITY_UPDATED_RPC_MESSAGE: + if (isEntityExists(tenantId, entityRelation.getTo()) + && isEntityExists(tenantId, entityRelation.getFrom())) { + relationService.saveRelationAsync(tenantId, entityRelation); + } + break; + case ENTITY_DELETED_RPC_MESSAGE: + relationService.deleteRelation(tenantId, entityRelation); + break; + case UNRECOGNIZED: + log.error("Unsupported msg type"); + } + return Futures.immediateFuture(null); + } catch (Exception e) { + log.error("Failed to process relation update msg [{}]", relationUpdateMsg, e); + return Futures.immediateFailedFuture(new RuntimeException("Failed to process relation update msg", e)); + } + } + + + private boolean isEntityExists(TenantId tenantId, EntityId entityId) throws ThingsboardException { + switch (entityId.getEntityType()) { + case DEVICE: + return deviceService.findDeviceById(tenantId, new DeviceId(entityId.getId())) != null; + case ASSET: + return assetService.findAssetById(tenantId, new AssetId(entityId.getId())) != null; + case ENTITY_VIEW: + return entityViewService.findEntityViewById(tenantId, new EntityViewId(entityId.getId())) != null; + case CUSTOMER: + return customerService.findCustomerById(tenantId, new CustomerId(entityId.getId())) != null; + case USER: + return userService.findUserById(tenantId, new UserId(entityId.getId())) != null; + case DASHBOARD: + return dashboardService.findDashboardById(tenantId, new DashboardId(entityId.getId())) != null; + default: + throw new ThingsboardException("Unsupported entity type " + entityId.getEntityType(), ThingsboardErrorCode.INVALID_ARGUMENTS); + } + } + + +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/TelemetryProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/TelemetryProcessor.java new file mode 100644 index 0000000000..2fdd5614aa --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/TelemetryProcessor.java @@ -0,0 +1,244 @@ +/** + * 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.processor; + +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.SettableFuture; +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; +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.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.DashboardId; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.EntityViewId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.kv.AttributeKey; +import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.common.msg.session.SessionMsgType; +import org.thingsboard.server.common.transport.adaptor.JsonConverter; +import org.thingsboard.server.common.transport.util.JsonUtils; +import org.thingsboard.server.gen.edge.AttributeDeleteMsg; +import org.thingsboard.server.gen.edge.EntityDataProto; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.queue.TbQueueCallback; +import org.thingsboard.server.queue.TbQueueMsgMetadata; +import org.thingsboard.server.queue.util.TbCoreComponent; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.UUID; + +@Component +@Slf4j +@TbCoreComponent +public class TelemetryProcessor extends BaseProcessor { + + private final Gson gson = new Gson(); + + public List> onTelemetryUpdate(TenantId tenantId, EntityDataProto entityData) { + List> result = new ArrayList<>(); + EntityId entityId = constructEntityId(entityData); + if ((entityData.hasPostAttributesMsg() || entityData.hasPostTelemetryMsg() || entityData.hasAttributesUpdatedMsg()) && entityId != null) { + TbMsgMetaData metaData = constructBaseMsgMetadata(tenantId, entityId); + metaData.putValue(DataConstants.MSG_SOURCE_KEY, DataConstants.EDGE_MSG_SOURCE); + if (entityData.hasPostAttributesMsg()) { + result.add(processPostAttributes(tenantId, entityId, entityData.getPostAttributesMsg(), metaData)); + } + if (entityData.hasAttributesUpdatedMsg()) { + metaData.putValue("scope", entityData.getPostAttributeScope()); + result.add(processAttributesUpdate(tenantId, entityId, entityData.getAttributesUpdatedMsg(), metaData)); + } + if (entityData.hasPostTelemetryMsg()) { + result.add(processPostTelemetry(tenantId, entityId, entityData.getPostTelemetryMsg(), metaData)); + } + } + if (entityData.hasAttributeDeleteMsg()) { + result.add(processAttributeDeleteMsg(tenantId, entityId, entityData.getAttributeDeleteMsg(), entityData.getEntityType())); + } + return result; + } + + private TbMsgMetaData constructBaseMsgMetadata(TenantId tenantId, EntityId entityId) { + TbMsgMetaData metaData = new TbMsgMetaData(); + switch (entityId.getEntityType()) { + case DEVICE: + Device device = deviceService.findDeviceById(tenantId, new DeviceId(entityId.getId())); + if (device != null) { + metaData.putValue("deviceName", device.getName()); + metaData.putValue("deviceType", device.getType()); + } + break; + case ASSET: + Asset asset = assetService.findAssetById(tenantId, new AssetId(entityId.getId())); + if (asset != null) { + metaData.putValue("assetName", asset.getName()); + metaData.putValue("assetType", asset.getType()); + } + break; + case ENTITY_VIEW: + EntityView entityView = entityViewService.findEntityViewById(tenantId, new EntityViewId(entityId.getId())); + if (entityView != null) { + metaData.putValue("entityViewName", entityView.getName()); + metaData.putValue("entityViewType", entityView.getType()); + } + break; + default: + log.debug("Using empty metadata for entityId [{}]", entityId); + break; + } + return metaData; + } + + private ListenableFuture processPostTelemetry(TenantId tenantId, EntityId entityId, TransportProtos.PostTelemetryMsg msg, TbMsgMetaData metaData) { + SettableFuture futureToSet = SettableFuture.create(); + 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)); + tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() { + @Override + public void onSuccess(TbQueueMsgMetadata metadata) { + futureToSet.set(null); + } + + @Override + public void onFailure(Throwable t) { + log.error("Can't process post telemetry [{}]", msg, t); + futureToSet.setException(t); + } + }); + } + return futureToSet; + } + + private ListenableFuture processPostAttributes(TenantId tenantId, EntityId entityId, TransportProtos.PostAttributeMsg msg, TbMsgMetaData metaData) { + SettableFuture futureToSet = SettableFuture.create(); + JsonObject json = JsonUtils.getJsonObject(msg.getKvList()); + TbMsg tbMsg = TbMsg.newMsg(SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), entityId, metaData, gson.toJson(json)); + tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() { + @Override + public void onSuccess(TbQueueMsgMetadata metadata) { + futureToSet.set(null); + } + + @Override + public void onFailure(Throwable t) { + log.error("Can't process post attributes [{}]", msg, t); + futureToSet.setException(t); + } + }); + return futureToSet; + } + + private ListenableFuture processAttributesUpdate(TenantId tenantId, EntityId entityId, TransportProtos.PostAttributeMsg msg, TbMsgMetaData metaData) { + SettableFuture futureToSet = SettableFuture.create(); + JsonObject json = JsonUtils.getJsonObject(msg.getKvList()); + Set attributes = JsonConverter.convertToAttributes(json); + ListenableFuture> future = attributesService.save(tenantId, entityId, metaData.getValue("scope"), new ArrayList<>(attributes)); + Futures.addCallback(future, new FutureCallback>() { + @Override + public void onSuccess(@Nullable List voids) { + TbMsg tbMsg = TbMsg.newMsg(DataConstants.ATTRIBUTES_UPDATED, entityId, metaData, gson.toJson(json)); + tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() { + @Override + public void onSuccess(TbQueueMsgMetadata metadata) { + futureToSet.set(null); + } + + @Override + public void onFailure(Throwable t) { + log.error("Can't process attributes update [{}]", msg, t); + futureToSet.setException(t); + } + }); + } + + @Override + public void onFailure(Throwable t) { + log.error("Can't process attributes update [{}]", msg, t); + futureToSet.setException(t); + } + }, dbCallbackExecutorService); + return futureToSet; + } + + private ListenableFuture processAttributeDeleteMsg(TenantId tenantId, EntityId entityId, AttributeDeleteMsg attributeDeleteMsg, String entityType) { + SettableFuture futureToSet = SettableFuture.create(); + String scope = attributeDeleteMsg.getScope(); + List attributeNames = attributeDeleteMsg.getAttributeNamesList(); + attributesService.removeAll(tenantId, entityId, scope, attributeNames); + if (EntityType.DEVICE.name().equals(entityType)) { + Set attributeKeys = new HashSet<>(); + for (String attributeName : attributeNames) { + attributeKeys.add(new AttributeKey(scope, attributeName)); + } + tbClusterService.pushMsgToCore(DeviceAttributesEventNotificationMsg.onDelete( + tenantId, (DeviceId) entityId, attributeKeys), new TbQueueCallback() { + @Override + public void onSuccess(TbQueueMsgMetadata metadata) { + futureToSet.set(null); + } + + @Override + public void onFailure(Throwable t) { + log.error("Can't process attribute delete msg [{}]", attributeDeleteMsg, t); + futureToSet.setException(t); + } + }); + } + return futureToSet; + } + + 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())); + case TENANT: + return new TenantId(new UUID(entityData.getEntityIdMSB(), entityData.getEntityIdLSB())); + case CUSTOMER: + return new CustomerId(new UUID(entityData.getEntityIdMSB(), entityData.getEntityIdLSB())); + case USER: + return new UserId(new UUID(entityData.getEntityIdMSB(), entityData.getEntityIdLSB())); + default: + log.warn("Unsupported entity type [{}] during construct of entity id. EntityDataProto [{}]", entityData.getEntityType(), entityData); + return null; + } + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/install/AbstractSqlTsDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/AbstractSqlTsDatabaseUpgradeService.java index 5f084c7a9d..ae650592d2 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/AbstractSqlTsDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/AbstractSqlTsDatabaseUpgradeService.java @@ -50,7 +50,7 @@ public abstract class AbstractSqlTsDatabaseUpgradeService { @Autowired protected InstallScripts installScripts; - protected abstract void loadSql(Connection conn, String fileName); + protected abstract void loadSql(Connection conn, String version, String fileName); protected void loadFunctions(Path sqlFile, Connection conn) throws Exception { String sql = new String(Files.readAllBytes(sqlFile), StandardCharsets.UTF_8); 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 17857e2807..0a62395ef0 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 @@ -49,6 +49,8 @@ public class CassandraTsDatabaseUpgradeService extends AbstractCassandraDatabase log.info("Schema updated."); break; case "2.5.0": + case "2.5.4": + case "2.5.5": case "3.1.1": break; default: diff --git a/application/src/main/java/org/thingsboard/server/service/install/DatabaseHelper.java b/application/src/main/java/org/thingsboard/server/service/install/DatabaseHelper.java index aea04bcf56..5c6b63debb 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/DatabaseHelper.java +++ b/application/src/main/java/org/thingsboard/server/service/install/DatabaseHelper.java @@ -57,6 +57,7 @@ public class DatabaseHelper { public static final String DASHBOARD = "dashboard"; public static final String ENTITY_VIEWS = "entity_views"; public static final String ENTITY_VIEW = "entity_view"; + public static final String RULE_CHAIN = "rule_chain"; public static final String ID = "id"; public static final String TITLE = "title"; public static final String TYPE = "type"; diff --git a/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java b/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java index 5a8b88c596..7493a8ecd7 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java +++ b/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java @@ -122,10 +122,6 @@ public class InstallScripts { } } ); - // TODO: voba -// dirStream.forEach( -// path -> loadRuleChainFromFile(tenantId, path) -// ); } } @@ -151,6 +147,11 @@ public class InstallScripts { } + public void createDefaultEdgeRuleChains(TenantId tenantId) { + Path tenantChainsDir = getTenantRuleChainsDir(); + loadRuleChainFromFile(tenantId, tenantChainsDir.resolve("edge_root_rule_chain.json")); + } + public void loadSystemWidgets() throws Exception { Path widgetBundlesDir = Paths.get(getDataDir(), JSON_DIR, SYSTEM_DIR, WIDGET_BUNDLES_DIR); try (DirectoryStream dirStream = Files.newDirectoryStream(widgetBundlesDir, path -> path.toString().endsWith(JSON_EXT))) { diff --git a/application/src/main/java/org/thingsboard/server/service/install/PsqlTsDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/PsqlTsDatabaseUpgradeService.java index 18433c6f53..ce5799a8ac 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/PsqlTsDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/PsqlTsDatabaseUpgradeService.java @@ -94,7 +94,7 @@ public class PsqlTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgradeSe log.info("PostgreSQL version is valid!"); if (isOldSchema(conn, 2004003)) { log.info("Load upgrade functions ..."); - loadSql(conn, LOAD_FUNCTIONS_SQL); + loadSql(conn, "2.4.3", LOAD_FUNCTIONS_SQL); log.info("Updating timeseries schema ..."); executeQuery(conn, CALL_CREATE_PARTITION_TS_KV_TABLE); if (!partitionType.equals("INDEFINITE")) { @@ -179,9 +179,9 @@ public class PsqlTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgradeSe } log.info("Load TTL functions ..."); - loadSql(conn, LOAD_TTL_FUNCTIONS_SQL); + loadSql(conn, "2.4.3", LOAD_TTL_FUNCTIONS_SQL); log.info("Load Drop Partitions functions ..."); - loadSql(conn, LOAD_DROP_PARTITIONS_FUNCTIONS_SQL); + loadSql(conn, "2.4.3", LOAD_DROP_PARTITIONS_FUNCTIONS_SQL); executeQuery(conn, "UPDATE tb_schema_settings SET schema_version = 2005000"); @@ -195,12 +195,18 @@ public class PsqlTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgradeSe executeQuery(conn, "UPDATE tb_schema_settings SET schema_version = 2005001"); } break; + case "2.5.5": + try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { + log.info("Load TTL functions ..."); + loadSql(conn, "2.6.0", LOAD_TTL_FUNCTIONS_SQL); + } + break; case "3.1.1": try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { log.info("Load TTL functions ..."); - loadSql(conn, LOAD_TTL_FUNCTIONS_SQL); + loadSql(conn, "2.4.3", LOAD_TTL_FUNCTIONS_SQL); log.info("Load Drop Partitions functions ..."); - loadSql(conn, LOAD_DROP_PARTITIONS_FUNCTIONS_SQL); + loadSql(conn, "2.4.3", LOAD_DROP_PARTITIONS_FUNCTIONS_SQL); } break; default: @@ -238,8 +244,8 @@ public class PsqlTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgradeSe } @Override - protected void loadSql(Connection conn, String fileName) { - Path schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.4.3", fileName); + protected void loadSql(Connection conn, String version, String fileName) { + Path schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", version, fileName); try { loadFunctions(schemaUpdateFile, conn); log.info("Functions successfully loaded!"); 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 a141837dfc..383236af11 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 @@ -260,7 +260,7 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService log.info("Schema updated."); } break; - case "2.6.0": + case "2.5.5": try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { log.info("Updating schema ..."); schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.5.0", SCHEMA_UPDATE_SQL); diff --git a/application/src/main/java/org/thingsboard/server/service/install/TimescaleTsDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/TimescaleTsDatabaseUpgradeService.java index 756356581a..5d614449de 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/TimescaleTsDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/TimescaleTsDatabaseUpgradeService.java @@ -89,7 +89,7 @@ public class TimescaleTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgr log.info("PostgreSQL version is valid!"); if (isOldSchema(conn, 2004003)) { log.info("Load upgrade functions ..."); - loadSql(conn, LOAD_FUNCTIONS_SQL); + loadSql(conn, "2.4.3", LOAD_FUNCTIONS_SQL); log.info("Updating timescale schema ..."); executeQuery(conn, CALL_CREATE_TS_KV_LATEST_TABLE); executeQuery(conn, CALL_CREATE_NEW_TENANT_TS_KV_TABLE); @@ -165,7 +165,7 @@ public class TimescaleTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgr } log.info("Load TTL functions ..."); - loadSql(conn, LOAD_TTL_FUNCTIONS_SQL); + loadSql(conn, "2.4.3", LOAD_TTL_FUNCTIONS_SQL); executeQuery(conn, "UPDATE tb_schema_settings SET schema_version = 2005000"); log.info("schema timescale updated!"); @@ -179,6 +179,8 @@ public class TimescaleTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgr break; case "3.1.1": break; + case "2.5.5": + break; default: throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion); } @@ -200,8 +202,8 @@ public class TimescaleTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgr } @Override - protected void loadSql(Connection conn, String fileName) { - Path schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.4.3", fileName); + protected void loadSql(Connection conn, String version, String fileName) { + Path schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", version, fileName); try { loadFunctions(schemaUpdateFile, conn); log.info("Functions successfully loaded!"); diff --git a/application/src/main/java/org/thingsboard/server/service/install/cql/CassandraDbHelper.java b/application/src/main/java/org/thingsboard/server/service/install/cql/CassandraDbHelper.java index 7e0ee6c539..58a0944685 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/cql/CassandraDbHelper.java +++ b/application/src/main/java/org/thingsboard/server/service/install/cql/CassandraDbHelper.java @@ -37,7 +37,6 @@ import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.time.Instant; import java.util.ArrayList; -import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.UUID; @@ -159,6 +158,8 @@ public class CassandraDbHelper { str = new Float(row.getFloat(index)).toString(); } else if (type.getProtocolCode() == ProtocolConstants.DataType.TIMESTAMP) { str = ""+row.getInstant(index).toEpochMilli(); + } else if (type.getProtocolCode() == ProtocolConstants.DataType.BOOLEAN) { + str = new Boolean(row.getBoolean(index)).toString(); } else { str = row.getString(index); } @@ -207,6 +208,8 @@ public class CassandraDbHelper { boundStatementBuilder.setFloat(column, Float.valueOf(value)); } else if (type.getProtocolCode() == ProtocolConstants.DataType.TIMESTAMP) { boundStatementBuilder.setInstant(column, Instant.ofEpochMilli(Long.valueOf(value))); + } else if (type.getProtocolCode() == ProtocolConstants.DataType.BOOLEAN) { + boundStatementBuilder.setBoolean(column, Boolean.valueOf(value)); } else { boundStatementBuilder.setString(column, value); } diff --git a/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java b/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java index bc86857c4d..c424f14f3b 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java @@ -15,9 +15,7 @@ */ package org.thingsboard.server.service.install.update; -import com.fasterxml.jackson.databind.JsonNode; 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; @@ -25,16 +23,12 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; -import org.springframework.util.StringUtils; import org.thingsboard.rule.engine.profile.TbDeviceProfileNode; import org.thingsboard.rule.engine.profile.TbDeviceProfileNodeConfiguration; import org.thingsboard.server.common.data.EntityView; -import org.thingsboard.server.common.data.SearchTextBased; import org.thingsboard.server.common.data.Tenant; -import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.id.UUIDBased; import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; import org.thingsboard.server.common.data.kv.ReadTsKvQuery; import org.thingsboard.server.common.data.kv.TsKvEntry; @@ -50,7 +44,6 @@ import org.thingsboard.server.dao.timeseries.TimeseriesService; import org.thingsboard.server.dao.util.mapping.JacksonUtil; import org.thingsboard.server.service.install.InstallScripts; -import javax.annotation.Nullable; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -58,7 +51,6 @@ import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; import static org.apache.commons.lang.StringUtils.isBlank; -import static org.thingsboard.server.service.install.DatabaseHelper.objectMapper; @Service @Profile("install") @@ -87,6 +79,10 @@ public class DefaultDataUpdateService implements DataUpdateService { log.info("Updating data from version 1.4.0 to 2.0.0 ..."); tenantsDefaultRuleChainUpdater.updateEntities(null); break; + case "2.5.5": + log.info("Updating data from version 2.5.5 to 2.6.0 ..."); + tenantsDefaultEdgeRuleChainUpdater.updateEntities(null); + break; case "3.0.1": log.info("Updating data from version 3.0.1 to 3.1.0 ..."); tenantsEntityViewsUpdater.updateEntities(null); @@ -121,6 +117,27 @@ public class DefaultDataUpdateService implements DataUpdateService { } }; + private PaginatedUpdater tenantsDefaultEdgeRuleChainUpdater = + new PaginatedUpdater() { + + @Override + protected PageData findEntities(String region, PageLink pageLink) { + return tenantService.findTenants(pageLink); + } + + @Override + protected void updateEntity(Tenant tenant) { + try { + RuleChain defaultEdgeRuleChain = ruleChainService.getDefaultRootEdgeRuleChain(tenant.getId()); + if (defaultEdgeRuleChain == null) { + installScripts.createDefaultEdgeRuleChains(tenant.getId()); + } + } catch (Exception e) { + log.error("Unable to update Tenant", e); + } + } + }; + private PaginatedUpdater tenantsRootRuleChainUpdater = new PaginatedUpdater() { diff --git a/application/src/main/java/org/thingsboard/server/service/queue/TbCoreConsumerStats.java b/application/src/main/java/org/thingsboard/server/service/queue/TbCoreConsumerStats.java index 45914cead1..a19b7d6c2e 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/TbCoreConsumerStats.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/TbCoreConsumerStats.java @@ -35,9 +35,9 @@ public class TbCoreConsumerStats { public static final String SUBSCRIPTION_INFO = "subInfo"; public static final String DEVICE_CLAIMS = "claimDevice"; public static final String DEVICE_STATES = "deviceState"; - public static final String EDGE_EVENTS = "edgeEvents"; public static final String SUBSCRIPTION_MSGS = "subMsgs"; public static final String TO_CORE_NOTIFICATIONS = "coreNfs"; + public static final String EDGE_NOTIFICATIONS = "edgeNfs"; private final StatsCounter totalCounter; private final StatsCounter sessionEventCounter; @@ -49,9 +49,9 @@ public class TbCoreConsumerStats { private final StatsCounter claimDeviceCounter; private final StatsCounter deviceStateCounter; - private final StatsCounter edgeNotificationCounter; private final StatsCounter subscriptionMsgCounter; private final StatsCounter toCoreNotificationsCounter; + private final StatsCounter edgeNotificationsCounter; private final List counters = new ArrayList<>(); @@ -67,10 +67,9 @@ public class TbCoreConsumerStats { this.subscriptionInfoCounter = statsFactory.createStatsCounter(statsKey, SUBSCRIPTION_INFO); this.claimDeviceCounter = statsFactory.createStatsCounter(statsKey, DEVICE_CLAIMS); this.deviceStateCounter = statsFactory.createStatsCounter(statsKey, DEVICE_STATES); - this.edgeNotificationCounter = statsFactory.createStatsCounter(statsKey, EDGE_EVENTS); this.subscriptionMsgCounter = statsFactory.createStatsCounter(statsKey, SUBSCRIPTION_MSGS); this.toCoreNotificationsCounter = statsFactory.createStatsCounter(statsKey, TO_CORE_NOTIFICATIONS); - + this.edgeNotificationsCounter = statsFactory.createStatsCounter(statsKey, EDGE_NOTIFICATIONS); counters.add(totalCounter); counters.add(sessionEventCounter); @@ -82,9 +81,9 @@ public class TbCoreConsumerStats { counters.add(claimDeviceCounter); counters.add(deviceStateCounter); - counters.add(edgeNotificationCounter); counters.add(subscriptionMsgCounter); counters.add(toCoreNotificationsCounter); + counters.add(edgeNotificationsCounter); } public void log(TransportProtos.TransportToDeviceActorMsg msg) { @@ -119,7 +118,7 @@ public class TbCoreConsumerStats { public void log(TransportProtos.EdgeNotificationMsgProto msg) { totalCounter.increment(); - edgeNotificationCounter.increment(); + edgeNotificationsCounter.increment(); } public void log(TransportProtos.SubscriptionMgrMsgProto msg) { diff --git a/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbRuleEngineRpcService.java b/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbRuleEngineRpcService.java index 80d47a84e8..9ebbed0e44 100644 --- a/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbRuleEngineRpcService.java +++ b/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbRuleEngineRpcService.java @@ -151,7 +151,8 @@ public class DefaultTbRuleEngineRpcService implements TbRuleEngineDeviceRpcServi } } - private void sendRpcResponseToTbCore(String originServiceId, FromDeviceRpcResponse response) { + @Override + public void sendRpcResponseToTbCore(String originServiceId, FromDeviceRpcResponse response) { if (serviceId.equals(originServiceId)) { if (tbCoreRpcService.isPresent()) { tbCoreRpcService.get().processRpcResponseFromRuleEngine(response); diff --git a/application/src/main/java/org/thingsboard/server/service/rpc/TbRuleEngineDeviceRpcService.java b/application/src/main/java/org/thingsboard/server/service/rpc/TbRuleEngineDeviceRpcService.java index 3d4f23a796..4739338c6c 100644 --- a/application/src/main/java/org/thingsboard/server/service/rpc/TbRuleEngineDeviceRpcService.java +++ b/application/src/main/java/org/thingsboard/server/service/rpc/TbRuleEngineDeviceRpcService.java @@ -29,4 +29,13 @@ public interface TbRuleEngineDeviceRpcService extends RuleEngineRpcService { */ void processRpcResponseFromDevice(FromDeviceRpcResponse response); + + /** + * Sends Rpc response from the Device to TB Core. + * + * @param originServiceId Service ID of the origin component + * @param response the RPC response + */ + void sendRpcResponseToTbCore(String originServiceId, FromDeviceRpcResponse response); + } diff --git a/application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java b/application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java index 549f44bd99..91f741c308 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java +++ b/application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java @@ -112,7 +112,7 @@ public class AccessValidator { @Autowired protected EntityViewService entityViewService; - @Autowired + @Autowired(required = false) protected EdgeService edgeService; @Autowired diff --git a/application/src/main/java/org/thingsboard/server/service/ttl/edge/EdgeEventsCleanUpService.java b/application/src/main/java/org/thingsboard/server/service/ttl/edge/EdgeEventsCleanUpService.java new file mode 100644 index 0000000000..3a84701a36 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/ttl/edge/EdgeEventsCleanUpService.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.ttl.edge; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; +import org.thingsboard.server.dao.util.PsqlDao; +import org.thingsboard.server.service.ttl.AbstractCleanUpService; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +@PsqlDao +@Slf4j +@Service +public class EdgeEventsCleanUpService extends AbstractCleanUpService { + + @Value("${sql.ttl.edge_events.edge_events_ttl}") + private long ttl; + + @Value("${sql.ttl.edge_events.enabled}") + private boolean ttlTaskExecutionEnabled; + + @Scheduled(initialDelayString = "${sql.ttl.edge_events.execution_interval_ms}", fixedDelayString = "${sql.ttl.edge_events.execution_interval_ms}") + public void cleanUp() { + if (ttlTaskExecutionEnabled) { + try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { + doCleanUp(conn); + } catch (SQLException e) { + log.error("SQLException occurred during TTL task execution ", e); + } + } + } + + @Override + protected void doCleanUp(Connection connection) throws SQLException { + long totalEdgeEventsRemoved = executeQuery(connection, "call cleanup_edge_events_by_ttl(" + ttl + ", 0);"); + log.info("Total edge events removed by TTL: [{}]", totalEdgeEventsRemoved); + } +} \ No newline at end of file diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index e0959c3a4c..fbbff1573a 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -308,6 +308,10 @@ sql: execution_interval_ms: "${SQL_TTL_EVENTS_EXECUTION_INTERVAL:86400000}" # Number of milliseconds. The current value corresponds to one day events_ttl: "${SQL_TTL_EVENTS_EVENTS_TTL:0}" # Number of seconds debug_events_ttl: "${SQL_TTL_EVENTS_DEBUG_EVENTS_TTL:604800}" # Number of seconds. The current value corresponds to one week + edge_events: + enabled: "${SQL_TTL_EDGE_EVENTS_ENABLED:true}" + execution_interval_ms: "${SQL_TTL_EDGE_EVENTS_EXECUTION_INTERVAL:86400000}" # Number of milliseconds. The current value corresponds to one day + edge_events_ttl: "${SQL_TTL_EDGE_EVENTS_TTL:2628000}" # Number of seconds. The current value corresponds to one month # Actor system parameters actors: @@ -625,8 +629,9 @@ transport: # Edges parameters edges: rpc: - enabled: "${EDGES_RPC_ENABLED:true}" - port: "${EDGES_RPC_PORT:60100}" + enabled: "${EDGES_RPC_ENABLED:false}" + port: "${EDGES_RPC_PORT:7070}" + client_max_keep_alive_time_sec: "${EDGES_RPC_CLIENT_MAX_KEEP_ALIVE_TIME_SEC:300}" ssl: # Enable/disable SSL support enabled: "${EDGES_RPC_SSL_ENABLED:false}" @@ -636,6 +641,9 @@ edges: max_read_records_count: "${EDGES_RPC_STORAGE_MAX_READ_RECORDS_COUNT:50}" no_read_records_sleep: "${EDGES_RPC_NO_READ_RECORDS_SLEEP:1000}" sleep_between_batches: "${EDGES_RPC_SLEEP_BETWEEN_BATCHES:1000}" + edge_events_ttl: "${EDGES_EDGE_EVENTS_TTL:0}" + state: + persistToTelemetry: "${EDGES_PERSIST_STATE_TO_TELEMETRY:false}" swagger: api_path_regex: "${SWAGGER_API_PATH_REGEX:/api.*}" diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java index 8d3391bb65..65ec10db91 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java @@ -24,6 +24,7 @@ import io.jsonwebtoken.Header; import io.jsonwebtoken.Jwt; import io.jsonwebtoken.Jwts; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.StringUtils; import org.hamcrest.Matcher; import org.junit.After; @@ -68,6 +69,7 @@ import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.device.profile.DeviceProfileData; +import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; @@ -543,4 +545,18 @@ public abstract class AbstractWebTest { return jsonPath("$.message", matcher); } + protected Edge constructEdge(String name, String type) { + return constructEdge(tenantId, name, type); + } + protected 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)); + edge.setEdgeLicenseKey(RandomStringUtils.randomAlphanumeric(20)); + edge.setCloudEndpoint("http://localhost:8080"); + return edge; + } } diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java index b0f193aec3..46dd3b019a 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java @@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.EntitySubtype; 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.CustomerId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; @@ -672,4 +673,30 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { Assert.assertEquals(0, pageData.getData().size()); } + @Test + public void testAssignAssetToEdge() throws Exception { + Edge edge = constructEdge("My edge", "default"); + Edge savedEdge = doPost("/api/edge", edge, Edge.class); + + Asset asset = new Asset(); + asset.setName("My asset"); + asset.setType("default"); + Asset savedAsset = doPost("/api/asset", asset, Asset.class); + + doPost("/api/edge/" + savedEdge.getId().getId().toString() + + "/asset/" + savedAsset.getId().getId().toString(), Asset.class); + + PageData pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId().toString() + "/assets?", + new TypeReference>() {}, new PageLink(100)); + + Assert.assertEquals(1, pageData.getData().size()); + + doDelete("/api/edge/" + savedEdge.getId().getId().toString() + + "/asset/" + savedAsset.getId().getId().toString(), Asset.class); + + pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId().toString() + "/assets?", + new TypeReference>() {}, new PageLink(100)); + + Assert.assertEquals(0, pageData.getData().size()); + } } diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java index 57b23d88c8..32ef9c4018 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java @@ -15,27 +15,30 @@ */ package org.thingsboard.server.controller; -import static org.hamcrest.Matchers.containsString; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - import com.datastax.oss.driver.api.core.uuid.Uuids; +import com.fasterxml.jackson.core.type.TypeReference; import org.apache.commons.lang3.RandomStringUtils; -import org.thingsboard.server.common.data.*; -import org.thingsboard.server.common.data.id.CustomerId; -import org.thingsboard.server.common.data.page.PageData; -import org.thingsboard.server.common.data.page.PageLink; -import org.thingsboard.server.common.data.page.TimePageLink; -import org.thingsboard.server.common.data.security.Authority; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +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.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.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.security.Authority; -import com.fasterxml.jackson.core.type.TypeReference; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static org.hamcrest.Matchers.containsString; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; public abstract class BaseDashboardControllerTest extends AbstractControllerTest { @@ -348,4 +351,30 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest Assert.assertEquals(dashboards, loadedDashboards); } + @Test + public void testAssignDashboardToEdge() throws Exception { + Edge edge = constructEdge("My edge", "default"); + Edge savedEdge = doPost("/api/edge", edge, Edge.class); + + Dashboard dashboard = new Dashboard(); + dashboard.setTitle("My dashboard"); + Dashboard savedDashboard = doPost("/api/dashboard", dashboard, Dashboard.class); + + doPost("/api/edge/" + savedEdge.getId().getId().toString() + + "/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); + + PageData pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId().toString() + "/dashboards?", + new TypeReference>() {}, new PageLink(100)); + + Assert.assertEquals(1, pageData.getData().size()); + + doDelete("/api/edge/" + savedEdge.getId().getId().toString() + + "/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); + + pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId().toString() + "/dashboards?", + new TypeReference>() {}, new PageLink(100)); + + Assert.assertEquals(0, pageData.getData().size()); + } + } diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java index 72a885b062..1a2eea1e92 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java @@ -24,10 +24,10 @@ import org.junit.Before; import org.junit.Test; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Device; -import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.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.DeviceCredentialsId; import org.thingsboard.server.common.data.id.DeviceId; @@ -463,7 +463,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { .andExpect(status().isOk()); } pageLink = new PageLink(4, 0, title1); - pageData = doGetTypedWithPageLink("/api/tenant/devices?", + pageData = doGetTypedWithPageLink("/api/tenant/devices?", new TypeReference>(){}, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); @@ -473,7 +473,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { .andExpect(status().isOk()); } pageLink = new PageLink(4, 0, title2); - pageData = doGetTypedWithPageLink("/api/tenant/devices?", + pageData = doGetTypedWithPageLink("/api/tenant/devices?", new TypeReference>(){}, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); @@ -669,7 +669,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { .andExpect(status().isOk()); } pageLink = new PageLink(4, 0, title1); - pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?", + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?", new TypeReference>(){}, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); @@ -679,7 +679,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { .andExpect(status().isOk()); } pageLink = new PageLink(4, 0, title2); - pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?", + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?", new TypeReference>(){}, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); @@ -827,4 +827,31 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { doDelete("/api/tenant/" + savedDifferentTenant.getId().getId().toString()) .andExpect(status().isOk()); } + + @Test + public void testAssignDeviceToEdge() throws Exception { + Edge edge = constructEdge("My edge", "default"); + Edge savedEdge = doPost("/api/edge", edge, Edge.class); + + Device device = new Device(); + device.setName("My device"); + device.setType("default"); + Device savedDevice = doPost("/api/device", device, Device.class); + + doPost("/api/edge/" + savedEdge.getId().getId().toString() + + "/device/" + savedDevice.getId().getId().toString(), Device.class); + + PageData pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId().toString() + "/devices?", + new TypeReference>() {}, new PageLink(100)); + + Assert.assertEquals(1, pageData.getData().size()); + + doDelete("/api/edge/" + savedEdge.getId().getId().toString() + + "/device/" + savedDevice.getId().getId().toString(), Device.class); + + pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId().toString() + "/devices?", + new TypeReference>() {}, new PageLink(100)); + + Assert.assertEquals(0, pageData.getData().size()); + } } diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseEdgeControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseEdgeControllerTest.java index b9537775ed..12b4a354dd 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseEdgeControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseEdgeControllerTest.java @@ -18,14 +18,17 @@ package org.thingsboard.server.controller; import com.datastax.oss.driver.api.core.uuid.Uuids; import com.fasterxml.jackson.core.type.TypeReference; import org.apache.commons.lang3.RandomStringUtils; +import org.apache.commons.lang3.StringUtils; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; 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.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.CustomerId; import org.thingsboard.server.common.data.id.TenantId; @@ -33,6 +36,12 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.dao.model.ModelConstants; +import org.thingsboard.server.edge.imitator.EdgeImitator; +import org.thingsboard.server.gen.edge.AssetUpdateMsg; +import org.thingsboard.server.gen.edge.DeviceUpdateMsg; +import org.thingsboard.server.gen.edge.RuleChainUpdateMsg; +import org.thingsboard.server.gen.edge.UserCredentialsUpdateMsg; +import org.thingsboard.server.gen.edge.UserUpdateMsg; import java.util.ArrayList; import java.util.Collections; @@ -90,6 +99,8 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest { Assert.assertNotNull(savedEdge.getCustomerId()); Assert.assertEquals(NULL_UUID, savedEdge.getCustomerId().getId()); Assert.assertEquals(edge.getName(), savedEdge.getName()); + Assert.assertTrue(StringUtils.isNoneBlank(savedEdge.getEdgeLicenseKey())); + Assert.assertTrue(StringUtils.isNoneBlank(savedEdge.getCloudEndpoint())); savedEdge.setName("My new edge"); doPost("/api/edge", savedEdge, Edge.class); @@ -638,17 +649,57 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest { Assert.assertEquals(0, pageData.getData().size()); } - private Edge constructEdge(String name, String type) { - return constructEdge(tenantId, name, type); + @Test + public void testSyncEdge() throws Exception { + Edge edge = doPost("/api/edge", constructEdge("Test Edge", "test"), Edge.class); + + Device device = new Device(); + device.setName("Edge Device 1"); + device.setType("test"); + Device savedDevice = doPost("/api/device", device, Device.class); + doPost("/api/edge/" + edge.getId().getId().toString() + + "/device/" + savedDevice.getId().getId().toString(), Device.class); + + Asset asset = new Asset(); + asset.setName("Edge Asset 1"); + asset.setType("test"); + Asset savedAsset = doPost("/api/asset", asset, Asset.class); + doPost("/api/edge/" + edge.getId().getId().toString() + + "/asset/" + savedAsset.getId().getId().toString(), Asset.class); + + EdgeImitator edgeImitator = new EdgeImitator("localhost", 7070, edge.getRoutingKey(), edge.getSecret()); + edgeImitator.ignoreType(UserCredentialsUpdateMsg.class); + edgeImitator.expectMessageAmount(7); + edgeImitator.connect(); + edgeImitator.waitForMessages(); + + Assert.assertEquals(7, edgeImitator.getDownlinkMsgs().size()); + Assert.assertTrue(edgeImitator.findMessageByType(RuleChainUpdateMsg.class).isPresent()); + Assert.assertTrue(edgeImitator.findMessageByType(DeviceUpdateMsg.class).isPresent()); + Assert.assertTrue(edgeImitator.findMessageByType(AssetUpdateMsg.class).isPresent()); + Assert.assertTrue(edgeImitator.findMessageByType(UserUpdateMsg.class).isPresent()); + + edgeImitator.getDownlinkMsgs().clear(); + + edgeImitator.expectMessageAmount(4); + doPost("/api/edge/sync", edge.getId()); + edgeImitator.waitForMessages(); + + Assert.assertEquals(4, edgeImitator.getDownlinkMsgs().size()); + Assert.assertTrue(edgeImitator.findMessageByType(RuleChainUpdateMsg.class).isPresent()); + Assert.assertTrue(edgeImitator.findMessageByType(DeviceUpdateMsg.class).isPresent()); + Assert.assertTrue(edgeImitator.findMessageByType(AssetUpdateMsg.class).isPresent()); + Assert.assertTrue(edgeImitator.findMessageByType(UserUpdateMsg.class).isPresent()); + + edgeImitator.allowIgnoredTypes(); + edgeImitator.disconnect(); + + doDelete("/api/device/" + savedDevice.getId().getId().toString()) + .andExpect(status().isOk()); + doDelete("/api/asset/" + savedAsset.getId().getId().toString()) + .andExpect(status().isOk()); + doDelete("/api/edge/" + edge.getId().getId().toString()) + .andExpect(status().isOk()); } - 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; - } } \ No newline at end of file diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseEdgeEventControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseEdgeEventControllerTest.java new file mode 100644 index 0000000000..e8daa2ad80 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/controller/BaseEdgeEventControllerTest.java @@ -0,0 +1,125 @@ +/** + * 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.controller; + +import com.fasterxml.jackson.core.type.TypeReference; +import lombok.extern.slf4j.Slf4j; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.asset.Asset; +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.TenantId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.TimePageLink; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.security.Authority; + +import java.util.List; + +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@Slf4j +public class BaseEdgeEventControllerTest extends AbstractControllerTest { + + private Tenant savedTenant; + private TenantId tenantId; + private User tenantAdmin; + + @Before + public void beforeTest() throws Exception { + loginSysAdmin(); + + Tenant tenant = new Tenant(); + tenant.setTitle("My tenant"); + savedTenant = doPost("/api/tenant", tenant, Tenant.class); + tenantId = savedTenant.getId(); + Assert.assertNotNull(savedTenant); + + tenantAdmin = new User(); + tenantAdmin.setAuthority(Authority.TENANT_ADMIN); + tenantAdmin.setTenantId(savedTenant.getId()); + tenantAdmin.setEmail("tenant2@thingsboard.org"); + tenantAdmin.setFirstName("Joe"); + tenantAdmin.setLastName("Downs"); + + tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1"); + } + + @After + public void afterTest() throws Exception { + loginSysAdmin(); + + doDelete("/api/tenant/" + savedTenant.getId().getId().toString()) + .andExpect(status().isOk()); + } + + @Test + public void testGetEdgeEvents() throws Exception { + Thread.sleep(1000); + Edge edge = constructEdge("TestEdge", "default"); + edge = doPost("/api/edge", edge, Edge.class); + + Device device = constructDevice("TestDevice", "default"); + Device savedDevice = doPost("/api/device", device, Device.class); + + doPost("/api/edge/" + edge.getId().toString() + "/device/" + savedDevice.getId().toString(), Device.class); + Thread.sleep(1000); + + Asset asset = constructAsset("TestAsset", "default"); + Asset savedAsset = doPost("/api/asset", asset, Asset.class); + + doPost("/api/edge/" + edge.getId().toString() + "/asset/" + savedAsset.getId().toString(), Asset.class); + Thread.sleep(1000); + + EntityRelation relation = new EntityRelation(savedAsset.getId(), savedDevice.getId(), EntityRelation.CONTAINS_TYPE); + + doPost("/api/relation", relation); + Thread.sleep(1000); + + List edgeEvents = doGetTypedWithTimePageLink("/api/edge/" + edge.getId().toString() + "/events?", + new TypeReference>() { + }, new TimePageLink(4)).getData(); + + Assert.assertFalse(edgeEvents.isEmpty()); + Assert.assertEquals(4, edgeEvents.size()); + Assert.assertEquals(EdgeEventType.RELATION, edgeEvents.get(0).getType()); + Assert.assertEquals(EdgeEventType.ASSET, edgeEvents.get(1).getType()); + Assert.assertEquals(EdgeEventType.DEVICE, edgeEvents.get(2).getType()); + Assert.assertEquals(EdgeEventType.RULE_CHAIN, edgeEvents.get(3).getType()); + } + + private Device constructDevice(String name, String type) { + Device device = new Device(); + device.setName(name); + device.setType(type); + return device; + } + + private Asset constructAsset(String name, String type) { + Asset asset = new Asset(); + asset.setName(name); + asset.setType(type); + return asset; + } + +} diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java index 5aeb12699b..f78a74c519 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java @@ -27,7 +27,13 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import org.thingsboard.server.common.data.*; +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.EntityView; +import org.thingsboard.server.common.data.EntityViewInfo; +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.objects.AttributesEntityView; import org.thingsboard.server.common.data.objects.TelemetryEntityView; @@ -49,7 +55,6 @@ import java.util.concurrent.TimeUnit; import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; @@ -568,4 +573,31 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes return loadedItems; } + + @Test + public void testAssignEntityViewToEdge() throws Exception { + Edge edge = constructEdge("My edge", "default"); + Edge savedEdge = doPost("/api/edge", edge, Edge.class); + + EntityView savedEntityView = getNewSavedEntityView("My entityView"); + + doPost("/api/edge/" + savedEdge.getId().getId().toString() + + "/device/" + testDevice.getId().getId().toString(), Device.class); + + doPost("/api/edge/" + savedEdge.getId().getId().toString() + + "/entityView/" + savedEntityView.getId().getId().toString(), EntityView.class); + + PageData pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId().toString() + "/entityViews?", + new TypeReference>() {}, new PageLink(100)); + + Assert.assertEquals(1, pageData.getData().size()); + + doDelete("/api/edge/" + savedEdge.getId().getId().toString() + + "/entityView/" + savedEntityView.getId().getId().toString(), EntityView.class); + + pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId().toString() + "/entityViews?", + new TypeReference>() {}, new PageLink(100)); + + Assert.assertEquals(0, pageData.getData().size()); + } } diff --git a/application/src/test/java/org/thingsboard/server/controller/nosql/EdgeEventControllerNoSqlTest.java b/application/src/test/java/org/thingsboard/server/controller/nosql/EdgeEventControllerNoSqlTest.java new file mode 100644 index 0000000000..8cb88c83f6 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/controller/nosql/EdgeEventControllerNoSqlTest.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.controller.nosql; + +import org.thingsboard.server.controller.BaseEdgeEventControllerTest; +import org.thingsboard.server.dao.service.DaoNoSqlTest; + +@DaoNoSqlTest +public class EdgeEventControllerNoSqlTest extends BaseEdgeEventControllerTest { +} diff --git a/application/src/test/java/org/thingsboard/server/controller/sql/EdgeEventControllerSqlTest.java b/application/src/test/java/org/thingsboard/server/controller/sql/EdgeEventControllerSqlTest.java new file mode 100644 index 0000000000..84b37970ed --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/controller/sql/EdgeEventControllerSqlTest.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.controller.sql; + +import org.thingsboard.server.controller.BaseEdgeEventControllerTest; +import org.thingsboard.server.dao.service.DaoSqlTest; + +@DaoSqlTest +public class EdgeEventControllerSqlTest extends BaseEdgeEventControllerTest { +} diff --git a/application/src/test/java/org/thingsboard/server/edge/BaseEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/BaseEdgeTest.java new file mode 100644 index 0000000000..35aaecfdf6 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/edge/BaseEdgeTest.java @@ -0,0 +1,1090 @@ +/** + * 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.edge; + +import com.datastax.oss.driver.api.core.uuid.Uuids; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.gson.JsonObject; +import com.google.protobuf.AbstractMessage; +import lombok.extern.slf4j.Slf4j; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +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.Tenant; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.common.data.alarm.AlarmInfo; +import org.thingsboard.server.common.data.alarm.AlarmSeverity; +import org.thingsboard.server.common.data.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.EdgeEvent; +import org.thingsboard.server.common.data.edge.EdgeEventType; +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.id.UserId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +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.RuleChainType; +import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.common.data.security.DeviceCredentials; +import org.thingsboard.server.common.data.widget.WidgetType; +import org.thingsboard.server.common.data.widget.WidgetsBundle; +import org.thingsboard.server.common.transport.adaptor.JsonConverter; +import org.thingsboard.server.controller.AbstractControllerTest; +import org.thingsboard.server.dao.edge.EdgeEventService; +import org.thingsboard.server.dao.util.mapping.JacksonUtil; +import org.thingsboard.server.edge.imitator.EdgeImitator; +import org.thingsboard.server.gen.edge.AlarmUpdateMsg; +import org.thingsboard.server.gen.edge.AssetUpdateMsg; +import org.thingsboard.server.gen.edge.CustomerUpdateMsg; +import org.thingsboard.server.gen.edge.DashboardUpdateMsg; +import org.thingsboard.server.gen.edge.DeviceCredentialsRequestMsg; +import org.thingsboard.server.gen.edge.DeviceCredentialsUpdateMsg; +import org.thingsboard.server.gen.edge.DeviceUpdateMsg; +import org.thingsboard.server.gen.edge.EdgeConfiguration; +import org.thingsboard.server.gen.edge.EntityDataProto; +import org.thingsboard.server.gen.edge.EntityViewUpdateMsg; +import org.thingsboard.server.gen.edge.RelationUpdateMsg; +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.UplinkMsg; +import org.thingsboard.server.gen.edge.UserCredentialsRequestMsg; +import org.thingsboard.server.gen.edge.UserCredentialsUpdateMsg; +import org.thingsboard.server.gen.edge.WidgetTypeUpdateMsg; +import org.thingsboard.server.gen.edge.WidgetsBundleUpdateMsg; +import org.thingsboard.server.gen.transport.TransportProtos; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +; + + +@Slf4j +abstract public class BaseEdgeTest extends AbstractControllerTest { + + private Tenant savedTenant; + private TenantId tenantId; + private User tenantAdmin; + + private EdgeImitator edgeImitator; + private Edge edge; + + @Autowired + private EdgeEventService edgeEventService; + + @Before + public void beforeTest() throws Exception { + loginSysAdmin(); + + Tenant tenant = new Tenant(); + tenant.setTitle("My tenant"); + savedTenant = doPost("/api/tenant", tenant, Tenant.class); + tenantId = savedTenant.getId(); + Assert.assertNotNull(savedTenant); + + tenantAdmin = new User(); + tenantAdmin.setAuthority(Authority.TENANT_ADMIN); + tenantAdmin.setTenantId(savedTenant.getId()); + tenantAdmin.setEmail("tenant2@thingsboard.org"); + tenantAdmin.setFirstName("Joe"); + tenantAdmin.setLastName("Downs"); + + tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1"); + installation(); + + edgeImitator = new EdgeImitator("localhost", 7070, edge.getRoutingKey(), edge.getSecret()); + // should be less, but events from SyncEdgeService stack with events from controller. will be fixed in next releases + edgeImitator.expectMessageAmount(7); + edgeImitator.connect(); + } + + @After + public void afterTest() throws Exception { + edgeImitator.disconnect(); + uninstallation(); + + loginSysAdmin(); + + doDelete("/api/tenant/" + savedTenant.getId().getId().toString()) + .andExpect(status().isOk()); + } + + + @Test + public void test() throws Exception { + testReceivedInitialData(); + testDevices(); + testAssets(); + testRuleChains(); + testDashboards(); + testRelations(); + testAlarms(); + testEntityView(); + testCustomer(); + testWidgetsBundleAndWidgetType(); + testTimeseries(); + testAttributes(); + testSendMessagesToCloud(); + } + + private void testReceivedInitialData() throws Exception { + log.info("Checking received data"); + edgeImitator.waitForMessages(); + + EdgeConfiguration configuration = edgeImitator.getConfiguration(); + Assert.assertNotNull(configuration); + + UserId userId = edgeImitator.getUserId(); + Assert.assertNotNull(userId); + + Optional optionalMsg1 = edgeImitator.findMessageByType(DeviceUpdateMsg.class); + Assert.assertTrue(optionalMsg1.isPresent()); + DeviceUpdateMsg deviceUpdateMsg = optionalMsg1.get(); + Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, deviceUpdateMsg.getMsgType()); + UUID deviceUUID = new UUID(deviceUpdateMsg.getIdMSB(), deviceUpdateMsg.getIdLSB()); + Device device = doGet("/api/device/" + deviceUUID.toString(), Device.class); + Assert.assertNotNull(device); + List edgeDevices = doGetTypedWithPageLink("/api/edge/" + edge.getId().getId().toString() + "/devices?", + new TypeReference>() {}, new PageLink(100)).getData(); + Assert.assertTrue(edgeDevices.contains(device)); + + Optional optionalMsg2 = edgeImitator.findMessageByType(AssetUpdateMsg.class); + Assert.assertTrue(optionalMsg2.isPresent()); + AssetUpdateMsg assetUpdateMsg = optionalMsg2.get(); + Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, assetUpdateMsg.getMsgType()); + UUID assetUUID = new UUID(assetUpdateMsg.getIdMSB(), assetUpdateMsg.getIdLSB()); + Asset asset = doGet("/api/asset/" + assetUUID.toString(), Asset.class); + Assert.assertNotNull(asset); + List edgeAssets = doGetTypedWithPageLink("/api/edge/" + edge.getId().getId().toString() + "/assets?", + new TypeReference>() {}, new PageLink(100)).getData(); + Assert.assertTrue(edgeAssets.contains(asset)); + + Optional optionalMsg3 = edgeImitator.findMessageByType(RuleChainUpdateMsg.class); + Assert.assertTrue(optionalMsg3.isPresent()); + RuleChainUpdateMsg ruleChainUpdateMsg = optionalMsg3.get(); + Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, ruleChainUpdateMsg.getMsgType()); + UUID ruleChainUUID = new UUID(ruleChainUpdateMsg.getIdMSB(), ruleChainUpdateMsg.getIdLSB()); + RuleChain ruleChain = doGet("/api/ruleChain/" + ruleChainUUID.toString(), RuleChain.class); + Assert.assertNotNull(ruleChain); + List edgeRuleChains = doGetTypedWithPageLink("/api/edge/" + edge.getId().getId().toString() + "/ruleChains?", + new TypeReference>() {}, new PageLink(100)).getData(); + Assert.assertTrue(edgeRuleChains.contains(ruleChain)); + + log.info("Received data checked"); + } + + private void testDevices() throws Exception { + log.info("Testing devices"); + + Device device = new Device(); + device.setName("Edge Device 2"); + device.setType("test"); + Device savedDevice = doPost("/api/device", device, Device.class); + + edgeImitator.expectMessageAmount(1); + doPost("/api/edge/" + edge.getId().getId().toString() + + "/device/" + savedDevice.getId().getId().toString(), Device.class); + edgeImitator.waitForMessages(); + + AbstractMessage latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof DeviceUpdateMsg); + DeviceUpdateMsg deviceUpdateMsg = (DeviceUpdateMsg) latestMessage; + Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, deviceUpdateMsg.getMsgType()); + Assert.assertEquals(deviceUpdateMsg.getIdMSB(), savedDevice.getUuidId().getMostSignificantBits()); + Assert.assertEquals(deviceUpdateMsg.getIdLSB(), savedDevice.getUuidId().getLeastSignificantBits()); + Assert.assertEquals(deviceUpdateMsg.getName(), savedDevice.getName()); + Assert.assertEquals(deviceUpdateMsg.getType(), savedDevice.getType()); + + edgeImitator.expectMessageAmount(1); + doDelete("/api/edge/" + edge.getId().getId().toString() + + "/device/" + savedDevice.getId().getId().toString(), Device.class); + edgeImitator.waitForMessages(); + + latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof DeviceUpdateMsg); + deviceUpdateMsg = (DeviceUpdateMsg) latestMessage; + Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, deviceUpdateMsg.getMsgType()); + Assert.assertEquals(deviceUpdateMsg.getIdMSB(), savedDevice.getUuidId().getMostSignificantBits()); + Assert.assertEquals(deviceUpdateMsg.getIdLSB(), savedDevice.getUuidId().getLeastSignificantBits()); + + edgeImitator.expectMessageAmount(1); + doDelete("/api/device/" + savedDevice.getId().getId().toString()) + .andExpect(status().isOk()); + edgeImitator.waitForMessages(); + + latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof DeviceUpdateMsg); + deviceUpdateMsg = (DeviceUpdateMsg) latestMessage; + Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, deviceUpdateMsg.getMsgType()); + Assert.assertEquals(deviceUpdateMsg.getIdMSB(), savedDevice.getUuidId().getMostSignificantBits()); + Assert.assertEquals(deviceUpdateMsg.getIdLSB(), savedDevice.getUuidId().getLeastSignificantBits()); + + log.info("Devices tested successfully"); + } + + + private void testAssets() throws Exception { + log.info("Testing assets"); + Asset asset = new Asset(); + asset.setName("Edge Asset 2"); + asset.setType("test"); + Asset savedAsset = doPost("/api/asset", asset, Asset.class); + + edgeImitator.expectMessageAmount(1); + doPost("/api/edge/" + edge.getId().getId().toString() + + "/asset/" + savedAsset.getId().getId().toString(), Asset.class); + edgeImitator.waitForMessages(); + + AbstractMessage latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof AssetUpdateMsg); + AssetUpdateMsg assetUpdateMsg = (AssetUpdateMsg) latestMessage; + Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, assetUpdateMsg.getMsgType()); + Assert.assertEquals(assetUpdateMsg.getIdMSB(), savedAsset.getUuidId().getMostSignificantBits()); + Assert.assertEquals(assetUpdateMsg.getIdLSB(), savedAsset.getUuidId().getLeastSignificantBits()); + Assert.assertEquals(assetUpdateMsg.getName(), savedAsset.getName()); + Assert.assertEquals(assetUpdateMsg.getType(), savedAsset.getType()); + + edgeImitator.expectMessageAmount(1); + doDelete("/api/edge/" + edge.getId().getId().toString() + + "/asset/" + savedAsset.getId().getId().toString(), Asset.class); + edgeImitator.waitForMessages(); + + latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof AssetUpdateMsg); + assetUpdateMsg = (AssetUpdateMsg) latestMessage; + Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, assetUpdateMsg.getMsgType()); + Assert.assertEquals(assetUpdateMsg.getIdMSB(), savedAsset.getUuidId().getMostSignificantBits()); + Assert.assertEquals(assetUpdateMsg.getIdLSB(), savedAsset.getUuidId().getLeastSignificantBits()); + + edgeImitator.expectMessageAmount(1); + doDelete("/api/asset/" + savedAsset.getId().getId().toString()) + .andExpect(status().isOk()); + edgeImitator.waitForMessages(); + + latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof AssetUpdateMsg); + assetUpdateMsg = (AssetUpdateMsg) latestMessage; + Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, assetUpdateMsg.getMsgType()); + Assert.assertEquals(assetUpdateMsg.getIdMSB(), savedAsset.getUuidId().getMostSignificantBits()); + Assert.assertEquals(assetUpdateMsg.getIdLSB(), savedAsset.getUuidId().getLeastSignificantBits()); + + log.info("Assets tested successfully"); + } + + private void testRuleChains() throws Exception { + log.info("Testing RuleChains"); + RuleChain ruleChain = new RuleChain(); + ruleChain.setName("Edge Test Rule Chain"); + ruleChain.setType(RuleChainType.EDGE); + RuleChain savedRuleChain = doPost("/api/ruleChain", ruleChain, RuleChain.class); + + edgeImitator.expectMessageAmount(1); + doPost("/api/edge/" + edge.getId().getId().toString() + + "/ruleChain/" + savedRuleChain.getId().getId().toString(), RuleChain.class); + edgeImitator.waitForMessages(); + + AbstractMessage latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof RuleChainUpdateMsg); + RuleChainUpdateMsg ruleChainUpdateMsg = (RuleChainUpdateMsg) latestMessage; + Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, ruleChainUpdateMsg.getMsgType()); + Assert.assertEquals(ruleChainUpdateMsg.getIdMSB(), savedRuleChain.getUuidId().getMostSignificantBits()); + Assert.assertEquals(ruleChainUpdateMsg.getIdLSB(), savedRuleChain.getUuidId().getLeastSignificantBits()); + Assert.assertEquals(ruleChainUpdateMsg.getName(), savedRuleChain.getName()); + + edgeImitator.expectMessageAmount(1); + doDelete("/api/edge/" + edge.getId().getId().toString() + + "/ruleChain/" + savedRuleChain.getId().getId().toString(), RuleChain.class); + edgeImitator.waitForMessages(); + + latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof RuleChainUpdateMsg); + ruleChainUpdateMsg = (RuleChainUpdateMsg) latestMessage; + Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, ruleChainUpdateMsg.getMsgType()); + Assert.assertEquals(ruleChainUpdateMsg.getIdMSB(), savedRuleChain.getUuidId().getMostSignificantBits()); + Assert.assertEquals(ruleChainUpdateMsg.getIdLSB(), savedRuleChain.getUuidId().getLeastSignificantBits()); + + edgeImitator.expectMessageAmount(1); + doDelete("/api/ruleChain/" + savedRuleChain.getId().getId().toString()) + .andExpect(status().isOk()); + edgeImitator.waitForMessages(); + + latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof RuleChainUpdateMsg); + ruleChainUpdateMsg = (RuleChainUpdateMsg) latestMessage; + Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, ruleChainUpdateMsg.getMsgType()); + Assert.assertEquals(ruleChainUpdateMsg.getIdMSB(), savedRuleChain.getUuidId().getMostSignificantBits()); + Assert.assertEquals(ruleChainUpdateMsg.getIdLSB(), savedRuleChain.getUuidId().getLeastSignificantBits()); + + log.info("RuleChains tested successfully"); + } + + private void testDashboards() throws Exception { + log.info("Testing Dashboards"); + Dashboard dashboard = new Dashboard(); + dashboard.setTitle("Edge Test Dashboard"); + Dashboard savedDashboard = doPost("/api/dashboard", dashboard, Dashboard.class); + + edgeImitator.expectMessageAmount(1); + doPost("/api/edge/" + edge.getId().getId().toString() + + "/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); + edgeImitator.waitForMessages(); + + AbstractMessage latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof DashboardUpdateMsg); + DashboardUpdateMsg dashboardUpdateMsg = (DashboardUpdateMsg) latestMessage; + Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, dashboardUpdateMsg.getMsgType()); + Assert.assertEquals(dashboardUpdateMsg.getIdMSB(), savedDashboard.getUuidId().getMostSignificantBits()); + Assert.assertEquals(dashboardUpdateMsg.getIdLSB(), savedDashboard.getUuidId().getLeastSignificantBits()); + Assert.assertEquals(dashboardUpdateMsg.getTitle(), savedDashboard.getName()); + + edgeImitator.expectMessageAmount(1); + savedDashboard.setTitle("Updated Edge Test Dashboard"); + doPost("/api/dashboard", savedDashboard, Dashboard.class); + edgeImitator.waitForMessages(); + + latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof DashboardUpdateMsg); + dashboardUpdateMsg = (DashboardUpdateMsg) latestMessage; + Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, dashboardUpdateMsg.getMsgType()); + Assert.assertEquals(dashboardUpdateMsg.getTitle(), savedDashboard.getName()); + + edgeImitator.expectMessageAmount(1); + doDelete("/api/edge/" + edge.getId().getId().toString() + + "/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); + edgeImitator.waitForMessages(); + + latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof DashboardUpdateMsg); + dashboardUpdateMsg = (DashboardUpdateMsg) latestMessage; + Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, dashboardUpdateMsg.getMsgType()); + Assert.assertEquals(dashboardUpdateMsg.getIdMSB(), savedDashboard.getUuidId().getMostSignificantBits()); + Assert.assertEquals(dashboardUpdateMsg.getIdLSB(), savedDashboard.getUuidId().getLeastSignificantBits()); + + edgeImitator.expectMessageAmount(1); + doDelete("/api/dashboard/" + savedDashboard.getId().getId().toString()) + .andExpect(status().isOk()); + edgeImitator.waitForMessages(); + + latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof DashboardUpdateMsg); + dashboardUpdateMsg = (DashboardUpdateMsg) latestMessage; + Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, dashboardUpdateMsg.getMsgType()); + Assert.assertEquals(dashboardUpdateMsg.getIdMSB(), savedDashboard.getUuidId().getMostSignificantBits()); + Assert.assertEquals(dashboardUpdateMsg.getIdLSB(), savedDashboard.getUuidId().getLeastSignificantBits()); + + log.info("Dashboards tested successfully"); + } + + private void testRelations() throws Exception { + log.info("Testing Relations"); + List edgeDevices = doGetTypedWithPageLink("/api/edge/" + edge.getId().getId().toString() + "/devices?", + new TypeReference>() {}, new PageLink(100)).getData(); + List edgeAssets = doGetTypedWithPageLink("/api/edge/" + edge.getId().getId().toString() + "/assets?", + new TypeReference>() {}, new PageLink(100)).getData(); + + Assert.assertEquals(1, edgeDevices.size()); + Assert.assertEquals(1, edgeAssets.size()); + Device device = edgeDevices.get(0); + Asset asset = edgeAssets.get(0); + Assert.assertEquals("Edge Device 1", device.getName()); + Assert.assertEquals("Edge Asset 1", asset.getName()); + + EntityRelation relation = new EntityRelation(); + relation.setType("test"); + relation.setFrom(device.getId()); + relation.setTo(asset.getId()); + relation.setTypeGroup(RelationTypeGroup.COMMON); + + edgeImitator.expectMessageAmount(1); + doPost("/api/relation", relation); + edgeImitator.waitForMessages(); + + AbstractMessage latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof RelationUpdateMsg); + RelationUpdateMsg relationUpdateMsg = (RelationUpdateMsg) latestMessage; + Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, relationUpdateMsg.getMsgType()); + Assert.assertEquals(relationUpdateMsg.getType(), relation.getType()); + Assert.assertEquals(relationUpdateMsg.getFromIdMSB(), relation.getFrom().getId().getMostSignificantBits()); + Assert.assertEquals(relationUpdateMsg.getFromIdLSB(), relation.getFrom().getId().getLeastSignificantBits()); + Assert.assertEquals(relationUpdateMsg.getToEntityType(), relation.getTo().getEntityType().name()); + Assert.assertEquals(relationUpdateMsg.getFromIdMSB(), relation.getFrom().getId().getMostSignificantBits()); + Assert.assertEquals(relationUpdateMsg.getToIdLSB(), relation.getTo().getId().getLeastSignificantBits()); + Assert.assertEquals(relationUpdateMsg.getToEntityType(), relation.getTo().getEntityType().name()); + Assert.assertEquals(relationUpdateMsg.getTypeGroup(), relation.getTypeGroup().name()); + + edgeImitator.expectMessageAmount(1); + doDelete("/api/relation?" + + "fromId=" + relation.getFrom().getId().toString() + + "&fromType=" + relation.getFrom().getEntityType().name() + + "&relationType=" + relation.getType() + + "&relationTypeGroup=" + relation.getTypeGroup().name() + + "&toId=" + relation.getTo().getId().toString() + + "&toType=" + relation.getTo().getEntityType().name()) + .andExpect(status().isOk()); + edgeImitator.waitForMessages(); + + latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof RelationUpdateMsg); + relationUpdateMsg = (RelationUpdateMsg) latestMessage; + Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, relationUpdateMsg.getMsgType()); + Assert.assertEquals(relationUpdateMsg.getType(), relation.getType()); + Assert.assertEquals(relationUpdateMsg.getFromIdMSB(), relation.getFrom().getId().getMostSignificantBits()); + Assert.assertEquals(relationUpdateMsg.getFromIdLSB(), relation.getFrom().getId().getLeastSignificantBits()); + Assert.assertEquals(relationUpdateMsg.getToEntityType(), relation.getTo().getEntityType().name());Assert.assertEquals(relationUpdateMsg.getFromIdMSB(), relation.getFrom().getId().getMostSignificantBits()); + Assert.assertEquals(relationUpdateMsg.getToIdLSB(), relation.getTo().getId().getLeastSignificantBits()); + Assert.assertEquals(relationUpdateMsg.getToEntityType(), relation.getTo().getEntityType().name()); + Assert.assertEquals(relationUpdateMsg.getTypeGroup(), relation.getTypeGroup().name()); + + log.info("Relations tested successfully"); + } + + private void testAlarms() throws Exception { + log.info("Testing Alarms"); + List edgeDevices = doGetTypedWithPageLink("/api/edge/" + edge.getId().getId().toString() + "/devices?", + new TypeReference>() {}, new PageLink(100)).getData(); + Assert.assertEquals(1, edgeDevices.size()); + Device device = edgeDevices.get(0); + Assert.assertEquals("Edge Device 1", device.getName()); + + Alarm alarm = new Alarm(); + alarm.setOriginator(device.getId()); + alarm.setStatus(AlarmStatus.ACTIVE_UNACK); + alarm.setType("alarm"); + alarm.setSeverity(AlarmSeverity.CRITICAL); + + edgeImitator.expectMessageAmount(1); + Alarm savedAlarm = doPost("/api/alarm", alarm, Alarm.class); + edgeImitator.waitForMessages(); + + AbstractMessage latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof AlarmUpdateMsg); + AlarmUpdateMsg alarmUpdateMsg = (AlarmUpdateMsg) latestMessage; + Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, alarmUpdateMsg.getMsgType()); + Assert.assertEquals(alarmUpdateMsg.getType(), savedAlarm.getType()); + Assert.assertEquals(alarmUpdateMsg.getName(), savedAlarm.getName()); + Assert.assertEquals(alarmUpdateMsg.getOriginatorName(), device.getName()); + Assert.assertEquals(alarmUpdateMsg.getStatus(), savedAlarm.getStatus().name()); + Assert.assertEquals(alarmUpdateMsg.getSeverity(), savedAlarm.getSeverity().name()); + + edgeImitator.expectMessageAmount(1); + doPost("/api/alarm/" + savedAlarm.getId().getId().toString() + "/ack"); + edgeImitator.waitForMessages(); + + latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof AlarmUpdateMsg); + alarmUpdateMsg = (AlarmUpdateMsg) latestMessage; + Assert.assertEquals(UpdateMsgType.ALARM_ACK_RPC_MESSAGE, alarmUpdateMsg.getMsgType()); + Assert.assertEquals(alarmUpdateMsg.getType(), savedAlarm.getType()); + Assert.assertEquals(alarmUpdateMsg.getName(), savedAlarm.getName()); + Assert.assertEquals(alarmUpdateMsg.getOriginatorName(), device.getName()); + Assert.assertEquals(alarmUpdateMsg.getStatus(), AlarmStatus.ACTIVE_ACK.name()); + + edgeImitator.expectMessageAmount(1); + doPost("/api/alarm/" + savedAlarm.getId().getId().toString() + "/clear"); + edgeImitator.waitForMessages(); + + latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof AlarmUpdateMsg); + alarmUpdateMsg = (AlarmUpdateMsg) latestMessage; + Assert.assertEquals(UpdateMsgType.ALARM_CLEAR_RPC_MESSAGE, alarmUpdateMsg.getMsgType()); + Assert.assertEquals(alarmUpdateMsg.getType(), savedAlarm.getType()); + Assert.assertEquals(alarmUpdateMsg.getName(), savedAlarm.getName()); + Assert.assertEquals(alarmUpdateMsg.getOriginatorName(), device.getName()); + Assert.assertEquals(alarmUpdateMsg.getStatus(), AlarmStatus.CLEARED_ACK.name()); + + doDelete("/api/alarm/" + savedAlarm.getId().getId().toString()) + .andExpect(status().isOk()); + log.info("Alarms tested successfully"); + } + + private void testEntityView() throws Exception { + log.info("Testing EntityView"); + List edgeDevices = doGetTypedWithPageLink("/api/edge/" + edge.getId().getId().toString() + "/devices?", + new TypeReference>() {}, new PageLink(100)).getData(); + Assert.assertEquals(1, edgeDevices.size()); + Device device = edgeDevices.get(0); + Assert.assertEquals("Edge Device 1", device.getName()); + + EntityView entityView = new EntityView(); + entityView.setName("Edge EntityView 1"); + entityView.setType("test"); + entityView.setEntityId(device.getId()); + EntityView savedEntityView = doPost("/api/entityView", entityView, EntityView.class); + + edgeImitator.expectMessageAmount(1); + doPost("/api/edge/" + edge.getId().getId().toString() + + "/entityView/" + savedEntityView.getId().getId().toString(), EntityView.class); + edgeImitator.waitForMessages(); + + AbstractMessage latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof EntityViewUpdateMsg); + EntityViewUpdateMsg entityViewUpdateMsg = (EntityViewUpdateMsg) latestMessage; + Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, entityViewUpdateMsg.getMsgType()); + Assert.assertEquals(entityViewUpdateMsg.getType(), savedEntityView.getType()); + Assert.assertEquals(entityViewUpdateMsg.getName(), savedEntityView.getName()); + Assert.assertEquals(entityViewUpdateMsg.getIdMSB(), savedEntityView.getUuidId().getMostSignificantBits()); + Assert.assertEquals(entityViewUpdateMsg.getIdLSB(), savedEntityView.getUuidId().getLeastSignificantBits()); + Assert.assertEquals(entityViewUpdateMsg.getEntityIdMSB(), device.getUuidId().getMostSignificantBits()); + Assert.assertEquals(entityViewUpdateMsg.getEntityIdLSB(), device.getUuidId().getLeastSignificantBits()); + Assert.assertEquals(entityViewUpdateMsg.getEntityType().name(), device.getId().getEntityType().name()); + + edgeImitator.expectMessageAmount(1); + doDelete("/api/edge/" + edge.getId().getId().toString() + + "/entityView/" + savedEntityView.getId().getId().toString(), EntityView.class); + edgeImitator.waitForMessages(); + + latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof EntityViewUpdateMsg); + entityViewUpdateMsg = (EntityViewUpdateMsg) latestMessage; + Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, entityViewUpdateMsg.getMsgType()); + Assert.assertEquals(entityViewUpdateMsg.getIdMSB(), savedEntityView.getUuidId().getMostSignificantBits()); + Assert.assertEquals(entityViewUpdateMsg.getIdLSB(), savedEntityView.getUuidId().getLeastSignificantBits()); + + edgeImitator.expectMessageAmount(1); + doDelete("/api/entityView/" + savedEntityView.getId().getId().toString()) + .andExpect(status().isOk()); + edgeImitator.waitForMessages(); + + latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof EntityViewUpdateMsg); + entityViewUpdateMsg = (EntityViewUpdateMsg) latestMessage; + Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, entityViewUpdateMsg.getMsgType()); + Assert.assertEquals(entityViewUpdateMsg.getIdMSB(), savedEntityView.getUuidId().getMostSignificantBits()); + Assert.assertEquals(entityViewUpdateMsg.getIdLSB(), savedEntityView.getUuidId().getLeastSignificantBits()); + + log.info("EntityView tested successfully"); + } + + private void testCustomer() throws Exception { + log.info("Testing Customer"); + + Customer customer = new Customer(); + customer.setTitle("Edge Customer 1"); + Customer savedCustomer = doPost("/api/customer", customer, Customer.class); + + edgeImitator.expectMessageAmount(1); + doPost("/api/customer/" + savedCustomer.getId().getId().toString() + + "/edge/" + edge.getId().getId().toString(), Edge.class); + edgeImitator.waitForMessages(); + + AbstractMessage latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof CustomerUpdateMsg); + CustomerUpdateMsg customerUpdateMsg = (CustomerUpdateMsg) latestMessage; + Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, customerUpdateMsg.getMsgType()); + Assert.assertEquals(customerUpdateMsg.getIdMSB(), savedCustomer.getUuidId().getMostSignificantBits()); + Assert.assertEquals(customerUpdateMsg.getIdLSB(), savedCustomer.getUuidId().getLeastSignificantBits()); + Assert.assertEquals(customerUpdateMsg.getTitle(), savedCustomer.getTitle()); + + edgeImitator.expectMessageAmount(1); + doDelete("/api/customer/edge/" + edge.getId().getId().toString(), Edge.class); + edgeImitator.waitForMessages(); + + latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof CustomerUpdateMsg); + customerUpdateMsg = (CustomerUpdateMsg) latestMessage; + Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, customerUpdateMsg.getMsgType()); + Assert.assertEquals(customerUpdateMsg.getIdMSB(), savedCustomer.getUuidId().getMostSignificantBits()); + Assert.assertEquals(customerUpdateMsg.getIdLSB(), savedCustomer.getUuidId().getLeastSignificantBits()); + + edgeImitator.expectMessageAmount(1); + doDelete("/api/customer/" + savedCustomer.getId().getId().toString()) + .andExpect(status().isOk()); + edgeImitator.waitForMessages(); + + latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof CustomerUpdateMsg); + customerUpdateMsg = (CustomerUpdateMsg) latestMessage; + Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, customerUpdateMsg.getMsgType()); + Assert.assertEquals(customerUpdateMsg.getIdMSB(), savedCustomer.getUuidId().getMostSignificantBits()); + Assert.assertEquals(customerUpdateMsg.getIdLSB(), savedCustomer.getUuidId().getLeastSignificantBits()); + + log.info("Customer tested successfully"); + } + + private void testWidgetsBundleAndWidgetType() throws Exception { + log.info("Testing WidgetsBundle and WidgetType"); + + WidgetsBundle widgetsBundle = new WidgetsBundle(); + widgetsBundle.setTitle("Test Widget Bundle"); + + edgeImitator.expectMessageAmount(1); + WidgetsBundle savedWidgetsBundle = doPost("/api/widgetsBundle", widgetsBundle, WidgetsBundle.class); + edgeImitator.waitForMessages(); + + AbstractMessage latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof WidgetsBundleUpdateMsg); + WidgetsBundleUpdateMsg widgetsBundleUpdateMsg = (WidgetsBundleUpdateMsg) latestMessage; + Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, widgetsBundleUpdateMsg.getMsgType()); + Assert.assertEquals(widgetsBundleUpdateMsg.getIdMSB(), savedWidgetsBundle.getUuidId().getMostSignificantBits()); + Assert.assertEquals(widgetsBundleUpdateMsg.getIdLSB(), savedWidgetsBundle.getUuidId().getLeastSignificantBits()); + Assert.assertEquals(widgetsBundleUpdateMsg.getAlias(), savedWidgetsBundle.getAlias()); + Assert.assertEquals(widgetsBundleUpdateMsg.getTitle(), savedWidgetsBundle.getTitle()); + + WidgetType widgetType = new WidgetType(); + widgetType.setName("Test Widget Type"); + widgetType.setBundleAlias(savedWidgetsBundle.getAlias()); + ObjectNode descriptor = mapper.createObjectNode(); + descriptor.put("key", "value"); + widgetType.setDescriptor(descriptor); + + edgeImitator.expectMessageAmount(1); + WidgetType savedWidgetType = doPost("/api/widgetType", widgetType, WidgetType.class); + edgeImitator.waitForMessages(); + + latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof WidgetTypeUpdateMsg); + WidgetTypeUpdateMsg widgetTypeUpdateMsg = (WidgetTypeUpdateMsg) latestMessage; + Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, widgetTypeUpdateMsg.getMsgType()); + Assert.assertEquals(widgetTypeUpdateMsg.getIdMSB(), savedWidgetType.getUuidId().getMostSignificantBits()); + Assert.assertEquals(widgetTypeUpdateMsg.getIdLSB(), savedWidgetType.getUuidId().getLeastSignificantBits()); + Assert.assertEquals(widgetTypeUpdateMsg.getAlias(), savedWidgetType.getAlias()); + Assert.assertEquals(widgetTypeUpdateMsg.getName(), savedWidgetType.getName()); + Assert.assertEquals(JacksonUtil.toJsonNode(widgetTypeUpdateMsg.getDescriptorJson()), savedWidgetType.getDescriptor()); + + edgeImitator.expectMessageAmount(1); + doDelete("/api/widgetType/" + savedWidgetType.getId().getId().toString()) + .andExpect(status().isOk()); + edgeImitator.waitForMessages(); + + latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof WidgetTypeUpdateMsg); + widgetTypeUpdateMsg = (WidgetTypeUpdateMsg) latestMessage; + Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, widgetTypeUpdateMsg.getMsgType()); + Assert.assertEquals(widgetTypeUpdateMsg.getIdMSB(), savedWidgetType.getUuidId().getMostSignificantBits()); + Assert.assertEquals(widgetTypeUpdateMsg.getIdLSB(), savedWidgetType.getUuidId().getLeastSignificantBits()); + + edgeImitator.expectMessageAmount(1); + doDelete("/api/widgetsBundle/" + savedWidgetsBundle.getId().getId().toString()) + .andExpect(status().isOk()); + edgeImitator.waitForMessages(); + + latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof WidgetsBundleUpdateMsg); + widgetsBundleUpdateMsg = (WidgetsBundleUpdateMsg) latestMessage; + Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, widgetsBundleUpdateMsg.getMsgType()); + Assert.assertEquals(widgetsBundleUpdateMsg.getIdMSB(), savedWidgetsBundle.getUuidId().getMostSignificantBits()); + Assert.assertEquals(widgetsBundleUpdateMsg.getIdLSB(), savedWidgetsBundle.getUuidId().getLeastSignificantBits()); + + log.info("WidgetsBundle and WidgetType tested successfully"); + } + + private void testTimeseries() throws Exception { + log.info("Testing timeseries"); + List edgeDevices = doGetTypedWithPageLink("/api/edge/" + edge.getId().getId().toString() + "/devices?", + new TypeReference>() {}, new PageLink(100)).getData(); + Assert.assertEquals(1, edgeDevices.size()); + Device device = edgeDevices.get(0); + Assert.assertEquals("Edge Device 1", device.getName()); + + String timeseriesData = "{\"data\":{\"temperature\":25},\"ts\":" + System.currentTimeMillis() + "}"; + JsonNode timeseriesEntityData = mapper.readTree(timeseriesData); + EdgeEvent edgeEvent1 = constructEdgeEvent(tenantId, edge.getId(), ActionType.TIMESERIES_UPDATED, device.getId().getId(), EdgeEventType.DEVICE, timeseriesEntityData); + edgeImitator.expectMessageAmount(1); + edgeEventService.saveAsync(edgeEvent1); + edgeImitator.waitForMessages(); + + AbstractMessage latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof EntityDataProto); + EntityDataProto latestEntityDataMsg = (EntityDataProto) latestMessage; + Assert.assertEquals(latestEntityDataMsg.getEntityIdMSB(), device.getUuidId().getMostSignificantBits()); + Assert.assertEquals(latestEntityDataMsg.getEntityIdLSB(), device.getUuidId().getLeastSignificantBits()); + Assert.assertEquals(latestEntityDataMsg.getEntityType(), device.getId().getEntityType().name()); + Assert.assertTrue(latestEntityDataMsg.hasPostTelemetryMsg()); + + TransportProtos.PostTelemetryMsg postTelemetryMsg = latestEntityDataMsg.getPostTelemetryMsg(); + Assert.assertEquals(1, postTelemetryMsg.getTsKvListCount()); + TransportProtos.TsKvListProto tsKvListProto = postTelemetryMsg.getTsKvList(0); + Assert.assertEquals(timeseriesEntityData.get("ts").asLong(), tsKvListProto.getTs()); + Assert.assertEquals(1, tsKvListProto.getKvCount()); + TransportProtos.KeyValueProto keyValueProto = tsKvListProto.getKv(0); + Assert.assertEquals("temperature", keyValueProto.getKey()); + Assert.assertEquals(25, keyValueProto.getLongV()); + log.info("Timeseries tested successfully"); + } + + private void testAttributes() throws Exception { + log.info("Testing attributes"); + List edgeDevices = doGetTypedWithPageLink("/api/edge/" + edge.getId().getId().toString() + "/devices?", + new TypeReference>() {}, new PageLink(100)).getData(); + Assert.assertEquals(1, edgeDevices.size()); + Device device = edgeDevices.get(0); + Assert.assertEquals("Edge Device 1", device.getName()); + + String attributesData = "{\"scope\":\"SERVER_SCOPE\",\"kv\":{\"key\":\"value\"}}"; + JsonNode attributesEntityData = mapper.readTree(attributesData); + EdgeEvent edgeEvent1 = constructEdgeEvent(tenantId, edge.getId(), ActionType.ATTRIBUTES_UPDATED, device.getId().getId(), EdgeEventType.DEVICE, attributesEntityData); + edgeImitator.expectMessageAmount(1); + edgeEventService.saveAsync(edgeEvent1); + edgeImitator.waitForMessages(); + + AbstractMessage latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof EntityDataProto); + EntityDataProto latestEntityDataMsg = (EntityDataProto) latestMessage; + Assert.assertEquals(latestEntityDataMsg.getEntityIdMSB(), device.getUuidId().getMostSignificantBits()); + Assert.assertEquals(latestEntityDataMsg.getEntityIdLSB(), device.getUuidId().getLeastSignificantBits()); + Assert.assertEquals(latestEntityDataMsg.getEntityType(), device.getId().getEntityType().name()); + Assert.assertEquals(latestEntityDataMsg.getPostAttributeScope(), attributesEntityData.get("scope").asText()); + Assert.assertTrue(latestEntityDataMsg.hasAttributesUpdatedMsg()); + + TransportProtos.PostAttributeMsg attributesUpdatedMsg = latestEntityDataMsg.getAttributesUpdatedMsg(); + Assert.assertEquals(1, attributesUpdatedMsg.getKvCount()); + TransportProtos.KeyValueProto keyValueProto = attributesUpdatedMsg.getKv(0); + Assert.assertEquals("key", keyValueProto.getKey()); + Assert.assertEquals("value", keyValueProto.getStringV()); + + ((ObjectNode) attributesEntityData).put("isPostAttributes", true); + EdgeEvent edgeEvent2 = constructEdgeEvent(tenantId, edge.getId(), ActionType.ATTRIBUTES_UPDATED, device.getId().getId(), EdgeEventType.DEVICE, attributesEntityData); + edgeImitator.expectMessageAmount(1); + edgeEventService.saveAsync(edgeEvent2); + edgeImitator.waitForMessages(); + + latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof EntityDataProto); + latestEntityDataMsg = (EntityDataProto) latestMessage; + Assert.assertEquals(latestEntityDataMsg.getEntityIdMSB(), device.getUuidId().getMostSignificantBits()); + Assert.assertEquals(latestEntityDataMsg.getEntityIdLSB(), device.getUuidId().getLeastSignificantBits()); + Assert.assertEquals(latestEntityDataMsg.getEntityType(), device.getId().getEntityType().name()); + Assert.assertEquals(latestEntityDataMsg.getPostAttributeScope(), attributesEntityData.get("scope").asText()); + Assert.assertTrue(latestEntityDataMsg.hasPostAttributesMsg()); + + attributesUpdatedMsg = latestEntityDataMsg.getPostAttributesMsg(); + Assert.assertEquals(1, attributesUpdatedMsg.getKvCount()); + keyValueProto = attributesUpdatedMsg.getKv(0); + Assert.assertEquals("key", keyValueProto.getKey()); + Assert.assertEquals("value", keyValueProto.getStringV()); + + log.info("Attributes tested successfully"); + } + + private void testSendMessagesToCloud() throws Exception { + log.info("Sending messages to cloud"); + sendDevice(); + sendAlarm(); + sendTelemetry(); + sendRelation(); + sendDeleteDeviceOnEdge(); + sendRuleChainMetadataRequest(); + sendUserCredentialsRequest(); + sendDeviceCredentialsRequest(); + log.info("Messages were sent successfully"); + } + + private void sendDevice() throws Exception { + UUID uuid = Uuids.timeBased(); + + UplinkMsg.Builder builder = UplinkMsg.newBuilder(); + DeviceUpdateMsg.Builder deviceUpdateMsgBuilder = DeviceUpdateMsg.newBuilder(); + deviceUpdateMsgBuilder.setIdMSB(uuid.getMostSignificantBits()); + deviceUpdateMsgBuilder.setIdLSB(uuid.getLeastSignificantBits()); + deviceUpdateMsgBuilder.setName("Edge Device 2"); + deviceUpdateMsgBuilder.setType("test"); + deviceUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); + builder.addDeviceUpdateMsg(deviceUpdateMsgBuilder.build()); + edgeImitator.expectResponsesAmount(1); + edgeImitator.sendUplinkMsg(builder.build()); + edgeImitator.waitForResponses(); + + Device device = doGet("/api/device/" + uuid.toString(), Device.class); + Assert.assertNotNull(device); + Assert.assertEquals("Edge Device 2", device.getName()); + } + + private void sendAlarm() throws Exception { + List edgeDevices = doGetTypedWithPageLink("/api/edge/" + edge.getId().getId().toString() + "/devices?", + new TypeReference>() {}, new PageLink(100)).getData(); + Optional foundDevice = edgeDevices.stream().filter(device1 -> device1.getName().equals("Edge Device 2")).findAny(); + Assert.assertTrue(foundDevice.isPresent()); + Device device = foundDevice.get(); + + UplinkMsg.Builder builder = UplinkMsg.newBuilder(); + AlarmUpdateMsg.Builder alarmUpdateMgBuilder = AlarmUpdateMsg.newBuilder(); + alarmUpdateMgBuilder.setName("alarm from edge"); + alarmUpdateMgBuilder.setStatus(AlarmStatus.ACTIVE_UNACK.name()); + alarmUpdateMgBuilder.setSeverity(AlarmSeverity.CRITICAL.name()); + alarmUpdateMgBuilder.setOriginatorName(device.getName()); + alarmUpdateMgBuilder.setOriginatorType(EntityType.DEVICE.name()); + builder.addAlarmUpdateMsg(alarmUpdateMgBuilder.build()); + edgeImitator.expectResponsesAmount(1); + edgeImitator.sendUplinkMsg(builder.build()); + edgeImitator.waitForResponses(); + + + List alarms = doGetTypedWithPageLink("/api/alarm/{entityType}/{entityId}?", + new TypeReference>() {}, + new PageLink(100), device.getId().getEntityType().name(), device.getId().getId().toString()) + .getData(); + Optional foundAlarm = alarms.stream().filter(alarm -> alarm.getType().equals("alarm from edge")).findAny(); + Assert.assertTrue(foundAlarm.isPresent()); + AlarmInfo alarmInfo = foundAlarm.get(); + Assert.assertEquals(device.getId(), alarmInfo.getOriginator()); + Assert.assertEquals(AlarmStatus.ACTIVE_UNACK, alarmInfo.getStatus()); + Assert.assertEquals(AlarmSeverity.CRITICAL, alarmInfo.getSeverity()); + } + + private void sendRelation() throws Exception { + List edgeDevices = doGetTypedWithPageLink("/api/edge/" + edge.getId().getId().toString() + "/devices?", + new TypeReference>() {}, new PageLink(100)).getData(); + Optional foundDevice1 = edgeDevices.stream().filter(device1 -> device1.getName().equals("Edge Device 1")).findAny(); + Assert.assertTrue(foundDevice1.isPresent()); + Device device1 = foundDevice1.get(); + Optional foundDevice2 = edgeDevices.stream().filter(device2 -> device2.getName().equals("Edge Device 2")).findAny(); + Assert.assertTrue(foundDevice2.isPresent()); + Device device2 = foundDevice2.get(); + + UplinkMsg.Builder builder = UplinkMsg.newBuilder(); + RelationUpdateMsg.Builder relationUpdateMsgBuilder = RelationUpdateMsg.newBuilder(); + relationUpdateMsgBuilder.setType("test"); + relationUpdateMsgBuilder.setTypeGroup(RelationTypeGroup.COMMON.name()); + relationUpdateMsgBuilder.setToIdMSB(device1.getId().getId().getMostSignificantBits()); + relationUpdateMsgBuilder.setToIdLSB(device1.getId().getId().getLeastSignificantBits()); + relationUpdateMsgBuilder.setToEntityType(device1.getId().getEntityType().name()); + relationUpdateMsgBuilder.setFromIdMSB(device2.getId().getId().getMostSignificantBits()); + relationUpdateMsgBuilder.setFromIdLSB(device2.getId().getId().getLeastSignificantBits()); + relationUpdateMsgBuilder.setFromEntityType(device2.getId().getEntityType().name()); + relationUpdateMsgBuilder.setAdditionalInfo("{}"); + builder.addRelationUpdateMsg(relationUpdateMsgBuilder.build()); + UplinkMsg msg = builder.build(); + edgeImitator.expectResponsesAmount(1); + edgeImitator.sendUplinkMsg(msg); + edgeImitator.waitForResponses(); + + EntityRelation relation = doGet("/api/relation?" + + "&fromId=" + device2.getId().getId().toString() + + "&fromType=" + device2.getId().getEntityType().name() + + "&relationType=" + "test" + + "&relationTypeGroup=" + RelationTypeGroup.COMMON.name() + + "&toId=" + device1.getId().getId().toString() + + "&toType=" + device1.getId().getEntityType().name(), EntityRelation.class); + Assert.assertNotNull(relation); + } + + private void sendTelemetry() throws Exception { + List edgeDevices = doGetTypedWithPageLink("/api/edge/" + edge.getId().getId().toString() + "/devices?", + new TypeReference>() {}, new PageLink(100)).getData(); + Optional foundDevice = edgeDevices.stream().filter(device1 -> device1.getName().equals("Edge Device 2")).findAny(); + Assert.assertTrue(foundDevice.isPresent()); + Device device = foundDevice.get(); + + edgeImitator.expectResponsesAmount(2); + + JsonObject data = new JsonObject(); + String timeseriesKey = "key"; + String timeseriesValue = "25"; + data.addProperty(timeseriesKey, timeseriesValue); + UplinkMsg.Builder builder1 = UplinkMsg.newBuilder(); + EntityDataProto.Builder entityDataBuilder = EntityDataProto.newBuilder(); + entityDataBuilder.setPostTelemetryMsg(JsonConverter.convertToTelemetryProto(data, System.currentTimeMillis())); + entityDataBuilder.setEntityType(device.getId().getEntityType().name()); + entityDataBuilder.setEntityIdMSB(device.getUuidId().getMostSignificantBits()); + entityDataBuilder.setEntityIdLSB(device.getUuidId().getLeastSignificantBits()); + builder1.addEntityData(entityDataBuilder.build()); + edgeImitator.sendUplinkMsg(builder1.build()); + + JsonObject attributesData = new JsonObject(); + String attributesKey = "test_attr"; + String attributesValue = "test_value"; + attributesData.addProperty(attributesKey, attributesValue); + UplinkMsg.Builder builder2 = UplinkMsg.newBuilder(); + EntityDataProto.Builder entityDataBuilder2 = EntityDataProto.newBuilder(); + entityDataBuilder2.setEntityType(device.getId().getEntityType().name()); + entityDataBuilder2.setEntityIdMSB(device.getId().getId().getMostSignificantBits()); + entityDataBuilder2.setEntityIdLSB(device.getId().getId().getLeastSignificantBits()); + entityDataBuilder2.setAttributesUpdatedMsg(JsonConverter.convertToAttributesProto(attributesData)); + entityDataBuilder2.setPostAttributeScope(DataConstants.SERVER_SCOPE); + builder2.addEntityData(entityDataBuilder2.build()); + edgeImitator.sendUplinkMsg(builder2.build()); + edgeImitator.waitForResponses(); + + Thread.sleep(1000); + Map>> timeseries = doGetAsync("/api/plugins/telemetry/DEVICE/" + device.getUuidId() + "/values/timeseries?keys=" + timeseriesKey, Map.class); + Assert.assertTrue(timeseries.containsKey(timeseriesKey)); + Assert.assertEquals(1, timeseries.get(timeseriesKey).size()); + Assert.assertEquals(timeseriesValue, timeseries.get(timeseriesKey).get(0).get("value")); + + List> attributes = doGetAsync("/api/plugins/telemetry/DEVICE/" + device.getId() + "/values/attributes/" + DataConstants.SERVER_SCOPE, List.class); + Assert.assertEquals(1, attributes.size()); + Assert.assertEquals(attributes.get(0).get("key"), attributesKey); + Assert.assertEquals(attributes.get(0).get("value"), attributesValue); + + } + + private void sendRuleChainMetadataRequest() throws Exception { + RuleChainId edgeRootRuleChainId = edge.getRootRuleChainId(); + + UplinkMsg.Builder builder = UplinkMsg.newBuilder(); + RuleChainMetadataRequestMsg.Builder ruleChainMetadataRequestMsgBuilder = RuleChainMetadataRequestMsg.newBuilder(); + ruleChainMetadataRequestMsgBuilder.setRuleChainIdMSB(edgeRootRuleChainId.getId().getMostSignificantBits()); + ruleChainMetadataRequestMsgBuilder.setRuleChainIdLSB(edgeRootRuleChainId.getId().getLeastSignificantBits()); + builder.addRuleChainMetadataRequestMsg(ruleChainMetadataRequestMsgBuilder.build()); + edgeImitator.expectResponsesAmount(1); + edgeImitator.expectMessageAmount(1); + edgeImitator.sendUplinkMsg(builder.build()); + edgeImitator.waitForResponses(); + edgeImitator.waitForMessages(); + + AbstractMessage latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof RuleChainMetadataUpdateMsg); + RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = (RuleChainMetadataUpdateMsg) latestMessage; + Assert.assertEquals(ruleChainMetadataUpdateMsg.getRuleChainIdMSB(), edgeRootRuleChainId.getId().getMostSignificantBits()); + Assert.assertEquals(ruleChainMetadataUpdateMsg.getRuleChainIdLSB(), edgeRootRuleChainId.getId().getLeastSignificantBits()); + } + + private void sendUserCredentialsRequest() throws Exception { + UserId userId = edgeImitator.getUserId(); + + UplinkMsg.Builder builder = UplinkMsg.newBuilder(); + UserCredentialsRequestMsg.Builder userCredentialsRequestMsgBuilder = UserCredentialsRequestMsg.newBuilder(); + userCredentialsRequestMsgBuilder.setUserIdMSB(userId.getId().getMostSignificantBits()); + userCredentialsRequestMsgBuilder.setUserIdLSB(userId.getId().getLeastSignificantBits()); + builder.addUserCredentialsRequestMsg(userCredentialsRequestMsgBuilder.build()); + edgeImitator.expectResponsesAmount(1); + edgeImitator.expectMessageAmount(1); + edgeImitator.sendUplinkMsg(builder.build()); + edgeImitator.waitForResponses(); + edgeImitator.waitForMessages(); + + AbstractMessage latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof UserCredentialsUpdateMsg); + UserCredentialsUpdateMsg userCredentialsUpdateMsg = (UserCredentialsUpdateMsg) latestMessage; + Assert.assertEquals(userCredentialsUpdateMsg.getUserIdMSB(), userId.getId().getMostSignificantBits()); + Assert.assertEquals(userCredentialsUpdateMsg.getUserIdLSB(), userId.getId().getLeastSignificantBits()); + } + + private void sendDeviceCredentialsRequest() throws Exception { + List edgeDevices = doGetTypedWithPageLink("/api/edge/" + edge.getId().getId().toString() + "/devices?", + new TypeReference>() {}, new PageLink(100)).getData(); + Optional foundDevice = edgeDevices.stream().filter(device1 -> device1.getName().equals("Edge Device 1")).findAny(); + Assert.assertTrue(foundDevice.isPresent()); + Device device = foundDevice.get(); + + DeviceCredentials deviceCredentials = doGet("/api/device/" + device.getId().getId().toString() + "/credentials", DeviceCredentials.class); + + UplinkMsg.Builder builder = UplinkMsg.newBuilder(); + DeviceCredentialsRequestMsg.Builder deviceCredentialsRequestMsgBuilder = DeviceCredentialsRequestMsg.newBuilder(); + deviceCredentialsRequestMsgBuilder.setDeviceIdMSB(device.getUuidId().getMostSignificantBits()); + deviceCredentialsRequestMsgBuilder.setDeviceIdLSB(device.getUuidId().getLeastSignificantBits()); + builder.addDeviceCredentialsRequestMsg(deviceCredentialsRequestMsgBuilder.build()); + + edgeImitator.expectResponsesAmount(1); + edgeImitator.expectMessageAmount(1); + edgeImitator.sendUplinkMsg(builder.build()); + edgeImitator.waitForResponses(); + edgeImitator.waitForMessages(); + + AbstractMessage latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof DeviceCredentialsUpdateMsg); + DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg = (DeviceCredentialsUpdateMsg) latestMessage; + Assert.assertEquals(deviceCredentialsUpdateMsg.getDeviceIdMSB(), device.getUuidId().getMostSignificantBits()); + Assert.assertEquals(deviceCredentialsUpdateMsg.getDeviceIdLSB(), device.getUuidId().getLeastSignificantBits()); + Assert.assertEquals(deviceCredentialsUpdateMsg.getCredentialsType(), deviceCredentials.getCredentialsType().name()); + Assert.assertEquals(deviceCredentialsUpdateMsg.getCredentialsId(), deviceCredentials.getCredentialsId()); + } + + private void sendDeleteDeviceOnEdge() throws Exception { + List edgeDevices = doGetTypedWithPageLink("/api/edge/" + edge.getId().getId().toString() + "/devices?", + new TypeReference>() {}, new PageLink(100)).getData(); + Optional foundDevice = edgeDevices.stream().filter(device1 -> device1.getName().equals("Edge Device 2")).findAny(); + Assert.assertTrue(foundDevice.isPresent()); + Device device = foundDevice.get(); + UplinkMsg.Builder builder = UplinkMsg.newBuilder(); + DeviceUpdateMsg.Builder deviceDeleteMsgBuilder = DeviceUpdateMsg.newBuilder(); + deviceDeleteMsgBuilder.setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE); + deviceDeleteMsgBuilder.setIdMSB(device.getId().getId().getMostSignificantBits()); + deviceDeleteMsgBuilder.setIdLSB(device.getId().getId().getLeastSignificantBits()); + builder.addDeviceUpdateMsg(deviceDeleteMsgBuilder.build()); + edgeImitator.expectResponsesAmount(1); + edgeImitator.sendUplinkMsg(builder.build()); + edgeImitator.waitForResponses(); + device = doGet("/api/device/" + device.getId().getId().toString(), Device.class); + Assert.assertNotNull(device); + edgeDevices = doGetTypedWithPageLink("/api/edge/" + edge.getId().getId().toString() + "/devices?", + new TypeReference>() { + }, new PageLink(100)).getData(); + Assert.assertFalse(edgeDevices.contains(device)); + } + + private void installation() throws Exception { + edge = doPost("/api/edge", constructEdge("Test Edge", "test"), Edge.class); + + Device device = new Device(); + device.setName("Edge Device 1"); + device.setType("test"); + Device savedDevice = doPost("/api/device", device, Device.class); + doPost("/api/edge/" + edge.getId().getId().toString() + + "/device/" + savedDevice.getId().getId().toString(), Device.class); + + Asset asset = new Asset(); + asset.setName("Edge Asset 1"); + asset.setType("test"); + Asset savedAsset = doPost("/api/asset", asset, Asset.class); + doPost("/api/edge/" + edge.getId().getId().toString() + + "/asset/" + savedAsset.getId().getId().toString(), Asset.class); + } + + private void uninstallation() throws Exception { + + PageData pageDataDevices = doGetTypedWithPageLink("/api/edge/" + edge.getId().getId().toString() + "/devices?", + new TypeReference>() {}, new PageLink(100)); + for (Device device: pageDataDevices.getData()) { + doDelete("/api/device/" + device.getId().getId().toString()) + .andExpect(status().isOk()); + } + + PageData pageDataAssets = doGetTypedWithPageLink("/api/edge/" + edge.getId().getId().toString() + "/assets?", + new TypeReference>() {}, new PageLink(100)); + for (Asset asset: pageDataAssets.getData()) { + doDelete("/api/asset/" + asset.getId().getId().toString()) + .andExpect(status().isOk()); + } + + doDelete("/api/edge/" + edge.getId().getId().toString()) + .andExpect(status().isOk()); + } + + private EdgeEvent constructEdgeEvent(TenantId tenantId, EdgeId edgeId, ActionType edgeEventAction, UUID entityId, EdgeEventType edgeEventType, JsonNode entityBody) { + EdgeEvent edgeEvent = new EdgeEvent(); + edgeEvent.setEdgeId(edgeId); + edgeEvent.setTenantId(tenantId); + edgeEvent.setAction(edgeEventAction.name()); + edgeEvent.setEntityId(entityId); + edgeEvent.setType(edgeEventType); + edgeEvent.setBody(entityBody); + return edgeEvent; + } +} diff --git a/application/src/test/java/org/thingsboard/server/edge/EdgeNoSqlTestSuite.java b/application/src/test/java/org/thingsboard/server/edge/EdgeNoSqlTestSuite.java new file mode 100644 index 0000000000..06d566d410 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/edge/EdgeNoSqlTestSuite.java @@ -0,0 +1,47 @@ +/** + * 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.edge; + +import org.cassandraunit.dataset.cql.ClassPathCQLDataSet; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.extensions.cpsuite.ClasspathSuite; +import org.junit.runner.RunWith; +import org.thingsboard.server.dao.CustomCassandraCQLUnit; +import org.thingsboard.server.queue.memory.InMemoryStorage; + +import java.util.Arrays; + +@RunWith(ClasspathSuite.class) +@ClasspathSuite.ClassnameFilters({ + "org.thingsboard.server.edge.nosql.*Test"}) +public class EdgeNoSqlTestSuite { + + @ClassRule + public static CustomCassandraCQLUnit cassandraUnit = + new CustomCassandraCQLUnit( + Arrays.asList( + new ClassPathCQLDataSet("cassandra/schema-ts.cql", false, false), + new ClassPathCQLDataSet("cassandra/schema-entities.cql", false, false), + new ClassPathCQLDataSet("cassandra/system-data.cql", false, false), + new ClassPathCQLDataSet("cassandra/system-test.cql", false, false)), + "cassandra-test.yaml", 30000l); + + @BeforeClass + public static void cleanupInMemStorage(){ + InMemoryStorage.getInstance().cleanup(); + } +} diff --git a/application/src/test/java/org/thingsboard/server/edge/EdgeSqlTestSuite.java b/application/src/test/java/org/thingsboard/server/edge/EdgeSqlTestSuite.java new file mode 100644 index 0000000000..5d0e4f977c --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/edge/EdgeSqlTestSuite.java @@ -0,0 +1,41 @@ +/** + * 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.edge; + +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.extensions.cpsuite.ClasspathSuite; +import org.junit.runner.RunWith; +import org.thingsboard.server.dao.CustomSqlUnit; +import org.thingsboard.server.queue.memory.InMemoryStorage; + +import java.util.Arrays; + +@RunWith(ClasspathSuite.class) +@ClasspathSuite.ClassnameFilters({"org.thingsboard.server.edge.sql.*Test"}) +public class EdgeSqlTestSuite { + + @ClassRule + public static CustomSqlUnit sqlUnit = new CustomSqlUnit( + Arrays.asList("sql/schema-ts-hsql.sql", "sql/schema-entities-hsql.sql", "sql/schema-entities-idx.sql", "sql/system-data.sql"), + "sql/hsql/drop-all-tables.sql", + "sql-test.properties"); + + @BeforeClass + public static void cleanupInMemStorage(){ + InMemoryStorage.getInstance().cleanup(); + } +} diff --git a/application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java b/application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java new file mode 100644 index 0000000000..3eca5438dd --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java @@ -0,0 +1,268 @@ +/** + * 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.edge.imitator; + +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.AbstractMessage; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.thingsboard.edge.rpc.EdgeGrpcClient; +import org.thingsboard.edge.rpc.EdgeRpcClient; +import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.gen.edge.AlarmUpdateMsg; +import org.thingsboard.server.gen.edge.AssetUpdateMsg; +import org.thingsboard.server.gen.edge.CustomerUpdateMsg; +import org.thingsboard.server.gen.edge.DashboardUpdateMsg; +import org.thingsboard.server.gen.edge.DeviceCredentialsUpdateMsg; +import org.thingsboard.server.gen.edge.DeviceUpdateMsg; +import org.thingsboard.server.gen.edge.DownlinkMsg; +import org.thingsboard.server.gen.edge.DownlinkResponseMsg; +import org.thingsboard.server.gen.edge.EdgeConfiguration; +import org.thingsboard.server.gen.edge.EntityDataProto; +import org.thingsboard.server.gen.edge.EntityViewUpdateMsg; +import org.thingsboard.server.gen.edge.RelationUpdateMsg; +import org.thingsboard.server.gen.edge.RuleChainMetadataUpdateMsg; +import org.thingsboard.server.gen.edge.RuleChainUpdateMsg; +import org.thingsboard.server.gen.edge.UplinkMsg; +import org.thingsboard.server.gen.edge.UplinkResponseMsg; +import org.thingsboard.server.gen.edge.UserCredentialsUpdateMsg; +import org.thingsboard.server.gen.edge.UserUpdateMsg; +import org.thingsboard.server.gen.edge.WidgetTypeUpdateMsg; +import org.thingsboard.server.gen.edge.WidgetsBundleUpdateMsg; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +@Slf4j +public class EdgeImitator { + + private String routingKey; + private String routingSecret; + + private EdgeRpcClient edgeRpcClient; + + private CountDownLatch messagesLatch; + private CountDownLatch responsesLatch; + private List> ignoredTypes; + + @Getter + private EdgeConfiguration configuration; + @Getter + private UserId userId; + @Getter + private List downlinkMsgs; + + public EdgeImitator(String host, int port, String routingKey, String routingSecret) throws NoSuchFieldException, IllegalAccessException { + edgeRpcClient = new EdgeGrpcClient(); + messagesLatch = new CountDownLatch(0); + responsesLatch = new CountDownLatch(0); + downlinkMsgs = new ArrayList<>(); + ignoredTypes = new ArrayList<>(); + this.routingKey = routingKey; + this.routingSecret = routingSecret; + setEdgeCredentials("rpcHost", host); + setEdgeCredentials("rpcPort", port); + setEdgeCredentials("keepAliveTimeSec", 300); + } + + private void setEdgeCredentials(String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException { + Field fieldToSet = edgeRpcClient.getClass().getDeclaredField(fieldName); + fieldToSet.setAccessible(true); + fieldToSet.set(edgeRpcClient, value); + fieldToSet.setAccessible(false); + } + + public void connect() { + edgeRpcClient.connect(routingKey, routingSecret, + this::onUplinkResponse, + this::onEdgeUpdate, + this::onDownlink, + this::onClose); + + edgeRpcClient.sendSyncRequestMsg(); + } + + public void disconnect() throws InterruptedException { + edgeRpcClient.disconnect(false); + } + + public void sendUplinkMsg(UplinkMsg uplinkMsg) { + edgeRpcClient.sendUplinkMsg(uplinkMsg); + } + + private void onUplinkResponse(UplinkResponseMsg msg) { + log.info("onUplinkResponse: {}", msg); + responsesLatch.countDown(); + } + + private void onEdgeUpdate(EdgeConfiguration edgeConfiguration) { + this.configuration = edgeConfiguration; + } + + private void onUserUpdate(UserUpdateMsg userUpdateMsg) { + this.userId = new UserId(new UUID(userUpdateMsg.getIdMSB(), userUpdateMsg.getIdLSB())); + } + + private void onDownlink(DownlinkMsg downlinkMsg) { + ListenableFuture> future = processDownlinkMsg(downlinkMsg); + Futures.addCallback(future, new FutureCallback>() { + @Override + public void onSuccess(@Nullable List result) { + DownlinkResponseMsg downlinkResponseMsg = DownlinkResponseMsg.newBuilder().setSuccess(true).build(); + edgeRpcClient.sendDownlinkResponseMsg(downlinkResponseMsg); + } + + @Override + public void onFailure(Throwable t) { + DownlinkResponseMsg downlinkResponseMsg = DownlinkResponseMsg.newBuilder().setSuccess(false).setErrorMsg(t.getMessage()).build(); + edgeRpcClient.sendDownlinkResponseMsg(downlinkResponseMsg); + } + }, MoreExecutors.directExecutor()); + } + + private void onClose(Exception e) { + log.info("onClose: {}", e.getMessage()); + } + + private ListenableFuture> processDownlinkMsg(DownlinkMsg downlinkMsg) { + List> result = new ArrayList<>(); + if (downlinkMsg.getDeviceUpdateMsgList() != null && !downlinkMsg.getDeviceUpdateMsgList().isEmpty()) { + for (DeviceUpdateMsg deviceUpdateMsg: downlinkMsg.getDeviceUpdateMsgList()) { + result.add(saveDownlinkMsg(deviceUpdateMsg)); + } + } + if (downlinkMsg.getDeviceCredentialsUpdateMsgList() != null && !downlinkMsg.getDeviceCredentialsUpdateMsgList().isEmpty()) { + for (DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg: downlinkMsg.getDeviceCredentialsUpdateMsgList()) { + result.add(saveDownlinkMsg(deviceCredentialsUpdateMsg)); + } + } + if (downlinkMsg.getAssetUpdateMsgList() != null && !downlinkMsg.getAssetUpdateMsgList().isEmpty()) { + for (AssetUpdateMsg assetUpdateMsg: downlinkMsg.getAssetUpdateMsgList()) { + result.add(saveDownlinkMsg(assetUpdateMsg)); + } + } + if (downlinkMsg.getRuleChainUpdateMsgList() != null && !downlinkMsg.getRuleChainUpdateMsgList().isEmpty()) { + for (RuleChainUpdateMsg ruleChainUpdateMsg: downlinkMsg.getRuleChainUpdateMsgList()) { + result.add(saveDownlinkMsg(ruleChainUpdateMsg)); + } + } + if (downlinkMsg.getRuleChainMetadataUpdateMsgList() != null && !downlinkMsg.getRuleChainMetadataUpdateMsgList().isEmpty()) { + for (RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg: downlinkMsg.getRuleChainMetadataUpdateMsgList()) { + result.add(saveDownlinkMsg(ruleChainMetadataUpdateMsg)); + } + } + if (downlinkMsg.getDashboardUpdateMsgList() != null && !downlinkMsg.getDashboardUpdateMsgList().isEmpty()) { + for (DashboardUpdateMsg dashboardUpdateMsg: downlinkMsg.getDashboardUpdateMsgList()) { + result.add(saveDownlinkMsg(dashboardUpdateMsg)); + } + } + if (downlinkMsg.getRelationUpdateMsgList() != null && !downlinkMsg.getRelationUpdateMsgList().isEmpty()) { + for (RelationUpdateMsg relationUpdateMsg: downlinkMsg.getRelationUpdateMsgList()) { + result.add(saveDownlinkMsg(relationUpdateMsg)); + } + } + if (downlinkMsg.getAlarmUpdateMsgList() != null && !downlinkMsg.getAlarmUpdateMsgList().isEmpty()) { + for (AlarmUpdateMsg alarmUpdateMsg: downlinkMsg.getAlarmUpdateMsgList()) { + result.add(saveDownlinkMsg(alarmUpdateMsg)); + } + } + if (downlinkMsg.getEntityDataList() != null && !downlinkMsg.getEntityDataList().isEmpty()) { + for (EntityDataProto entityData: downlinkMsg.getEntityDataList()) { + result.add(saveDownlinkMsg(entityData)); + } + } + if (downlinkMsg.getEntityViewUpdateMsgList() != null && !downlinkMsg.getEntityViewUpdateMsgList().isEmpty()) { + for (EntityViewUpdateMsg entityViewUpdateMsg: downlinkMsg.getEntityViewUpdateMsgList()) { + result.add(saveDownlinkMsg(entityViewUpdateMsg)); + } + } + if (downlinkMsg.getCustomerUpdateMsgList() != null && !downlinkMsg.getCustomerUpdateMsgList().isEmpty()) { + for (CustomerUpdateMsg customerUpdateMsg: downlinkMsg.getCustomerUpdateMsgList()) { + result.add(saveDownlinkMsg(customerUpdateMsg)); + } + } + if (downlinkMsg.getWidgetsBundleUpdateMsgList() != null && !downlinkMsg.getWidgetsBundleUpdateMsgList().isEmpty()) { + for (WidgetsBundleUpdateMsg widgetsBundleUpdateMsg: downlinkMsg.getWidgetsBundleUpdateMsgList()) { + result.add(saveDownlinkMsg(widgetsBundleUpdateMsg)); + } + } + if (downlinkMsg.getWidgetTypeUpdateMsgList() != null && !downlinkMsg.getWidgetTypeUpdateMsgList().isEmpty()) { + for (WidgetTypeUpdateMsg widgetTypeUpdateMsg: downlinkMsg.getWidgetTypeUpdateMsgList()) { + result.add(saveDownlinkMsg(widgetTypeUpdateMsg)); + } + } + if (downlinkMsg.getUserUpdateMsgList() != null && !downlinkMsg.getUserUpdateMsgList().isEmpty()) { + for (UserUpdateMsg userUpdateMsg: downlinkMsg.getUserUpdateMsgList()) { + onUserUpdate(userUpdateMsg); + result.add(saveDownlinkMsg(userUpdateMsg)); + } + } + if (downlinkMsg.getUserCredentialsUpdateMsgList() != null && !downlinkMsg.getUserCredentialsUpdateMsgList().isEmpty()) { + for (UserCredentialsUpdateMsg userCredentialsUpdateMsg: downlinkMsg.getUserCredentialsUpdateMsgList()) { + result.add(saveDownlinkMsg(userCredentialsUpdateMsg)); + } + } + return Futures.allAsList(result); + } + + private ListenableFuture saveDownlinkMsg(AbstractMessage message) { + if (!ignoredTypes.contains(message.getClass())) { + downlinkMsgs.add(message); + messagesLatch.countDown(); + } + return Futures.immediateFuture(null); + } + + public void waitForMessages() throws InterruptedException { + messagesLatch.await(5, TimeUnit.SECONDS); + } + + public void expectMessageAmount(int messageAmount) { + messagesLatch = new CountDownLatch(messageAmount); + } + + public void waitForResponses() throws InterruptedException { responsesLatch.await(5, TimeUnit.SECONDS); } + + public void expectResponsesAmount(int messageAmount) { + responsesLatch = new CountDownLatch(messageAmount); + } + + public Optional findMessageByType(Class tClass) { + return (Optional) downlinkMsgs.stream().filter(downlinkMsg -> downlinkMsg.getClass().isAssignableFrom(tClass)).findAny(); + } + + public AbstractMessage getLatestMessage() { + return downlinkMsgs.get(downlinkMsgs.size() - 1); + } + + public void ignoreType(Class type) { + ignoredTypes.add(type); + } + + public void allowIgnoredTypes() { + ignoredTypes.clear(); + } + +} diff --git a/application/src/test/java/org/thingsboard/server/edge/nosql/EdgeNoSqlTest.java b/application/src/test/java/org/thingsboard/server/edge/nosql/EdgeNoSqlTest.java new file mode 100644 index 0000000000..c612eab16c --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/edge/nosql/EdgeNoSqlTest.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.edge.nosql; + +import org.thingsboard.server.dao.service.DaoNoSqlTest; +import org.thingsboard.server.edge.BaseEdgeTest; + +@DaoNoSqlTest +public class EdgeNoSqlTest extends BaseEdgeTest { +} diff --git a/application/src/test/java/org/thingsboard/server/edge/sql/EdgeSqlTest.java b/application/src/test/java/org/thingsboard/server/edge/sql/EdgeSqlTest.java new file mode 100644 index 0000000000..4b2d087fc0 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/edge/sql/EdgeSqlTest.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.edge.sql; + +import org.thingsboard.server.dao.service.DaoSqlTest; +import org.thingsboard.server.edge.BaseEdgeTest; + +@DaoSqlTest +public class EdgeSqlTest extends BaseEdgeTest { +} diff --git a/application/src/test/java/org/thingsboard/server/mqtt/MqttNoSqlTestSuite.java b/application/src/test/java/org/thingsboard/server/mqtt/MqttNoSqlTestSuite.java index 071008157e..bebf1ae017 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/MqttNoSqlTestSuite.java +++ b/application/src/test/java/org/thingsboard/server/mqtt/MqttNoSqlTestSuite.java @@ -28,8 +28,7 @@ import java.util.Arrays; @RunWith(ClasspathSuite.class) @ClasspathSuite.ClassnameFilters({ - // TODO: voba - fix before final test on cassandra - "org.thingsboard.server.mqtt.*.nosql.*VOBA_FIX_BEFORE_FINAL_TESTTest"}) + "org.thingsboard.server.mqtt.*.nosql.*Test"}) public class MqttNoSqlTestSuite { @ClassRule 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 d30efc9b49..954da11be3 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 @@ -58,7 +58,5 @@ public interface DashboardService { Dashboard unassignDashboardFromEdge(TenantId tenantId, DashboardId dashboardId, EdgeId edgeId); - void unassignEdgeDashboards(TenantId tenantId, EdgeId edgeId); - PageData findDashboardsByTenantIdAndEdgeId(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 9cb4d3e485..dd55f2b884 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 @@ -89,5 +89,5 @@ public interface DeviceService { Device unassignDeviceFromEdge(TenantId tenantId, DeviceId deviceId, EdgeId edgeId); - PageData findDevicesByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink); + PageData findDevicesByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink); } 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 index 32720491c3..14a691fa08 100644 --- 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 @@ -16,9 +16,7 @@ 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.PageData; @@ -26,10 +24,8 @@ import org.thingsboard.server.common.data.page.TimePageLink; public interface EdgeEventService { - EdgeEventType getEdgeEventTypeByEntityType(EntityType entityType); - ListenableFuture saveAsync(EdgeEvent edgeEvent); - PageData findEdgeEvents(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink); + PageData findEdgeEvents(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink, boolean withTsUpdate); } 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 7e85626a74..a5823c9ed6 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 @@ -23,6 +23,7 @@ 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.EntityId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; @@ -78,4 +79,10 @@ public interface EdgeService { ListenableFuture> findEdgesByTenantIdAndRuleChainId(TenantId tenantId, RuleChainId ruleChainId); ListenableFuture> findEdgesByTenantIdAndDashboardId(TenantId tenantId, DashboardId dashboardId); + + ListenableFuture> findRelatedEdgeIdsByEntityId(TenantId tenantId, EntityId entityId); + + Object checkInstance(Object request); + + Object activateInstance(String licenseSecret, String releaseDate); } 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 61cb6f478d..7d832c5f1b 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 @@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.page.TimePageLink; import java.util.List; @@ -81,5 +82,5 @@ public interface EntityViewService { EntityView unassignEntityViewFromEdge(TenantId tenantId, EntityViewId entityViewId, EdgeId edgeId); - PageData findEntityViewsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink); + PageData findEntityViewsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, 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 776d7c8b6b..0ce94be25a 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 @@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainData; @@ -76,9 +77,7 @@ public interface RuleChainService { RuleChain unassignRuleChainFromEdge(TenantId tenantId, RuleChainId ruleChainId, EdgeId edgeId, boolean remove); - void unassignEdgeRuleChains(TenantId tenantId, EdgeId edgeId); - - PageData findRuleChainsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink); + PageData findRuleChainsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink); RuleChain getDefaultRootEdgeRuleChain(TenantId tenantId); 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 97889a488e..73b893d099 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 @@ -16,15 +16,12 @@ package org.thingsboard.server.common.data; import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.Getter; -import lombok.Setter; -import org.thingsboard.server.common.data.edge.Edge; 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.TenantId; -import java.util.*; +import java.util.HashSet; +import java.util.Set; public class DashboardInfo extends SearchTextBased implements HasName, HasTenantId { 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 31449a7801..465e1fcd72 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 @@ -15,59 +15,41 @@ */ package org.thingsboard.server.common.data; -import org.thingsboard.server.common.data.id.EdgeId; - -import java.util.Set; +import org.thingsboard.server.common.data.edge.EdgeEventType; 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, ShortEdgeInfo edgeInfo) { - 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, ShortEdgeInfo edgeInfo) { - if (assignedEdges != null && assignedEdges.contains(edgeInfo)) { - assignedEdges.remove(edgeInfo); - assignedEdges.add(edgeInfo); - return true; - } else { - return false; - } - } - - public static boolean removeAssignedEdge(Set assignedEdges, ShortEdgeInfo edgeInfo) { - if (assignedEdges != null && assignedEdges.contains(edgeInfo)) { - assignedEdges.remove(edgeInfo); - return true; - } else { - return false; + public static EdgeEventType getEdgeEventTypeByEntityType(EntityType entityType) { + switch (entityType) { + case EDGE: + return EdgeEventType.EDGE; + 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; + case RULE_CHAIN: + return EdgeEventType.RULE_CHAIN; + case ALARM: + return EdgeEventType.ALARM; + case TENANT: + return EdgeEventType.TENANT; + case CUSTOMER: + return EdgeEventType.CUSTOMER; + case WIDGETS_BUNDLE: + return EdgeEventType.WIDGETS_BUNDLE; + case WIDGET_TYPE: + return EdgeEventType.WIDGET_TYPE; + default: + return null; } } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/ShortEdgeInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/ShortEdgeInfo.java deleted file mode 100644 index bdfc1dbb22..0000000000 --- a/common/data/src/main/java/org/thingsboard/server/common/data/ShortEdgeInfo.java +++ /dev/null @@ -1,50 +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.server.common.data; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.Setter; -import org.thingsboard.server.common.data.id.EdgeId; -import org.thingsboard.server.common.data.id.RuleChainId; - -@AllArgsConstructor -public class ShortEdgeInfo { - - @Getter @Setter - private EdgeId edgeId; - - @Getter @Setter - private String title; - - @Getter @Setter - private RuleChainId rootRuleChainId; - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - ShortEdgeInfo that = (ShortEdgeInfo) o; - - return edgeId.equals(that.edgeId); - } - - @Override - public int hashCode() { - return edgeId.hashCode(); - } -} 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 0aed389b8a..6b6706bc84 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 @@ -45,7 +45,9 @@ public enum ActionType { ASSIGNED_FROM_TENANT(false), ASSIGNED_TO_TENANT(false), ASSIGNED_TO_EDGE(false), // log edge name - UNASSIGNED_FROM_EDGE(false); // log edge name + UNASSIGNED_FROM_EDGE(false), // log edge name + CREDENTIALS_REQUEST(false), // request credentials from edge + ENTITY_EXISTS_REQUEST(false); // request to recreate entity on edge private final boolean isRead; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/edge/Edge.java b/common/data/src/main/java/org/thingsboard/server/common/data/edge/Edge.java index 2c9656a364..699fdf954a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/edge/Edge.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/edge/Edge.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.common.data.edge; -import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.databind.JsonNode; import lombok.EqualsAndHashCode; import lombok.Getter; @@ -25,8 +24,6 @@ import org.thingsboard.server.common.data.HasCustomerId; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; -import org.thingsboard.server.common.data.ShortCustomerInfo; -import org.thingsboard.server.common.data.ShortEdgeInfo; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.RuleChainId; @@ -48,6 +45,8 @@ public class Edge extends SearchTextBasedWithAdditionalInfo implements H private String label; private String routingKey; private String secret; + private String edgeLicenseKey; + private String cloudEndpoint; private transient JsonNode configuration; public Edge() { @@ -67,14 +66,11 @@ public class Edge extends SearchTextBasedWithAdditionalInfo implements H this.name = edge.getName(); this.routingKey = edge.getRoutingKey(); this.secret = edge.getSecret(); + this.edgeLicenseKey = edge.getEdgeLicenseKey(); + this.cloudEndpoint = edge.getCloudEndpoint(); this.configuration = edge.getConfiguration(); } - @JsonIgnore - public ShortEdgeInfo toShortEdgeInfo() { - return new ShortEdgeInfo(id, name, rootRuleChainId); - } - @Override public String getSearchText() { return getName(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEvent.java b/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEvent.java index 1c9ea9343c..c76a03fa43 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEvent.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEvent.java @@ -20,7 +20,6 @@ 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; @@ -30,10 +29,11 @@ public class EdgeEvent extends BaseData { private TenantId tenantId; private EdgeId edgeId; - private String edgeEventAction; + private String action; private UUID entityId; - private EdgeEventType edgeEventType; - private transient JsonNode entityBody; + private String uid; + private EdgeEventType type; + private transient JsonNode body; public EdgeEvent() { super(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEventType.java b/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEventType.java index ada50c9998..cc295546a8 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEventType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEventType.java @@ -16,5 +16,19 @@ package org.thingsboard.server.common.data.edge; public enum EdgeEventType { - DASHBOARD, ASSET, DEVICE, ENTITY_VIEW, ALARM, RULE_CHAIN, RULE_CHAIN_METADATA, EDGE, USER, CUSTOMER, RELATION + DASHBOARD, + ASSET, + DEVICE, + ENTITY_VIEW, + ALARM, + RULE_CHAIN, + RULE_CHAIN_METADATA, + EDGE, + USER, + CUSTOMER, + RELATION, + TENANT, + WIDGETS_BUNDLE, + WIDGET_TYPE, + ADMIN_SETTINGS } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/exception/ThingsboardErrorCode.java b/common/data/src/main/java/org/thingsboard/server/common/data/exception/ThingsboardErrorCode.java index 916a50b26a..d28eea57aa 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/exception/ThingsboardErrorCode.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/exception/ThingsboardErrorCode.java @@ -28,7 +28,8 @@ public enum ThingsboardErrorCode { BAD_REQUEST_PARAMS(31), ITEM_NOT_FOUND(32), TOO_MANY_REQUESTS(33), - TOO_MANY_UPDATES(34); + TOO_MANY_UPDATES(34), + SUBSCRIPTION_VIOLATION(40); private int errorCode; 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 bb3069277f..b3713947fd 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 @@ -91,6 +91,10 @@ public class EntityIdFactory { return new RuleChainId(uuid); case ENTITY_VIEW: return new EntityViewId(uuid); + case WIDGETS_BUNDLE: + return new WidgetsBundleId(uuid); + case WIDGET_TYPE: + return new WidgetTypeId(uuid); case EDGE: return new EdgeId(uuid); } 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 38b377c4a8..650fb89130 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 @@ -28,9 +28,9 @@ 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.DownlinkMsg; +import org.thingsboard.server.gen.edge.DownlinkResponseMsg; import org.thingsboard.server.gen.edge.EdgeConfiguration; import org.thingsboard.server.gen.edge.EdgeRpcServiceGrpc; -import org.thingsboard.server.gen.edge.EntityUpdateMsg; import org.thingsboard.server.gen.edge.RequestMsg; import org.thingsboard.server.gen.edge.RequestMsgType; import org.thingsboard.server.gen.edge.ResponseMsg; @@ -40,7 +40,9 @@ import org.thingsboard.server.gen.edge.UplinkResponseMsg; import javax.net.ssl.SSLException; import java.io.File; import java.net.URISyntaxException; +import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; import java.util.function.Consumer; @Service @@ -53,6 +55,8 @@ public class EdgeGrpcClient implements EdgeRpcClient { private int rpcPort; @Value("${cloud.rpc.timeout}") private int timeoutSecs; + @Value("${cloud.rpc.keep_alive_time_sec}") + private int keepAliveTimeSec; @Value("${cloud.rpc.ssl.enabled}") private boolean sslEnabled; @Value("${cloud.rpc.ssl.cert}") @@ -62,15 +66,18 @@ public class EdgeGrpcClient implements EdgeRpcClient { private StreamObserver inputStream; + private static final ReentrantLock uplinkMsgLock = new ReentrantLock(); + @Override public void connect(String edgeKey, String edgeSecret, Consumer onUplinkResponse, Consumer onEdgeUpdate, - Consumer onEntityUpdate, Consumer onDownlink, Consumer onError) { - NettyChannelBuilder builder = NettyChannelBuilder.forAddress(rpcHost, rpcPort).usePlaintext(); + NettyChannelBuilder builder = NettyChannelBuilder.forAddress(rpcHost, rpcPort) + .keepAliveTime(keepAliveTimeSec, TimeUnit.SECONDS) + .usePlaintext(); if (sslEnabled) { try { builder.sslContext(GrpcSslContexts.forClient().trustManager(new File(Resources.getResource(certResource).toURI())).build()); @@ -79,47 +86,19 @@ 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); - this.inputStream = stub.handleMsgs(initOutputStream(edgeKey, onUplinkResponse, onEdgeUpdate, onEntityUpdate, onDownlink, onError)); + this.inputStream = stub.handleMsgs(initOutputStream(edgeKey, onUplinkResponse, onEdgeUpdate, onDownlink, onError)); this.inputStream.onNext(RequestMsg.newBuilder() .setMsgType(RequestMsgType.CONNECT_RPC_MESSAGE) .setConnectRequestMsg(ConnectRequestMsg.newBuilder().setEdgeRoutingKey(edgeKey).setEdgeSecret(edgeSecret).build()) .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(); - if (channel != null) { - channel.shutdown().awaitTermination(timeoutSecs, TimeUnit.SECONDS); - } - } - - @Override - public void sendUplinkMsg(UplinkMsg msg) { - this.inputStream.onNext(RequestMsg.newBuilder() - .setMsgType(RequestMsgType.UPLINK_RPC_MESSAGE) - .setUplinkMsg(msg) - .build()); - } - private StreamObserver initOutputStream(String edgeKey, Consumer onUplinkResponse, Consumer onEdgeUpdate, - Consumer onEntityUpdate, Consumer onDownlink, Consumer onError) { return new StreamObserver() { @@ -132,14 +111,19 @@ public class EdgeGrpcClient implements EdgeRpcClient { onEdgeUpdate.accept(connectResponseMsg.getConfiguration()); } else { log.error("[{}] Failed to establish the connection! Code: {}. Error message: {}.", edgeKey, connectResponseMsg.getResponseCode(), connectResponseMsg.getErrorMsg()); + try { + EdgeGrpcClient.this.disconnect(true); + } catch (InterruptedException e) { + log.error("[{}] Got interruption during disconnect!", edgeKey, e); + } onError.accept(new EdgeConnectionException("Failed to establish the connection! Response code: " + connectResponseMsg.getResponseCode().name())); } + } else if (responseMsg.hasEdgeUpdateMsg()) { + log.debug("[{}] Edge update message received {}", edgeKey, responseMsg.getEdgeUpdateMsg()); + onEdgeUpdate.accept(responseMsg.getEdgeUpdateMsg().getConfiguration()); } else if (responseMsg.hasUplinkResponseMsg()) { log.debug("[{}] Uplink response message received {}", edgeKey, responseMsg.getUplinkResponseMsg()); onUplinkResponse.accept(responseMsg.getUplinkResponseMsg()); - } else if (responseMsg.hasEntityUpdateMsg()) { - log.debug("[{}] Entity update message received {}", edgeKey, responseMsg.getEntityUpdateMsg()); - onEntityUpdate.accept(responseMsg.getEntityUpdateMsg()); } else if (responseMsg.hasDownlinkMsg()) { log.debug("[{}] Downlink message received {}", edgeKey, responseMsg.getDownlinkMsg()); onDownlink.accept(responseMsg.getDownlinkMsg()); @@ -149,6 +133,11 @@ public class EdgeGrpcClient implements EdgeRpcClient { @Override public void onError(Throwable t) { log.debug("[{}] The rpc session received an error!", edgeKey, t); + try { + EdgeGrpcClient.this.disconnect(true); + } catch (InterruptedException e) { + log.error("[{}] Got interruption during disconnect!", edgeKey, e); + } onError.accept(new RuntimeException(t)); } @@ -158,4 +147,54 @@ public class EdgeGrpcClient implements EdgeRpcClient { } }; } + + @Override + public void disconnect(boolean onError) throws InterruptedException { + if (!onError) { + try { + inputStream.onCompleted(); + } catch (Exception ignored) {} + } + if (channel != null) { + channel.shutdown().awaitTermination(timeoutSecs, TimeUnit.SECONDS); + } + } + + @Override + public void sendUplinkMsg(UplinkMsg msg) { + try { + uplinkMsgLock.lock(); + this.inputStream.onNext(RequestMsg.newBuilder() + .setMsgType(RequestMsgType.UPLINK_RPC_MESSAGE) + .setUplinkMsg(msg) + .build()); + } finally { + uplinkMsgLock.unlock(); + } + } + + @Override + public void sendSyncRequestMsg() { + try { + uplinkMsgLock.lock(); + this.inputStream.onNext(RequestMsg.newBuilder() + .setMsgType(RequestMsgType.SYNC_REQUEST_RPC_MESSAGE) + .build()); + } finally { + uplinkMsgLock.unlock(); + } + } + + @Override + public void sendDownlinkResponseMsg(DownlinkResponseMsg downlinkResponseMsg) { + try { + uplinkMsgLock.lock(); + this.inputStream.onNext(RequestMsg.newBuilder() + .setMsgType(RequestMsgType.UPLINK_RPC_MESSAGE) + .setDownlinkResponseMsg(downlinkResponseMsg) + .build()); + } finally { + uplinkMsgLock.unlock(); + } + } } diff --git a/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeRpcClient.java b/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeRpcClient.java index d2ab91dfaf..7aa017c3db 100644 --- a/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeRpcClient.java +++ b/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeRpcClient.java @@ -16,8 +16,8 @@ package org.thingsboard.edge.rpc; import org.thingsboard.server.gen.edge.DownlinkMsg; +import org.thingsboard.server.gen.edge.DownlinkResponseMsg; import org.thingsboard.server.gen.edge.EdgeConfiguration; -import org.thingsboard.server.gen.edge.EntityUpdateMsg; import org.thingsboard.server.gen.edge.UplinkMsg; import org.thingsboard.server.gen.edge.UplinkResponseMsg; @@ -29,11 +29,14 @@ public interface EdgeRpcClient { String integrationSecret, Consumer onUplinkResponse, Consumer onEdgeUpdate, - Consumer onEntityUpdate, Consumer onDownlink, Consumer onError); - void disconnect() throws InterruptedException; + void disconnect(boolean onError) throws InterruptedException; - void sendUplinkMsg(UplinkMsg uplinkMsg) throws InterruptedException; + void sendSyncRequestMsg(); + + void sendUplinkMsg(UplinkMsg uplinkMsg); + + void sendDownlinkResponseMsg(DownlinkResponseMsg downlinkResponseMsg); } diff --git a/common/edge-api/src/main/proto/edge.proto b/common/edge-api/src/main/proto/edge.proto index 38cbe7092c..fd26e1fcd4 100644 --- a/common/edge-api/src/main/proto/edge.proto +++ b/common/edge-api/src/main/proto/edge.proto @@ -37,31 +37,24 @@ message RequestMsg { RequestMsgType msgType = 1; ConnectRequestMsg connectRequestMsg = 2; UplinkMsg uplinkMsg = 3; + DownlinkResponseMsg downlinkResponseMsg = 4; } message ResponseMsg { ConnectResponseMsg connectResponseMsg = 1; UplinkResponseMsg uplinkResponseMsg = 2; - EntityUpdateMsg entityUpdateMsg = 3; - DownlinkMsg downlinkMsg = 4; -} - -message EntityUpdateMsg { - DeviceUpdateMsg deviceUpdateMsg = 1; - RuleChainUpdateMsg ruleChainUpdateMsg = 2; - RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = 3; - DashboardUpdateMsg dashboardUpdateMsg = 4; - AssetUpdateMsg assetUpdateMsg = 5; - EntityViewUpdateMsg entityViewUpdateMsg = 6; - AlarmUpdateMsg alarmUpdateMsg = 7; - UserUpdateMsg userUpdateMsg = 8; - CustomerUpdateMsg customerUpdateMsg = 9; - RelationUpdateMsg relationUpdateMsg = 10; + DownlinkMsg downlinkMsg = 3; + EdgeUpdateMsg edgeUpdateMsg = 4; } enum RequestMsgType { CONNECT_RPC_MESSAGE = 0; UPLINK_RPC_MESSAGE = 1; + SYNC_REQUEST_RPC_MESSAGE = 2; +} + +message EdgeUpdateMsg { + EdgeConfiguration configuration = 1; } message ConnectRequestMsg { @@ -82,12 +75,16 @@ message ConnectResponseMsg { } message EdgeConfiguration { - int64 tenantIdMSB = 1; - int64 tenantIdLSB = 2; - string name = 3; - string routingKey = 4; - string type = 5; - string cloudType = 6; + int64 edgeIdMSB = 1; + int64 edgeIdLSB = 2; + int64 tenantIdMSB = 3; + int64 tenantIdLSB = 4; + string name = 5; + string routingKey = 6; + string type = 7; + string edgeLicenseKey = 8; + string cloudEndpoint = 9; + string cloudType = 10; } enum UpdateMsgType { @@ -105,9 +102,17 @@ message EntityDataProto { string entityType = 3; transport.PostTelemetryMsg postTelemetryMsg = 4; transport.PostAttributeMsg postAttributesMsg = 5; + transport.PostAttributeMsg attributesUpdatedMsg = 6; + string postAttributeScope = 7; + AttributeDeleteMsg attributeDeleteMsg = 8; // transport.ToDeviceRpcRequestMsg ??? } +message AttributeDeleteMsg { + string scope = 1; + repeated string attributeNames = 2; +} + message RuleChainUpdateMsg { UpdateMsgType msgType = 1; int64 idMSB = 2; @@ -158,40 +163,56 @@ message DashboardUpdateMsg { UpdateMsgType msgType = 1; int64 idMSB = 2; int64 idLSB = 3; - string title = 4; - string configuration = 5; + int64 customerIdMSB = 4; + int64 customerIdLSB = 5; + string title = 6; + string configuration = 7; } message DeviceUpdateMsg { UpdateMsgType msgType = 1; int64 idMSB = 2; int64 idLSB = 3; - string name = 4; - string type = 5; - string label = 6; - string credentialsType = 7; - string credentialsId = 8; - string credentialsValue = 9; + int64 customerIdMSB = 4; + int64 customerIdLSB = 5; + string name = 6; + string type = 7; + string label = 8; + string additionalInfo = 9; +} + +message DeviceCredentialsUpdateMsg { + int64 deviceIdMSB = 1; + int64 deviceIdLSB = 2; + string credentialsType = 3; + string credentialsId = 4; + string credentialsValue = 5; } message AssetUpdateMsg { UpdateMsgType msgType = 1; int64 idMSB = 2; int64 idLSB = 3; - string name = 4; - string type = 5; - string label = 6; + int64 customerIdMSB = 4; + int64 customerIdLSB = 5; + string name = 6; + string type = 7; + string label = 8; + string additionalInfo = 9; } message EntityViewUpdateMsg { UpdateMsgType msgType = 1; int64 idMSB = 2; int64 idLSB = 3; - string name = 4; - string type = 5; - int64 entityIdMSB = 6; - int64 entityIdLSB = 7; - EdgeEntityType entityType = 8; + int64 customerIdMSB = 4; + int64 customerIdLSB = 5; + string name = 6; + string type = 7; + int64 entityIdMSB = 8; + int64 entityIdLSB = 9; + EdgeEntityType entityType = 10; + string additionalInfo = 11; } message AlarmUpdateMsg { @@ -243,13 +264,47 @@ message UserUpdateMsg { UpdateMsgType msgType = 1; int64 idMSB = 2; int64 idLSB = 3; - string email = 4; - string authority = 5; - string firstName = 6; - string lastName = 7; - string additionalInfo = 8; - bool enabled = 9; - string password = 10; + int64 customerIdMSB = 4; + int64 customerIdLSB = 5; + string email = 6; + string authority = 7; + string firstName = 8; + string lastName = 9; + string additionalInfo = 10; +} + +message WidgetsBundleUpdateMsg { + UpdateMsgType msgType = 1; + int64 idMSB = 2; + int64 idLSB = 3; + string title = 4; + string alias = 5; + bytes image = 6; + bool isSystem = 7; +} + +message WidgetTypeUpdateMsg { + UpdateMsgType msgType = 1; + int64 idMSB = 2; + int64 idLSB = 3; + string bundleAlias = 4; + string alias = 5; + string name = 6; + string descriptorJson = 7; + bool isSystem = 8; +} + +message AdminSettingsUpdateMsg { + bool isSystem = 1; + string key = 2; + string jsonValue = 3; +} + +message UserCredentialsUpdateMsg { + int64 userIdMSB = 1; + int64 userIdLSB = 2; + bool enabled = 3; + string password = 4; } message RuleChainMetadataRequestMsg { @@ -257,6 +312,50 @@ message RuleChainMetadataRequestMsg { int64 ruleChainIdLSB = 2; } +message AttributesRequestMsg { + int64 entityIdMSB = 1; + int64 entityIdLSB = 2; + string entityType = 3; +} + +message RelationRequestMsg { + int64 entityIdMSB = 1; + int64 entityIdLSB = 2; + string entityType = 3; +} + +message UserCredentialsRequestMsg { + int64 userIdMSB = 1; + int64 userIdLSB = 2; +} + +message DeviceCredentialsRequestMsg { + int64 deviceIdMSB = 1; + int64 deviceIdLSB = 2; +} + +message DeviceRpcCallMsg { + int64 deviceIdMSB = 1; + int64 deviceIdLSB = 2; + int64 requestIdMSB = 3; + int64 requestIdLSB = 4; + int64 expirationTime = 5; + bool oneway = 6; + string originServiceId = 7; + RpcRequestMsg requestMsg = 8; + RpcResponseMsg responseMsg = 9; +} + +message RpcRequestMsg { + string method = 1; + string params = 2; +} + +message RpcResponseMsg { + string response = 1; + string error = 2; +} + enum EdgeEntityType { DEVICE = 0; ASSET = 1; @@ -270,8 +369,15 @@ message UplinkMsg { int32 uplinkMsgId = 1; repeated EntityDataProto entityData = 2; repeated DeviceUpdateMsg deviceUpdateMsg = 3; - repeated AlarmUpdateMsg alarmUpdateMsg = 4; - repeated RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg = 5; + repeated DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg = 4; + repeated AlarmUpdateMsg alarmUpdateMsg = 5; + repeated RelationUpdateMsg relationUpdateMsg = 6; + repeated RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg = 7; + repeated AttributesRequestMsg attributesRequestMsg = 8; + repeated RelationRequestMsg relationRequestMsg = 9; + repeated UserCredentialsRequestMsg userCredentialsRequestMsg = 10; + repeated DeviceCredentialsRequestMsg deviceCredentialsRequestMsg = 11; + repeated DeviceRpcCallMsg deviceRpcCallMsg = 12; } message UplinkResponseMsg { @@ -279,8 +385,30 @@ message UplinkResponseMsg { string errorMsg = 2; } +message DownlinkResponseMsg { + bool success = 1; + string errorMsg = 2; +} + message DownlinkMsg { int32 downlinkMsgId = 1; repeated EntityDataProto entityData = 2; + repeated DeviceCredentialsRequestMsg deviceCredentialsRequestMsg = 3; + repeated DeviceUpdateMsg deviceUpdateMsg = 4; + repeated DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg = 5; + repeated RuleChainUpdateMsg ruleChainUpdateMsg = 6; + repeated RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = 7; + repeated DashboardUpdateMsg dashboardUpdateMsg = 8; + repeated AssetUpdateMsg assetUpdateMsg = 9; + repeated EntityViewUpdateMsg entityViewUpdateMsg = 10; + repeated AlarmUpdateMsg alarmUpdateMsg = 11; + repeated UserUpdateMsg userUpdateMsg = 12; + repeated UserCredentialsUpdateMsg userCredentialsUpdateMsg = 13; + repeated CustomerUpdateMsg customerUpdateMsg = 14; + repeated RelationUpdateMsg relationUpdateMsg = 15; + repeated WidgetsBundleUpdateMsg widgetsBundleUpdateMsg = 16; + repeated WidgetTypeUpdateMsg widgetTypeUpdateMsg = 17; + repeated AdminSettingsUpdateMsg adminSettingsUpdateMsg = 18; + repeated DeviceRpcCallMsg deviceRpcCallMsg = 19; } diff --git a/common/queue/src/main/proto/queue.proto b/common/queue/src/main/proto/queue.proto index 6f4746e874..9a4b422007 100644 --- a/common/queue/src/main/proto/queue.proto +++ b/common/queue/src/main/proto/queue.proto @@ -434,12 +434,12 @@ message EdgeNotificationMsgProto { int64 tenantIdLSB = 2; int64 edgeIdMSB = 3; int64 edgeIdLSB = 4; - string edgeEventType = 5; - string edgeEventAction = 6; + string type = 5; + string action = 6; int64 entityIdMSB = 7; int64 entityIdLSB = 8; string entityType = 9; - string entityBody = 10; + string body = 10; PostTelemetryMsg postTelemetryMsg = 11; PostAttributeMsg postAttributesMsg = 12; } diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java index 428dfb0912..3b5d79d994 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java @@ -67,12 +67,16 @@ public class JsonConverter { private static int maxStringValueLength = 0; - public static PostTelemetryMsg convertToTelemetryProto(JsonElement jsonElement) throws JsonSyntaxException { + public static PostTelemetryMsg convertToTelemetryProto(JsonElement jsonElement, long ts) throws JsonSyntaxException { PostTelemetryMsg.Builder builder = PostTelemetryMsg.newBuilder(); - convertToTelemetry(jsonElement, System.currentTimeMillis(), null, builder); + convertToTelemetry(jsonElement, ts, null, builder); return builder.build(); } + public static PostTelemetryMsg convertToTelemetryProto(JsonElement jsonElement) throws JsonSyntaxException { + return convertToTelemetryProto(jsonElement, System.currentTimeMillis()); + } + private static void convertToTelemetry(JsonElement jsonElement, long systemTs, Map> result, PostTelemetryMsg.Builder builder) { if (jsonElement.isJsonObject()) { parseObject(systemTs, result, builder, jsonElement.getAsJsonObject()); 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 6df4697fdd..8068f59866 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 @@ -30,7 +30,6 @@ 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; @@ -51,9 +50,7 @@ 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; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; @@ -92,12 +89,6 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ @Autowired private CustomerDao customerDao; - @Autowired - private EntityViewService entityViewService; - - @Autowired - private EdgeService edgeService; - @Autowired private CacheManager cacheManager; @@ -352,6 +343,9 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ if (edge == null) { throw new DataValidationException("Can't unassign asset from non-existent edge!"); } + + checkAssignedEntityViewsToEdge(tenantId, assetId, edgeId); + try { deleteRelation(tenantId, new EntityRelation(edgeId, assetId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE)); } catch (Exception e) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java index 065007a218..21d31c894d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java @@ -31,7 +31,6 @@ import org.thingsboard.server.common.data.page.PageLink; 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.edge.EdgeService; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.exception.DataValidationException; @@ -76,9 +75,6 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom @Autowired private DashboardService dashboardService; - @Autowired - private EdgeService edgeService; - @Override public Customer findCustomerById(TenantId tenantId, CustomerId customerId) { log.trace("Executing findCustomerById [{}]", customerId); 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 9907f24e1b..1abf6fe4e4 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 @@ -40,7 +40,6 @@ import org.thingsboard.server.dao.entity.AbstractEntityService; 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.TimePaginatedRemover; import org.thingsboard.server.dao.service.Validator; import org.thingsboard.server.dao.tenant.TenantDao; @@ -215,7 +214,7 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb if (edge == null) { throw new DataValidationException("Can't assign dashboard to non-existent edge!"); } - if (!edge.getTenantId().getId().equals(dashboard.getTenantId().getId())) { + if (!edge.getTenantId().equals(dashboard.getTenantId())) { throw new DataValidationException("Can't assign dashboard to edge from different tenant!"); } try { @@ -243,17 +242,6 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb return dashboard; } - @Override - public void unassignEdgeDashboards(TenantId tenantId, EdgeId edgeId) { - log.trace("Executing unassignEdgeDashboards, edgeId [{}]", edgeId); - Validator.validateId(edgeId, "Incorrect edgeId " + edgeId); - Edge edge = edgeDao.findById(tenantId, edgeId.getId()); - if (edge == null) { - throw new DataValidationException("Can't unassign dashboards from non-existent edge!"); - } - new EdgeDashboardsUnassigner(edge).removeEntities(tenantId, edge); - } - @Override public PageData findDashboardsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink) { log.trace("Executing findDashboardsByTenantIdAndEdgeId, tenantId [{}], edgeId [{}], pageLink [{}]", tenantId, edgeId, pageLink); @@ -335,28 +323,4 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb } - private class EdgeDashboardsUnassigner extends TimePaginatedRemover { - - private Edge edge; - - EdgeDashboardsUnassigner(Edge edge) { - this.edge = edge; - } - - @Override - protected PageData findEntities(TenantId tenantId, Edge edge, TimePageLink pageLink) { - try { - return dashboardInfoDao.findDashboardsByTenantIdAndEdgeId(edge.getTenantId().getId(), edge.getId().getId(), pageLink); - } catch (Exception e) { - log.error("[{}] Can't find dashboards by tenant id and edge id. Edge Id {}", edge.getId(), e); - throw new RuntimeException("[{}] Can't find dashboards by tenant id and edge id. Edge Id '" + edge.getId() + "'", e); - } - } - - @Override - protected void removeEntity(TenantId tenantId, DashboardInfo entity) { - unassignDashboardFromEdge(edge.getTenantId(), new DashboardId(entity.getUuidId()), this.edge.getId()); - } - } - } 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 ff43185386..068a812b75 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 @@ -22,6 +22,7 @@ import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.dao.Dao; import java.util.List; @@ -224,5 +225,5 @@ public interface DeviceDao extends Dao { * @param pageLink the page link * @return the list of device objects */ - PageData findDevicesByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, PageLink pageLink); + PageData 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 92a0659548..eeefb6d3b9 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 @@ -61,9 +61,7 @@ import org.thingsboard.server.common.data.relation.RelationTypeGroup; 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.event.EventService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; @@ -113,12 +111,6 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe @Autowired private DeviceProfileService deviceProfileService; - @Autowired - private EntityViewService entityViewService; - - @Autowired - private EdgeService edgeService; - @Autowired private CacheManager cacheManager; @@ -502,6 +494,9 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe if (edge == null) { throw new DataValidationException("Can't unassign device from non-existent edge!"); } + + checkAssignedEntityViewsToEdge(tenantId, deviceId, edgeId); + try { deleteRelation(tenantId, new EntityRelation(edgeId, deviceId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE)); } catch (Exception e) { @@ -512,7 +507,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe } @Override - public PageData findDevicesByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink) { + public PageData 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); 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 eeeb3a3fe9..8c1382c500 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 @@ -20,9 +20,7 @@ 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.PageData; @@ -35,28 +33,7 @@ import org.thingsboard.server.dao.service.DataValidator; 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; - case ALARM: - return EdgeEventType.ALARM; - default: - log.warn("Failed to push notification to edge service. Unsupported entity type [{}]", entityType); - return null; - } - } + private EdgeEventDao edgeEventDao; @Override public ListenableFuture saveAsync(EdgeEvent edgeEvent) { @@ -65,8 +42,8 @@ public class BaseEdgeEventService implements EdgeEventService { } @Override - public PageData findEdgeEvents(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink) { - return edgeEventDao.findEdgeEvents(tenantId.getId(), edgeId, pageLink); + public PageData findEdgeEvents(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink, boolean withTsUpdate) { + return edgeEventDao.findEdgeEvents(tenantId.getId(), edgeId, pageLink, withTsUpdate); } private DataValidator edgeEventValidator = @@ -76,7 +53,7 @@ public class BaseEdgeEventService implements EdgeEventService { if (edgeEvent.getEdgeId() == null) { throw new DataValidationException("Edge id should be specified!"); } - if (StringUtils.isEmpty(edgeEvent.getEdgeEventAction())) { + if (StringUtils.isEmpty(edgeEvent.getAction())) { 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 index 002fd2d8ca..96fcfe1f69 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeEventDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeEventDao.java @@ -46,6 +46,6 @@ public interface EdgeEventDao extends Dao { * @param pageLink the pageLink * @return the event list */ - PageData findEdgeEvents(UUID tenantId, EdgeId edgeId, TimePageLink pageLink); + PageData findEdgeEvents(UUID tenantId, EdgeId edgeId, TimePageLink pageLink, boolean withTsUpdate); } 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 d63e5295b2..9ab97a4cf3 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 @@ -20,17 +20,26 @@ 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.http.HttpHost; +import org.apache.http.conn.ssl.DefaultHostnameVerifier; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; +import org.springframework.web.client.RestTemplate; import org.thingsboard.server.common.data.Customer; 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.User; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeInfo; import org.thingsboard.server.common.data.edge.EdgeSearchQuery; @@ -38,15 +47,17 @@ 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.EntityId; +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.id.UserId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; 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.dao.customer.CustomerDao; -import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.relation.RelationService; @@ -55,12 +66,18 @@ 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 org.thingsboard.server.dao.user.UserService; import javax.annotation.Nullable; +import javax.annotation.PostConstruct; +import java.net.InetSocketAddress; +import java.net.Proxy; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; @@ -77,10 +94,13 @@ import static org.thingsboard.server.dao.service.Validator.validateString; public class EdgeServiceImpl extends AbstractEntityService implements EdgeService { 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_EDGE_ID = "Incorrect edgeId "; + private RestTemplate restTemplate; + + private static final String EDGE_LICENSE_SERVER_ENDPOINT = "https://license.thingsboard.io"; + @Autowired private EdgeDao edgeDao; @@ -91,10 +111,10 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic private CustomerDao customerDao; @Autowired - private CacheManager cacheManager; + private UserService userService; @Autowired - private DashboardService dashboardService; + private CacheManager cacheManager; @Autowired private RuleChainService ruleChainService; @@ -102,6 +122,16 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic @Autowired private RelationService relationService; + @Value("${edges.rpc.enabled:false}") + private boolean edgesRpcEnabled; + + @PostConstruct + public void init() { + if (edgesRpcEnabled) { + initRestTemplate(); + } + } + @Override public Edge findEdgeById(TenantId tenantId, EdgeId edgeId) { log.trace("Executing findEdgeById [{}]", edgeId); @@ -161,10 +191,6 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic Edge edge = edgeDao.findById(tenantId, edgeId.getId()); - dashboardService.unassignEdgeDashboards(tenantId, edgeId); - // TODO: validate that rule chains are removed by deleteEntityRelations(tenantId, edgeId); call - ruleChainService.unassignEdgeRuleChains(tenantId, edgeId); - List list = new ArrayList<>(); list.add(edge.getTenantId()); list.add(edge.getName()); @@ -355,6 +381,12 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic if (StringUtils.isEmpty(edge.getRoutingKey())) { throw new DataValidationException("Edge routing key should be specified!"); } + if (StringUtils.isEmpty(edge.getEdgeLicenseKey())) { + throw new DataValidationException("Edge license key should be specified!"); + } + if (StringUtils.isEmpty(edge.getCloudEndpoint())) { + throw new DataValidationException("Cloud endpoint should be specified!"); + } if (edge.getTenantId() == null) { throw new DataValidationException("Edge should be assigned to tenant!"); } else { @@ -404,4 +436,107 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic } }; + @Override + public ListenableFuture> findRelatedEdgeIdsByEntityId(TenantId tenantId, EntityId entityId) { + if (EntityType.TENANT.equals(entityId.getEntityType())) { + PageData edgesByTenantId = findEdgesByTenantId(tenantId, new PageLink(Integer.MAX_VALUE)); + return Futures.immediateFuture(edgesByTenantId.getData().stream().map(IdBased::getId).collect(Collectors.toList())); + } else { + 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 && + originatorEdgeRelations.get(0).getFrom() != null) { + return Collections.singletonList(new EdgeId(originatorEdgeRelations.get(0).getFrom().getId())); + } else { + return Collections.emptyList(); + } + }, MoreExecutors.directExecutor()); + case DASHBOARD: + return convertToEdgeIds(findEdgesByTenantIdAndDashboardId(tenantId, new DashboardId(entityId.getId()))); + case RULE_CHAIN: + return convertToEdgeIds(findEdgesByTenantIdAndRuleChainId(tenantId, new RuleChainId(entityId.getId()))); + case USER: + User userById = userService.findUserById(tenantId, new UserId(entityId.getId())); + if (userById == null) { + return Futures.immediateFuture(Collections.emptyList()); + } + PageData edges; + if (userById.getCustomerId() == null || userById.getCustomerId().isNullUid()) { + edges = findEdgesByTenantId(tenantId, new PageLink(Integer.MAX_VALUE)); + } else { + edges = findEdgesByTenantIdAndCustomerId(tenantId, new CustomerId(entityId.getId()), new PageLink(Integer.MAX_VALUE)); + } + return convertToEdgeIds(Futures.immediateFuture(edges.getData())); + default: + return Futures.immediateFuture(Collections.emptyList()); + } + } + } + + private ListenableFuture> convertToEdgeIds(ListenableFuture> future) { + return Futures.transform(future, edges -> { + if (edges != null && !edges.isEmpty()) { + return edges.stream().map(IdBased::getId).collect(Collectors.toList()); + } else { + return Collections.emptyList(); + } + }, MoreExecutors.directExecutor()); + } + + @Override + public Object checkInstance(Object request) { + return this.restTemplate.postForEntity(EDGE_LICENSE_SERVER_ENDPOINT + "/api/license/checkInstance", request, Object.class, new Object[0]); + } + + @Override + public Object activateInstance(String edgeLicenseSecret, String releaseDate) { + Map params = new HashMap(); + params.put("licenseSecret", edgeLicenseSecret); + params.put("releaseDate", releaseDate); + return this.restTemplate.postForEntity(EDGE_LICENSE_SERVER_ENDPOINT + "/api/license/activateInstance?licenseSecret={licenseSecret}&releaseDate={releaseDate}", (Object) null, Object.class, params); + } + + private void initRestTemplate() { + boolean jdkHttpClientEnabled = org.apache.commons.lang3.StringUtils.isNotEmpty(System.getProperty("tb.proxy.jdk")) && System.getProperty("tb.proxy.jdk").equalsIgnoreCase("true"); + boolean systemProxyEnabled = org.apache.commons.lang3.StringUtils.isNotEmpty(System.getProperty("tb.proxy.system")) && System.getProperty("tb.proxy.system").equalsIgnoreCase("true"); + boolean proxyEnabled = org.apache.commons.lang3.StringUtils.isNotEmpty(System.getProperty("tb.proxy.host")) && org.apache.commons.lang3.StringUtils.isNotEmpty(System.getProperty("tb.proxy.port")); + if (jdkHttpClientEnabled) { + log.warn("Going to use plain JDK Http Client!"); + SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); + if (proxyEnabled) { + log.warn("Going to use Proxy Server: [{}:{}]", System.getProperty("tb.proxy.host"), System.getProperty("tb.proxy.port")); + factory.setProxy(new Proxy(Proxy.Type.HTTP, InetSocketAddress.createUnresolved(System.getProperty("tb.proxy.host"), Integer.parseInt(System.getProperty("tb.proxy.port"))))); + } + + this.restTemplate = new RestTemplate(new SimpleClientHttpRequestFactory()); + } else { + CloseableHttpClient httpClient; + HttpComponentsClientHttpRequestFactory requestFactory; + if (systemProxyEnabled) { + log.warn("Going to use System Proxy Server!"); + httpClient = HttpClients.createSystem(); + requestFactory = new HttpComponentsClientHttpRequestFactory(); + requestFactory.setHttpClient(httpClient); + this.restTemplate = new RestTemplate(requestFactory); + } else if (proxyEnabled) { + log.warn("Going to use Proxy Server: [{}:{}]", System.getProperty("tb.proxy.host"), System.getProperty("tb.proxy.port")); + httpClient = HttpClients.custom().setSSLHostnameVerifier(new DefaultHostnameVerifier()).setProxy(new HttpHost(System.getProperty("tb.proxy.host"), Integer.parseInt(System.getProperty("tb.proxy.port")), "https")).build(); + requestFactory = new HttpComponentsClientHttpRequestFactory(); + requestFactory.setHttpClient(httpClient); + this.restTemplate = new RestTemplate(requestFactory); + } else { + httpClient = HttpClients.custom().setSSLHostnameVerifier(new DefaultHostnameVerifier()).build(); + requestFactory = new HttpComponentsClientHttpRequestFactory(); + requestFactory.setHttpClient(httpClient); + this.restTemplate = new RestTemplate(requestFactory); + } + } + + } + } 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 149077e4f4..35430b6768 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 @@ -18,11 +18,18 @@ package org.thingsboard.server.dao.entity; import lombok.extern.slf4j.Slf4j; import org.hibernate.exception.ConstraintViolationException; import org.springframework.beans.factory.annotation.Autowired; +import org.thingsboard.server.common.data.EntityView; +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.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; +import org.thingsboard.server.dao.edge.EdgeService; +import org.thingsboard.server.dao.entityview.EntityViewService; +import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.relation.RelationService; +import java.util.List; import java.util.Optional; @Slf4j @@ -34,6 +41,12 @@ public abstract class AbstractEntityService { @Autowired protected RelationService relationService; + @Autowired + protected EntityViewService entityViewService; + + @Autowired(required = false) + protected EdgeService edgeService; + protected void createRelation(TenantId tenantId, EntityRelation relation) { log.debug("Creating relation: {}", relation); relationService.saveRelation(tenantId, relation); @@ -59,4 +72,21 @@ public abstract class AbstractEntityService { } } + protected void checkAssignedEntityViewsToEdge(TenantId tenantId, EntityId entityId, EdgeId edgeId) { + try { + List entityViews = entityViewService.findEntityViewsByTenantIdAndEntityIdAsync(tenantId, entityId).get(); + if (entityViews != null && !entityViews.isEmpty()) { + EntityView entityView = entityViews.get(0); + Boolean relationExists = relationService.checkRelation(tenantId,edgeId, entityView.getId(), + EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE).get(); + if (relationExists) { + throw new DataValidationException("Can't unassign device/asset from edge that is related to entity view and entity view is assigned to edge!"); + } + } + } catch (Exception e) { + log.error("Exception while finding entity views for entityId [{}]", entityId, e); + throw new RuntimeException("Exception while finding entity views for entityId [" + entityId + "]", e); + } + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java b/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java index 087d0d5506..dfaa263f48 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java @@ -44,7 +44,6 @@ 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 org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.rule.RuleChainService; @@ -93,9 +92,6 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe @Autowired private EntityQueryDao entityQueryDao; - @Autowired - private EdgeService edgeService; - @Override public void deleteEntityRelations(TenantId tenantId, EntityId entityId) { super.deleteEntityRelations(tenantId, entityId); 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 75b8911b39..bead2c34b5 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 @@ -35,12 +35,6 @@ import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.EntityViewInfo; import org.thingsboard.server.common.data.Tenant; -import org.thingsboard.server.common.data.Customer; -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.EntityViewInfo; -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; @@ -55,7 +49,6 @@ 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; @@ -100,9 +93,6 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti @Autowired private CustomerDao customerDao; - @Autowired - private EdgeService edgeService; - @Autowired private CacheManager cacheManager; @@ -354,6 +344,18 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti if (!edge.getTenantId().getId().equals(entityView.getTenantId().getId())) { throw new DataValidationException("Can't assign entityView to edge from different tenant!"); } + + try { + Boolean relationExists = relationService.checkRelation(tenantId, edgeId, entityView.getEntityId(), + EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE).get(); + if (!relationExists) { + throw new DataValidationException("Can't assign entity view to edge because related device/asset doesn't assigned to edge!"); + } + } catch (ExecutionException | InterruptedException e) { + log.error("Exception during relation check", e); + throw new RuntimeException("Exception during relation check", e); + } + try { createRelation(tenantId, new EntityRelation(edgeId, entityViewId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE)); } catch (Exception e) { @@ -380,7 +382,7 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti } @Override - public PageData findEntityViewsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink) { + public PageData 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); 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 40168a9eba..1ac1170e81 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 @@ -403,9 +403,17 @@ public class ModelConstants { public static final String EDGE_TYPE_PROPERTY = "type"; public static final String EDGE_CONFIGURATION_PROPERTY = "configuration"; public static final String EDGE_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY; + public static final String EDGE_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "edge_by_tenant_and_search_text"; + public static final String EDGE_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "edge_by_tenant_by_type_and_search_text"; + public static final String EDGE_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "edge_by_customer_and_search_text"; + public static final String EDGE_BY_CUSTOMER_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "edge_by_customer_by_type_and_search_text"; + public static final String EDGE_BY_TENANT_AND_NAME_VIEW_NAME = "edge_by_tenant_and_name"; + public static final String EDGE_BY_ROUTING_KEY_VIEW_NAME = "edge_by_routing_key"; public static final String EDGE_ROUTING_KEY_PROPERTY = "routing_key"; public static final String EDGE_SECRET_PROPERTY = "secret"; + public static final String EDGE_LICENSE_KEY_PROPERTY = "edge_license_key"; + public static final String EDGE_CLOUD_ENDPOINT_KEY_PROPERTY = "cloud_endpoint"; /** * Cassandra edge queue constants. @@ -415,8 +423,11 @@ public class ModelConstants { 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_UID_PROPERTY = "edge_event_uid"; public static final String EDGE_EVENT_ENTITY_ID_PROPERTY = "entity_id"; - public static final String EDGE_EVENT_ENTITY_BODY_PROPERTY = "entity_body"; + public static final String EDGE_EVENT_BODY_PROPERTY = "body"; + + public static final String EDGE_EVENT_BY_ID_VIEW_NAME = "edge_event_by_id"; /** * Cassandra attributes and timeseries constants. diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractEdgeEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractEdgeEntity.java index 70f9219a76..862f803433 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractEdgeEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractEdgeEntity.java @@ -35,7 +35,17 @@ import org.thingsboard.server.dao.util.mapping.JsonStringType; import javax.persistence.Column; import javax.persistence.MappedSuperclass; -import static org.thingsboard.server.dao.model.ModelConstants.*; +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_CLOUD_ENDPOINT_KEY_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_CUSTOMER_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_LABEL_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_LICENSE_KEY_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_NAME_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_ROOT_RULE_CHAIN_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_ROUTING_KEY_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_SECRET_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_TENANT_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_TYPE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; @Data @EqualsAndHashCode(callSuper = true) @@ -70,6 +80,12 @@ public abstract class AbstractEdgeEntity extends BaseSqlEntity extends BaseSqlEntity extends BaseSqlEntity extends BaseSqlEntity implements BaseEnt private String edgeEventAction; @Type(type = "json") - @Column(name = EDGE_EVENT_ENTITY_BODY_PROPERTY) + @Column(name = EDGE_EVENT_BODY_PROPERTY) private JsonNode entityBody; + @Column(name = EDGE_EVENT_UID_PROPERTY) + private String edgeEventUid; + @Column(name = TS_COLUMN) private long ts; @@ -95,9 +100,10 @@ public class EdgeEventEntity extends BaseSqlEntity implements BaseEnt if (edgeEvent.getEntityId() != null) { this.entityId = edgeEvent.getEntityId(); } - this.edgeEventType = edgeEvent.getEdgeEventType(); - this.edgeEventAction = edgeEvent.getEdgeEventAction(); - this.entityBody = edgeEvent.getEntityBody(); + this.edgeEventType = edgeEvent.getType(); + this.edgeEventAction = edgeEvent.getAction(); + this.entityBody = edgeEvent.getBody(); + this.edgeEventUid = edgeEvent.getUid(); } @Override @@ -109,9 +115,10 @@ public class EdgeEventEntity extends BaseSqlEntity implements BaseEnt if (entityId != null) { edgeEvent.setEntityId(entityId); } - edgeEvent.setEdgeEventType(edgeEventType); - edgeEvent.setEdgeEventAction(edgeEventAction); - edgeEvent.setEntityBody(entityBody); + edgeEvent.setType(edgeEventType); + edgeEvent.setAction(edgeEventAction); + edgeEvent.setBody(entityBody); + edgeEvent.setUid(edgeEventUid); return edgeEvent; } 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 4ae017b9c7..75c29290ed 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 @@ -48,12 +48,10 @@ import org.thingsboard.server.common.data.rule.RuleChainImportResult; 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.EdgeService; import org.thingsboard.server.dao.entity.AbstractEntityService; 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.TimePaginatedRemover; import org.thingsboard.server.dao.service.Validator; import org.thingsboard.server.dao.tenant.TenantDao; @@ -89,9 +87,6 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC @Autowired private TenantDao tenantDao; - @Autowired - private EdgeService edgeService; - @Override public RuleChain saveRuleChain(RuleChain ruleChain) { ruleChainValidator.validate(ruleChain, RuleChain::getTenantId); @@ -552,7 +547,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC if (edge == null) { throw new DataValidationException("Can't assign ruleChain to non-existent edge!"); } - if (!edge.getTenantId().getId().equals(ruleChain.getTenantId().getId())) { + if (!edge.getTenantId().equals(ruleChain.getTenantId())) { throw new DataValidationException("Can't assign ruleChain to edge from different tenant!"); } try { @@ -584,18 +579,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC } @Override - public void unassignEdgeRuleChains(TenantId tenantId, EdgeId edgeId) { - log.trace("Executing unassignEdgeRuleChains, edgeId [{}]", edgeId); - Validator.validateId(edgeId, "Incorrect edgeId " + edgeId); - Edge edge = edgeService.findEdgeById(tenantId, edgeId); - if (edge == null) { - throw new DataValidationException("Can't unassign ruleChains from non-existent edge!"); - } - new EdgeRuleChainsUnassigner(edge).removeEntities(tenantId, edge); - } - - @Override - public PageData findRuleChainsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink) { + public PageData 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 edgeId " + edgeId); @@ -742,29 +726,4 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC checkRuleNodesAndDelete(tenantId, entity.getId()); } }; - - private class EdgeRuleChainsUnassigner extends TimePaginatedRemover { - - private Edge edge; - - EdgeRuleChainsUnassigner(Edge edge) { - this.edge = edge; - } - - @Override - protected PageData findEntities(TenantId tenantId, Edge edge, TimePageLink pageLink) { - try { - return ruleChainDao.findRuleChainsByTenantIdAndEdgeId(edge.getTenantId().getId(), edge.getId().getId(), pageLink); - } catch (Exception e) { - log.error("[{}] Can't find rule chains by tenant id and edge id. Edge Id {}", edge.getId(), e); - throw new RuntimeException("[{}] Can't find rule chains by tenant id and edge id. Edge Id '" + edge.getId() + "'", e); - } - } - - @Override - protected void removeEntity(TenantId tenantId, RuleChain entity) { - unassignRuleChainFromEdge(edge.getTenantId(), new RuleChainId(entity.getUuidId()), this.edge.getId(), true); - } - - } } 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 b4459bb3c3..7657ede430 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 @@ -28,6 +28,7 @@ import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.device.DeviceDao; import org.thingsboard.server.dao.model.sql.DeviceEntity; @@ -233,7 +234,7 @@ public class JpaDeviceDao extends JpaAbstractSearchTextDao } @Override - public PageData findDevicesByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, PageLink pageLink) { + public PageData findDevicesByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink) { log.debug("Try to find devices by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink); return DaoUtil.toPageData(deviceRepository .findByTenantIdAndEdgeId( 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 index fc12b990d2..37d84c4e05 100644 --- 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 @@ -65,7 +65,7 @@ public class JpaBaseEdgeEventDao extends JpaAbstractSearchTextDao findEdgeEvents(UUID tenantId, EdgeId edgeId, TimePageLink pageLink) { + public PageData findEdgeEvents(UUID tenantId, EdgeId edgeId, TimePageLink pageLink, boolean withTsUpdate) { return DaoUtil.toPageData( edgeEventRepository .findEdgeEventsByTenantIdAndEdgeId( @@ -74,7 +74,12 @@ public class JpaBaseEdgeEventDao extends JpaAbstractSearchTextDao timeSearchSpec = JpaAbstractSearchTimeDao.getTimeSearchPageSpec(pageLink, "id"); +// Specification fieldsSpec = getEntityFieldsSpec(tenantId, edgeId, withTsUpdate); +// 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) { @@ -88,4 +93,24 @@ public class JpaBaseEdgeEventDao extends JpaAbstractSearchTextDao getEntityFieldsSpec(UUID tenantId, EdgeId edgeId, boolean withTsUpdate) { +// 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); +// } +// if (!withTsUpdate) { +// Predicate edgeEventActionPredicate = criteriaBuilder.notEqual(root.get("edgeEventAction"), ActionType.TIMESERIES_UPDATED.name()); +// predicates.add(edgeEventActionPredicate); +// } +// return criteriaBuilder.and(predicates.toArray(new Predicate[]{})); +// }; +// } } 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 8c311247b7..7c5aaa20bb 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 @@ -158,19 +158,17 @@ public class JpaEdgeDao extends JpaAbstractSearchTextDao imple 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()); - for (EntityRelation relation : input) { - edgeFutures.add(findByIdAsync(new TenantId(tenantId), relation.getFrom().getId())); - } - return Futures.successfulAsList(edgeFutures); - }, MoreExecutors.directExecutor()); + return transformFromRelationToEdge(tenantId, relations); } @Override 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 transformFromRelationToEdge(tenantId, relations); + } + + private ListenableFuture> transformFromRelationToEdge(UUID tenantId, ListenableFuture> relations) { return Futures.transformAsync(relations, input -> { List> edgeFutures = new ArrayList<>(input.size()); for (EntityRelation relation : input) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java index 09139a2e2f..bbfc22585e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java @@ -32,7 +32,6 @@ import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.dao.device.DeviceService; -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; @@ -85,9 +84,6 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe @Autowired private RuleChainService ruleChainService; - @Autowired - private EdgeService edgeService; - @Override public Tenant findTenantById(TenantId tenantId) { log.trace("Executing findTenantById [{}]", tenantId); 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 7e3474d0e8..314684c3fe 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 @@ -37,7 +37,6 @@ import org.thingsboard.server.common.data.page.PageLink; 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; @@ -85,9 +84,6 @@ 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); diff --git a/dao/src/main/resources/sql/schema-entities-hsql.sql b/dao/src/main/resources/sql/schema-entities-hsql.sql index a4861da1ed..3c86fd2914 100644 --- a/dao/src/main/resources/sql/schema-entities-hsql.sql +++ b/dao/src/main/resources/sql/schema-entities-hsql.sql @@ -343,6 +343,8 @@ CREATE TABLE IF NOT EXISTS edge ( label varchar(255), routing_key varchar(255), secret varchar(255), + edge_license_key varchar(30), + cloud_endpoint varchar(255), search_text varchar(255), tenant_id uuid, CONSTRAINT edge_name_unq_key UNIQUE (tenant_id, name), @@ -353,9 +355,10 @@ CREATE TABLE IF NOT EXISTS edge_event ( id uuid NOT NULL CONSTRAINT edge_event_pkey PRIMARY KEY, edge_id uuid, edge_event_type varchar(255), + edge_event_uid varchar(255), entity_id uuid, edge_event_action varchar(255), - entity_body varchar(10000000), + body varchar(10000000), tenant_id uuid, 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 d89ad346aa..dae5abf8cd 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -371,6 +371,8 @@ CREATE TABLE IF NOT EXISTS edge ( label varchar(255), routing_key varchar(255), secret varchar(255), + edge_license_key varchar(30), + cloud_endpoint varchar(255), search_text varchar(255), tenant_id uuid, CONSTRAINT edge_name_unq_key UNIQUE (tenant_id, name), @@ -381,9 +383,10 @@ CREATE TABLE IF NOT EXISTS edge_event ( id uuid NOT NULL CONSTRAINT edge_event_pkey PRIMARY KEY, edge_id uuid, edge_event_type varchar(255), + edge_event_uid varchar(255), entity_id uuid, edge_event_action varchar(255), - entity_body varchar(10000000), + body varchar(10000000), tenant_id uuid, ts bigint NOT NULL ); @@ -421,3 +424,20 @@ BEGIN END; $$ LANGUAGE plpgsql; + +CREATE OR REPLACE PROCEDURE cleanup_edge_events_by_ttl(IN ttl bigint, INOUT deleted bigint) + LANGUAGE plpgsql AS +$$ +DECLARE + ttl_ts bigint; + ttl_deleted_count bigint DEFAULT 0; +BEGIN + IF ttl > 0 THEN + ttl_ts := (EXTRACT(EPOCH FROM current_timestamp) * 1000 - ttl::bigint * 1000)::bigint; + EXECUTE format( + 'WITH deleted AS (DELETE FROM edge_event WHERE ts < %L::bigint RETURNING *) SELECT count(*) FROM deleted', ttl_ts) into ttl_deleted_count; + END IF; + RAISE NOTICE 'Edge events removed by ttl: %', ttl_deleted_count; + deleted := ttl_deleted_count; +END +$$; diff --git a/dao/src/test/java/org/thingsboard/server/dao/NoSqlDaoServiceTestSuite.java b/dao/src/test/java/org/thingsboard/server/dao/NoSqlDaoServiceTestSuite.java index 9c2beee811..c8ff6b45e0 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/NoSqlDaoServiceTestSuite.java +++ b/dao/src/test/java/org/thingsboard/server/dao/NoSqlDaoServiceTestSuite.java @@ -25,8 +25,7 @@ import java.util.Arrays; @RunWith(ClasspathSuite.class) @ClassnameFilters({ - // TODO: voba - fix before final test on cassandra - "org.thingsboard.server.dao.service.*VOBA_FIX_BEFORE_FINAL_TESTServiceNoSqlTest" + "org.thingsboard.server.dao.service.nosql.*ServiceNoSqlTest" }) public class NoSqlDaoServiceTestSuite { 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 5b1d588f47..730d01f960 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 @@ -358,6 +358,8 @@ public abstract class BaseDashboardServiceTest extends AbstractServiceTest { edge.setType("default"); edge.setSecret(RandomStringUtils.randomAlphanumeric(20)); edge.setRoutingKey(RandomStringUtils.randomAlphanumeric(20)); + edge.setEdgeLicenseKey(RandomStringUtils.randomAlphanumeric(20)); + edge.setCloudEndpoint("http://localhost:8080"); edge = edgeService.saveEdge(edge); try { dashboardService.assignDashboardToEdge(tenantId, dashboard.getId(), edge.getId()); 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 index 7aec1cbd32..21477c24de 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseEdgeEventServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseEdgeEventServiceTest.java @@ -19,6 +19,7 @@ import com.datastax.oss.driver.api.core.uuid.Uuids; import org.junit.Assert; import org.junit.Test; import org.thingsboard.server.common.data.DataConstants; +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.DeviceId; @@ -46,9 +47,9 @@ public abstract class BaseEdgeEventServiceTest extends AbstractServiceTest { 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()); + Assert.assertEquals(saved.getType(), edgeEvent.getType()); + Assert.assertEquals(saved.getAction(), edgeEvent.getAction()); + Assert.assertEquals(saved.getBody(), edgeEvent.getBody()); } protected EdgeEvent generateEdgeEvent(TenantId tenantId, EdgeId edgeId, EntityId entityId, String edgeEventAction) throws IOException { @@ -59,9 +60,9 @@ public abstract class BaseEdgeEventServiceTest extends AbstractServiceTest { edgeEvent.setTenantId(tenantId); edgeEvent.setEdgeId(edgeId); edgeEvent.setEntityId(entityId.getId()); - edgeEvent.setEdgeEventType(EdgeEventType.DEVICE); - edgeEvent.setEdgeEventAction(edgeEventAction); - edgeEvent.setEntityBody(readFromResource("TestJsonData.json")); + edgeEvent.setType(EdgeEventType.DEVICE); + edgeEvent.setAction(edgeEventAction); + edgeEvent.setBody(readFromResource("TestJsonData.json")); return edgeEvent; } @@ -84,7 +85,7 @@ public abstract class BaseEdgeEventServiceTest extends AbstractServiceTest { saveEdgeEventWithProvidedTime(timeAfterEndTime, edgeId, deviceId, tenantId); TimePageLink pageLink = new TimePageLink(2, 0, "", new SortOrder("createdTime", SortOrder.Direction.DESC), startTime, endTime); - PageData edgeEvents = edgeEventService.findEdgeEvents(tenantId, edgeId, pageLink); + PageData edgeEvents = edgeEventService.findEdgeEvents(tenantId, edgeId, pageLink, true); Assert.assertNotNull(edgeEvents.getData()); Assert.assertTrue(edgeEvents.getData().size() == 2); @@ -93,7 +94,7 @@ public abstract class BaseEdgeEventServiceTest extends AbstractServiceTest { Assert.assertTrue(edgeEvents.hasNext()); Assert.assertNotNull(pageLink.nextPageLink()); - edgeEvents = edgeEventService.findEdgeEvents(tenantId, edgeId, pageLink.nextPageLink()); + edgeEvents = edgeEventService.findEdgeEvents(tenantId, edgeId, pageLink.nextPageLink(), true); Assert.assertNotNull(edgeEvents.getData()); Assert.assertTrue(edgeEvents.getData().size() == 1); @@ -101,6 +102,26 @@ public abstract class BaseEdgeEventServiceTest extends AbstractServiceTest { Assert.assertFalse(edgeEvents.hasNext()); } + @Test + public void findEdgeEventsWithTsUpdateAndWithout() throws Exception { + EdgeId edgeId = new EdgeId(Uuids.timeBased()); + DeviceId deviceId = new DeviceId(Uuids.timeBased()); + TenantId tenantId = new TenantId(Uuids.timeBased()); + TimePageLink pageLink = new TimePageLink(1); + + EdgeEvent edgeEventWithTsUpdate = generateEdgeEvent(tenantId, edgeId, deviceId, ActionType.TIMESERIES_UPDATED.name()); + edgeEventService.saveAsync(edgeEventWithTsUpdate).get(); + + PageData allEdgeEvents = edgeEventService.findEdgeEvents(tenantId, edgeId, pageLink, true); + PageData edgeEventsWithoutTsUpdate = edgeEventService.findEdgeEvents(tenantId, edgeId, pageLink, false); + + Assert.assertNotNull(allEdgeEvents.getData()); + Assert.assertNotNull(edgeEventsWithoutTsUpdate.getData()); + Assert.assertEquals(1, allEdgeEvents.getData().size()); + Assert.assertEquals(allEdgeEvents.getData().get(0).getUuidId(), edgeEventWithTsUpdate.getUuidId()); + Assert.assertTrue(edgeEventsWithoutTsUpdate.getData().isEmpty()); + } + 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))); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseEdgeServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseEdgeServiceTest.java index c49534a046..ae919c9511 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseEdgeServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseEdgeServiceTest.java @@ -590,6 +590,8 @@ public abstract class BaseEdgeServiceTest extends AbstractServiceTest { edge.setType(type); edge.setSecret(RandomStringUtils.randomAlphanumeric(20)); edge.setRoutingKey(RandomStringUtils.randomAlphanumeric(20)); + edge.setEdgeLicenseKey(RandomStringUtils.randomAlphanumeric(20)); + edge.setCloudEndpoint("http://localhost:8080"); return edge; } 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 071bad925c..d16c592be1 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 @@ -332,6 +332,21 @@ public abstract class BaseRuleChainServiceTest extends AbstractServiceTest { Assert.assertEquals(1, result.size()); } + @Test + public void setDefaultRootEdgeRuleChain() throws Exception { + RuleChainId ruleChainId1 = saveRuleChainAndSetDefaultEdge("Default Edge Rule Chain 1"); + RuleChainId ruleChainId2 = saveRuleChainAndSetDefaultEdge("Default Edge Rule Chain 2"); + + ruleChainService.setDefaultRootEdgeRuleChain(tenantId, ruleChainId1); + ruleChainService.setDefaultRootEdgeRuleChain(tenantId, ruleChainId2); + + RuleChain ruleChainById = ruleChainService.findRuleChainById(tenantId, ruleChainId1); + Assert.assertFalse(ruleChainById.isRoot()); + + ruleChainById = ruleChainService.findRuleChainById(tenantId, ruleChainId2); + Assert.assertTrue(ruleChainById.isRoot()); + } + private RuleChainId saveRuleChainAndSetDefaultEdge(String name) { RuleChain edgeRuleChain = new RuleChain(); edgeRuleChain.setTenantId(tenantId); diff --git a/dao/src/test/resources/application-test.properties b/dao/src/test/resources/application-test.properties index e4b1bd532a..2b038912ef 100644 --- a/dao/src/test/resources/application-test.properties +++ b/dao/src/test/resources/application-test.properties @@ -51,3 +51,5 @@ security.claim.duration=60000 database.ts_max_intervals=700 sql.remove_null_chars=true + +edges.rpc.enabled=true diff --git a/pom.xml b/pom.xml index 8d16bc204e..f0e4ff3ac1 100755 --- a/pom.xml +++ b/pom.xml @@ -729,6 +729,7 @@ src/browserslist **/*.raw **/apache/cassandra/io/** + .run/** JAVADOC_STYLE diff --git a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java index d2ba4d43b4..fac1a7f8df 100644 --- a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java +++ b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java @@ -54,12 +54,16 @@ import org.thingsboard.server.common.data.asset.AssetSearchQuery; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.audit.AuditLog; import org.thingsboard.server.common.data.device.DeviceSearchQuery; +import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.edge.EdgeSearchQuery; import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery; import org.thingsboard.server.common.data.id.AlarmId; 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; @@ -1998,6 +2002,401 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { } } + public Edge saveEdge(Edge edge) { + return restTemplate.postForEntity(baseURL + "/api/edge", edge, Edge.class).getBody(); + } + + public void deleteEdge(EdgeId edgeId) { + restTemplate.delete(baseURL + "/api/edge/{edgeId}", edgeId.getId()); + } + + public Optional getEdgeById(EdgeId edgeId) { + try { + ResponseEntity edge = restTemplate.getForEntity(baseURL + "/api/edge/{edgeId}", Edge.class, edgeId.getId()); + return Optional.ofNullable(edge.getBody()); + } catch (HttpClientErrorException exception) { + if (exception.getStatusCode() == HttpStatus.NOT_FOUND) { + return Optional.empty(); + } else { + throw exception; + } + } + } + + public Optional assignEdgeToCustomer(CustomerId customerId, EdgeId edgeId) { + try { + ResponseEntity edge = restTemplate.postForEntity(baseURL + "/api/customer/{customerId}/edge/{edgeId}", null, Edge.class, customerId.getId(), edgeId.getId()); + return Optional.ofNullable(edge.getBody()); + } catch (HttpClientErrorException exception) { + if (exception.getStatusCode() == HttpStatus.NOT_FOUND) { + return Optional.empty(); + } else { + throw exception; + } + } + } + + public Optional assignEdgeToPublicCustomer(EdgeId edgeId) { + try { + ResponseEntity edge = restTemplate.postForEntity(baseURL + "/api/customer/public/edge/{edgeId}", null, Edge.class, edgeId.getId()); + return Optional.ofNullable(edge.getBody()); + } catch (HttpClientErrorException exception) { + if (exception.getStatusCode() == HttpStatus.NOT_FOUND) { + return Optional.empty(); + } else { + throw exception; + } + } + } + + public Optional setRootRuleChain(EdgeId edgeId, RuleChainId ruleChainId) { + try { + ResponseEntity ruleChain = restTemplate.postForEntity(baseURL + "/api/edge/{edgeId}/{ruleChainId}/root", null, Edge.class, edgeId.getId(), ruleChainId.getId()); + return Optional.ofNullable(ruleChain.getBody()); + } catch (HttpClientErrorException exception) { + if (exception.getStatusCode() == HttpStatus.NOT_FOUND) { + return Optional.empty(); + } else { + throw exception; + } + } + } + + public PageData getEdges(PageLink pageLink) { + Map params = new HashMap<>(); + addPageLinkToParam(params, pageLink); + return restTemplate.exchange( + baseURL + "/api/edges?" + getUrlParams(pageLink), + HttpMethod.GET, HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }, params).getBody(); + } + + public Optional unassignEdgeFromCustomer(EdgeId edgeId) { + try { + ResponseEntity edge = restTemplate.exchange(baseURL + "/api/customer/edge/{edgeId}", HttpMethod.DELETE, HttpEntity.EMPTY, Edge.class, edgeId.getId()); + return Optional.ofNullable(edge.getBody()); + } catch (HttpClientErrorException exception) { + if (exception.getStatusCode() == HttpStatus.NOT_FOUND) { + return Optional.empty(); + } else { + throw exception; + } + } + } + + public Optional assignDeviceToEdge(EdgeId edgeId, DeviceId deviceId) { + try { + ResponseEntity device = restTemplate.postForEntity(baseURL + "/api/edge/{edgeId}/device/{deviceId}", null, Device.class, edgeId.getId(), deviceId.getId()); + return Optional.ofNullable(device.getBody()); + } catch (HttpClientErrorException exception) { + if (exception.getStatusCode() == HttpStatus.NOT_FOUND) { + return Optional.empty(); + } else { + throw exception; + } + } + } + + public Optional unassignDeviceFromEdge(EdgeId edgeId, DeviceId deviceId) { + try { + ResponseEntity device = restTemplate.exchange(baseURL + "/api/edge/{edgeId}/device/{deviceId}", HttpMethod.DELETE, HttpEntity.EMPTY, Device.class, edgeId.getId(), deviceId.getId()); + return Optional.ofNullable(device.getBody()); + } catch (HttpClientErrorException exception) { + if (exception.getStatusCode() == HttpStatus.NOT_FOUND) { + return Optional.empty(); + } else { + throw exception; + } + } + } + + public PageData getEdgeDevices(EdgeId edgeId, PageLink pageLink) { + Map params = new HashMap<>(); + params.put("edgeId", edgeId.getId().toString()); + addPageLinkToParam(params, pageLink); + return restTemplate.exchange( + baseURL + "/api/edge/{edgeId}/devices?" + getUrlParams(pageLink), + HttpMethod.GET, HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }, params).getBody(); + } + + public Optional assignAssetToEdge(EdgeId edgeId, AssetId assetId) { + try { + ResponseEntity asset = restTemplate.postForEntity(baseURL + "/api/edge/{edgeId}/asset/{assetId}", null, Asset.class, edgeId.getId(), assetId.getId()); + return Optional.ofNullable(asset.getBody()); + } catch (HttpClientErrorException exception) { + if (exception.getStatusCode() == HttpStatus.NOT_FOUND) { + return Optional.empty(); + } else { + throw exception; + } + } + } + + public Optional unassignAssetFromEdge(EdgeId edgeId, AssetId assetId) { + try { + ResponseEntity asset = restTemplate.exchange(baseURL + "/api/edge/{edgeId}/asset/{assetId}", HttpMethod.DELETE, HttpEntity.EMPTY, Asset.class, edgeId.getId(), assetId.getId()); + return Optional.ofNullable(asset.getBody()); + } catch (HttpClientErrorException exception) { + if (exception.getStatusCode() == HttpStatus.NOT_FOUND) { + return Optional.empty(); + } else { + throw exception; + } + } + } + + public PageData getEdgeAssets(EdgeId edgeId, PageLink pageLink) { + Map params = new HashMap<>(); + params.put("edgeId", edgeId.getId().toString()); + addPageLinkToParam(params, pageLink); + return restTemplate.exchange( + baseURL + "/api/edge/{edgeId}/assets?" + getUrlParams(pageLink), + HttpMethod.GET, HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }, params).getBody(); + } + + public Optional assignDashboardToEdge(EdgeId edgeId, DashboardId dashboardId) { + try { + ResponseEntity dashboard = restTemplate.postForEntity(baseURL + "/api/edge/{edgeId}/dashboard/{dashboardId}", null, Dashboard.class, edgeId.getId(), dashboardId.getId()); + return Optional.ofNullable(dashboard.getBody()); + } catch (HttpClientErrorException exception) { + if (exception.getStatusCode() == HttpStatus.NOT_FOUND) { + return Optional.empty(); + } else { + throw exception; + } + } + } + + public Optional unassignDashboardFromEdge(EdgeId edgeId, DashboardId dashboardId) { + try { + ResponseEntity dashboard = restTemplate.exchange(baseURL + "/api/edge/{edgeId}/dashboard/{dashboardId}", HttpMethod.DELETE, HttpEntity.EMPTY, Dashboard.class, edgeId.getId(), dashboardId.getId()); + return Optional.ofNullable(dashboard.getBody()); + } catch (HttpClientErrorException exception) { + if (exception.getStatusCode() == HttpStatus.NOT_FOUND) { + return Optional.empty(); + } else { + throw exception; + } + } + } + + public PageData getEdgeDashboards(EdgeId edgeId, TimePageLink pageLink) { + Map params = new HashMap<>(); + params.put("edgeId", edgeId.getId().toString()); + addPageLinkToParam(params, pageLink); + return restTemplate.exchange( + baseURL + "/api/edge/{edgeId}/dashboards?" + getUrlParams(pageLink), + HttpMethod.GET, HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }, params).getBody(); + } + + public Optional assignEntityViewToEdge(EdgeId edgeId, EntityViewId entityViewId) { + try { + ResponseEntity entityView = restTemplate.postForEntity(baseURL + "/api/edge/{edgeId}/entityView/{entityViewId}", null, EntityView.class, edgeId.getId(), entityViewId.getId()); + return Optional.ofNullable(entityView.getBody()); + } catch (HttpClientErrorException exception) { + if (exception.getStatusCode() == HttpStatus.NOT_FOUND) { + return Optional.empty(); + } else { + throw exception; + } + } + } + + public Optional unassignEntityViewFromEdge(EdgeId edgeId, EntityViewId entityViewId) { + try { + ResponseEntity entityView = restTemplate.exchange(baseURL + "/api/edge/{edgeId}/entityView/{entityViewId}", + HttpMethod.DELETE, HttpEntity.EMPTY, EntityView.class, edgeId.getId(), entityViewId.getId()); + return Optional.ofNullable(entityView.getBody()); + } catch (HttpClientErrorException exception) { + if (exception.getStatusCode() == HttpStatus.NOT_FOUND) { + return Optional.empty(); + } else { + throw exception; + } + } + } + + public PageData getEdgeEntityViews(EdgeId edgeId, PageLink pageLink) { + Map params = new HashMap<>(); + params.put("edgeId", edgeId.getId().toString()); + addPageLinkToParam(params, pageLink); + return restTemplate.exchange( + baseURL + "/api/edge/{edgeId}/entityViews?" + getUrlParams(pageLink), + HttpMethod.GET, + HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }, params).getBody(); + } + + public Optional assignRuleChainToEdge(EdgeId edgeId, RuleChainId ruleChainId) { + try { + ResponseEntity ruleChain = restTemplate.postForEntity(baseURL + "/api/edge/{edgeId}/ruleChain/{ruleChainId}", null, RuleChain.class, edgeId.getId(), ruleChainId.getId()); + return Optional.ofNullable(ruleChain.getBody()); + } catch (HttpClientErrorException exception) { + if (exception.getStatusCode() == HttpStatus.NOT_FOUND) { + return Optional.empty(); + } else { + throw exception; + } + } + } + + public Optional unassignRuleChainFromEdge(EdgeId edgeId, RuleChainId ruleChainId) { + try { + ResponseEntity ruleChain = restTemplate.exchange(baseURL + "/api/edge/{edgeId}/ruleChain/{ruleChainId}", HttpMethod.DELETE, HttpEntity.EMPTY, RuleChain.class, edgeId.getId(), ruleChainId.getId()); + return Optional.ofNullable(ruleChain.getBody()); + } catch (HttpClientErrorException exception) { + if (exception.getStatusCode() == HttpStatus.NOT_FOUND) { + return Optional.empty(); + } else { + throw exception; + } + } + } + + public PageData getEdgeRuleChains(EdgeId edgeId, TimePageLink pageLink) { + Map params = new HashMap<>(); + params.put("edgeId", edgeId.getId().toString()); + addPageLinkToParam(params, pageLink); + return restTemplate.exchange( + baseURL + "/api/edge/{edgeId}/ruleChains?" + getUrlParams(pageLink), + HttpMethod.GET, HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }, params).getBody(); + } + + public Optional addDefaultEdgeRuleChain(RuleChainId ruleChainId) { + try { + ResponseEntity ruleChain = restTemplate.postForEntity(baseURL + "/api/ruleChain/{ruleChainId}/defaultEdge", null, RuleChain.class, ruleChainId.getId()); + return Optional.ofNullable(ruleChain.getBody()); + } catch (HttpClientErrorException exception) { + if (exception.getStatusCode() == HttpStatus.NOT_FOUND) { + return Optional.empty(); + } else { + throw exception; + } + } + } + + public Optional removeDefaultEdgeRuleChain(RuleChainId ruleChainId) { + try { + ResponseEntity ruleChain = restTemplate.exchange(baseURL + "/api/ruleChain/{ruleChainId}/defaultEdge", HttpMethod.DELETE, HttpEntity.EMPTY, RuleChain.class, ruleChainId.getId()); + return Optional.ofNullable(ruleChain.getBody()); + } catch (HttpClientErrorException exception) { + if (exception.getStatusCode() == HttpStatus.NOT_FOUND) { + return Optional.empty(); + } else { + throw exception; + } + } + } + + public List getDefaultEdgeRuleChains() { + return restTemplate.exchange(baseURL + "/api/ruleChain/defaultEdgeRuleChains", + HttpMethod.GET, + HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }).getBody(); + } + + public Optional setDefaultRootEdgeRuleChain(RuleChainId ruleChainId) { + try { + ResponseEntity ruleChain = restTemplate.postForEntity(baseURL + "/api/ruleChain/{ruleChainId}/defaultRootEdge", null, RuleChain.class, ruleChainId.getId()); + return Optional.ofNullable(ruleChain.getBody()); + } catch (HttpClientErrorException exception) { + if (exception.getStatusCode() == HttpStatus.NOT_FOUND) { + return Optional.empty(); + } else { + throw exception; + } + } + } + + public PageData getTenantEdges(String type, PageLink pageLink) { + Map params = new HashMap<>(); + params.put("type", type); + addPageLinkToParam(params, pageLink); + return restTemplate.exchange( + baseURL + "/api/tenant/edges?type={type}&" + getUrlParams(pageLink), + HttpMethod.GET, HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }, params).getBody(); + } + + public Optional getTenantEdge(String edgeName) { + try { + ResponseEntity edge = restTemplate.getForEntity(baseURL + "/api/tenant/edges?edgeName={edgeName}", Edge.class, edgeName); + return Optional.ofNullable(edge.getBody()); + } catch (HttpClientErrorException exception) { + if (exception.getStatusCode() == HttpStatus.NOT_FOUND) { + return Optional.empty(); + } else { + throw exception; + } + } + } + + public PageData getCustomerEdges(CustomerId customerId, String edgeType, PageLink pageLink) { + Map params = new HashMap<>(); + params.put("customerId", customerId.getId().toString()); + params.put("type", edgeType); + addPageLinkToParam(params, pageLink); + return restTemplate.exchange( + baseURL + "/api/customer/{customerId}/edges?type={type}&" + getUrlParams(pageLink), + HttpMethod.GET, HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }, params).getBody(); + } + + public List getEdgesByIds(List edgeIds) { + return restTemplate.exchange(baseURL + "/api/edges?edgeIds={edgeIds}", + HttpMethod.GET, + HttpEntity.EMPTY, new ParameterizedTypeReference>() { + }, listIdsToString(edgeIds)).getBody(); + } + + public List findByQuery(EdgeSearchQuery query) { + return restTemplate.exchange( + baseURL + "/api/edges", + HttpMethod.POST, + new HttpEntity<>(query), + new ParameterizedTypeReference>() { + }).getBody(); + } + + public List getEdgeTypes() { + return restTemplate.exchange( + baseURL + "/api/edge/types", + HttpMethod.GET, + HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }).getBody(); + } + + public PageData getEdgeEvents(EdgeId edgeId, TimePageLink pageLink) { + Map params = new HashMap<>(); + params.put("edgeId", edgeId.toString()); + addPageLinkToParam(params, pageLink); + return restTemplate.exchange( + baseURL + "/api/edge/{edgeId}/events?" + getUrlParams(pageLink), + HttpMethod.GET, + HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }, + params).getBody(); + } + + public void syncEdge(EdgeId edgeId) { + restTemplate.postForEntity(baseURL + "/api/edge/sync", edgeId, EdgeId.class); + } + @Deprecated public Optional getAttributes(String accessToken, String clientKeys, String sharedKeys) { Map params = new HashMap<>(); 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 1efe0081d7..0272b3b372 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.CORE; + RuleChainType[] ruleChainTypes() default {RuleChainType.CORE, RuleChainType.EDGE}; } 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 5d0fe87fa9..2ae79c81e5 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 @@ -22,9 +22,13 @@ 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.EntityType; -import org.thingsboard.server.common.data.id.*; +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.EntityViewId; import org.thingsboard.server.common.data.plugin.ComponentType; -import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; @Slf4j @@ -37,8 +41,7 @@ import org.thingsboard.server.common.msg.TbMsg; "Will create new Customer if it doesn't exists and 'Create new Customer if not exists' is set to true.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeAssignToCustomerConfig", - icon = "add_circle", - ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} + icon = "add_circle" ) 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 f56546c877..67e9063ac8 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 @@ -29,7 +29,6 @@ import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmStatus; import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.plugin.ComponentType; -import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; @Slf4j @@ -46,8 +45,7 @@ import org.thingsboard.server.common.msg.TbMsg; "Message metadata can be accessed via metadata property. For example 'name = ' + metadata.customerName;.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeClearAlarmConfig", - icon = "notifications_off", - ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} + icon = "notifications_off" ) 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 96595eb845..6c569a1713 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 @@ -21,19 +21,18 @@ import com.google.gson.JsonElement; import com.google.gson.JsonParser; import com.google.gson.JsonPrimitive; import lombok.extern.slf4j.Slf4j; +import org.thingsboard.common.util.DonAsynchron; import org.thingsboard.rule.engine.api.EmptyNodeConfiguration; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; -import org.thingsboard.common.util.DonAsynchron; import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.plugin.ComponentType; -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.common.transport.adaptor.JsonConverter; @@ -57,8 +56,7 @@ import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; "Changes message originator to related entity view and produces new messages according to count of updated entity views", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbNodeEmptyConfig", - icon = "content_copy", - ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} + icon = "content_copy" ) 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 ee4a02416a..1693b4003b 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 @@ -31,7 +31,6 @@ import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmStatus; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.plugin.ComponentType; -import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; import java.io.IOException; @@ -51,8 +50,7 @@ import java.util.List; "Message metadata can be accessed via metadata property. For example 'name = ' + metadata.customerName;.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeCreateAlarmConfig", - icon = "notifications_active", - ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} + icon = "notifications_active" ) 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 c9c0edd96f..f87c49ba7f 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 @@ -34,7 +34,6 @@ 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.data.rule.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; import java.util.ArrayList; @@ -54,8 +53,7 @@ import java.util.List; nodeDetails = "If the relation already exists or successfully created - Message send via Success chain, otherwise Failure chain will be used.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeCreateRelationConfig", - icon = "add_circle", - ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} + icon = "add_circle" ) 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 2ea2264dad..b27dde1140 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 @@ -27,7 +27,6 @@ import org.thingsboard.rule.engine.util.EntityContainer; 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.data.rule.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; import java.util.ArrayList; @@ -44,8 +43,7 @@ import java.util.List; nodeDetails = "If the relation(s) successfully deleted - Message send via Success chain, otherwise Failure chain will be used.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeDeleteRelationConfig", - icon = "remove_circle", - ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} + icon = "remove_circle" ) 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 393cc72b0b..3dbdb2a57c 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 @@ -17,14 +17,17 @@ package org.thingsboard.rule.engine.action; import lombok.extern.slf4j.Slf4j; import org.thingsboard.common.util.ListeningExecutor; +import org.thingsboard.rule.engine.api.RuleNode; +import org.thingsboard.rule.engine.api.ScriptEngine; +import org.thingsboard.rule.engine.api.TbContext; +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.rule.engine.api.*; import org.thingsboard.server.common.data.plugin.ComponentType; -import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; import static org.thingsboard.common.util.DonAsynchron.withCallback; -import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; @Slf4j @RuleNode( @@ -37,10 +40,8 @@ import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; "Message metadata can be accessed via metadata property. For example 'name = ' + metadata.customerName;.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeLogConfig", - icon = "menu", - ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} + icon = "menu" ) - public class TbLogNode implements TbNode { private TbLogNodeConfiguration config; 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 9062b2bbae..188fe7d7bc 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 @@ -18,12 +18,14 @@ package org.thingsboard.rule.engine.action; import com.google.gson.Gson; import com.google.gson.JsonObject; import lombok.extern.slf4j.Slf4j; -import org.thingsboard.rule.engine.api.*; +import org.thingsboard.rule.engine.api.RuleNode; +import org.thingsboard.rule.engine.api.TbContext; +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.plugin.ComponentType; -import org.thingsboard.server.common.data.rule.RuleChainType; 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.ServiceQueue; import org.thingsboard.server.common.msg.session.SessionMsgType; @@ -43,8 +45,7 @@ import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; nodeDetails = "Count incoming messages for specified interval and produces POST_TELEMETRY_REQUEST msg with messages count", icon = "functions", uiResources = {"static/rulenode/rulenode-core-config.js"}, - configDirective = "tbActionNodeMsgCountConfig", - ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} + configDirective = "tbActionNodeMsgCountConfig" ) public class TbMsgCountNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNode.java index 992c2441c9..bf14a040e0 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNode.java @@ -29,7 +29,11 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.google.gson.JsonPrimitive; import lombok.extern.slf4j.Slf4j; -import org.thingsboard.rule.engine.api.*; +import org.thingsboard.rule.engine.api.RuleNode; +import org.thingsboard.rule.engine.api.TbContext; +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.plugin.ComponentType; import org.thingsboard.server.common.data.rule.RuleChainType; @@ -63,8 +67,8 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback; " otherwise, the message will be routed via success chain.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeCustomTableConfig", - icon = "file_upload" -) + icon = "file_upload", + ruleChainTypes = RuleChainType.CORE) public class TbSaveToCustomCassandraTableNode implements TbNode { private static final String TABLE_PREFIX = "cs_tb_"; 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 79513c728d..3f2b17157d 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 @@ -21,9 +21,13 @@ 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.EntityType; -import org.thingsboard.server.common.data.id.*; +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.EntityViewId; import org.thingsboard.server.common.data.plugin.ComponentType; -import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; @RuleNode( @@ -34,8 +38,7 @@ import org.thingsboard.server.common.msg.TbMsg; nodeDetails = "Finds target Entity Customer by Customer name pattern and then unassign Originator Entity from this customer.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeUnAssignToCustomerConfig", - icon = "remove_circle", - ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} + icon = "remove_circle" ) 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 bfc9a0c865..2c0bf17575 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 @@ -24,10 +24,13 @@ import com.amazonaws.services.sns.model.PublishRequest; import com.amazonaws.services.sns.model.PublishResult; import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; +import org.thingsboard.rule.engine.api.RuleNode; +import org.thingsboard.rule.engine.api.TbContext; +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.rule.engine.api.*; import org.thingsboard.server.common.data.plugin.ComponentType; -import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; @@ -46,8 +49,7 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback; "For example requestId field can be accessed with metadata.requestId.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeSnsConfig", - iconUrl = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjQ4IiBoZWlnaHQ9IjQ4Ij48cGF0aCBkPSJNMTMuMjMgMTAuNTZWMTBjLTEuOTQgMC0zLjk5LjM5LTMuOTkgMi42NyAwIDEuMTYuNjEgMS45NSAxLjYzIDEuOTUuNzYgMCAxLjQzLS40NyAxLjg2LTEuMjIuNTItLjkzLjUtMS44LjUtMi44NG0yLjcgNi41M2MtLjE4LjE2LS40My4xNy0uNjMuMDYtLjg5LS43NC0xLjA1LTEuMDgtMS41NC0xLjc5LTEuNDcgMS41LTIuNTEgMS45NS00LjQyIDEuOTUtMi4yNSAwLTQuMDEtMS4zOS00LjAxLTQuMTcgMC0yLjE4IDEuMTctMy42NCAyLjg2LTQuMzggMS40Ni0uNjQgMy40OS0uNzYgNS4wNC0uOTNWNy41YzAtLjY2LjA1LTEuNDEtLjMzLTEuOTYtLjMyLS40OS0uOTUtLjctMS41LS43LTEuMDIgMC0xLjkzLjUzLTIuMTUgMS42MS0uMDUuMjQtLjI1LjQ4LS40Ny40OWwtMi42LS4yOGMtLjIyLS4wNS0uNDYtLjIyLS40LS41Ni42LTMuMTUgMy40NS00LjEgNi00LjEgMS4zIDAgMyAuMzUgNC4wMyAxLjMzQzE3LjExIDQuNTUgMTcgNi4xOCAxNyA3Ljk1djQuMTdjMCAxLjI1LjUgMS44MSAxIDIuNDguMTcuMjUuMjEuNTQgMCAuNzFsLTIuMDYgMS43OGgtLjAxIj48L3BhdGg+PHBhdGggZD0iTTIwLjE2IDE5LjU0QzE4IDIxLjE0IDE0LjgyIDIyIDEyLjEgMjJjLTMuODEgMC03LjI1LTEuNDEtOS44NS0zLjc2LS4yLS4xOC0uMDItLjQzLjI1LS4yOSAyLjc4IDEuNjMgNi4yNSAyLjYxIDkuODMgMi42MSAyLjQxIDAgNS4wNy0uNSA3LjUxLTEuNTMuMzctLjE2LjY2LjI0LjMyLjUxIj48L3BhdGg+PHBhdGggZD0iTTIxLjA3IDE4LjVjLS4yOC0uMzYtMS44NS0uMTctMi41Ny0uMDgtLjE5LjAyLS4yMi0uMTYtLjAzLS4zIDEuMjQtLjg4IDMuMjktLjYyIDMuNTMtLjMzLjI0LjMtLjA3IDIuMzUtMS4yNCAzLjMyLS4xOC4xNi0uMzUuMDctLjI2LS4xMS4yNi0uNjcuODUtMi4xNC41Ny0yLjV6Ij48L3BhdGg+PC9zdmc+", - ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} + iconUrl = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjQ4IiBoZWlnaHQ9IjQ4Ij48cGF0aCBkPSJNMTMuMjMgMTAuNTZWMTBjLTEuOTQgMC0zLjk5LjM5LTMuOTkgMi42NyAwIDEuMTYuNjEgMS45NSAxLjYzIDEuOTUuNzYgMCAxLjQzLS40NyAxLjg2LTEuMjIuNTItLjkzLjUtMS44LjUtMi44NG0yLjcgNi41M2MtLjE4LjE2LS40My4xNy0uNjMuMDYtLjg5LS43NC0xLjA1LTEuMDgtMS41NC0xLjc5LTEuNDcgMS41LTIuNTEgMS45NS00LjQyIDEuOTUtMi4yNSAwLTQuMDEtMS4zOS00LjAxLTQuMTcgMC0yLjE4IDEuMTctMy42NCAyLjg2LTQuMzggMS40Ni0uNjQgMy40OS0uNzYgNS4wNC0uOTNWNy41YzAtLjY2LjA1LTEuNDEtLjMzLTEuOTYtLjMyLS40OS0uOTUtLjctMS41LS43LTEuMDIgMC0xLjkzLjUzLTIuMTUgMS42MS0uMDUuMjQtLjI1LjQ4LS40Ny40OWwtMi42LS4yOGMtLjIyLS4wNS0uNDYtLjIyLS40LS41Ni42LTMuMTUgMy40NS00LjEgNi00LjEgMS4zIDAgMyAuMzUgNC4wMyAxLjMzQzE3LjExIDQuNTUgMTcgNi4xOCAxNyA3Ljk1djQuMTdjMCAxLjI1LjUgMS44MSAxIDIuNDguMTcuMjUuMjEuNTQgMCAuNzFsLTIuMDYgMS43OGgtLjAxIj48L3BhdGg+PHBhdGggZD0iTTIwLjE2IDE5LjU0QzE4IDIxLjE0IDE0LjgyIDIyIDEyLjEgMjJjLTMuODEgMC03LjI1LTEuNDEtOS44NS0zLjc2LS4yLS4xOC0uMDItLjQzLjI1LS4yOSAyLjc4IDEuNjMgNi4yNSAyLjYxIDkuODMgMi42MSAyLjQxIDAgNS4wNy0uNSA3LjUxLTEuNTMuMzctLjE2LjY2LjI0LjMyLjUxIj48L3BhdGg+PHBhdGggZD0iTTIxLjA3IDE4LjVjLS4yOC0uMzYtMS44NS0uMTctMi41Ny0uMDgtLjE5LjAyLS4yMi0uMTYtLjAzLS4zIDEuMjQtLjg4IDMuMjktLjYyIDMuNTMtLjMzLjI0LjMtLjA3IDIuMzUtMS4yNCAzLjMyLS4xOC4xNi0uMzUuMDctLjI2LS4xMS4yNi0uNjcuODUtMi4xNC41Ny0yLjV6Ij48L3BhdGg+PC9zdmc+" ) 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 27359dc8f3..0fd09ca351 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 @@ -26,16 +26,18 @@ import com.amazonaws.services.sqs.model.SendMessageResult; import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; +import org.thingsboard.rule.engine.api.RuleNode; +import org.thingsboard.rule.engine.api.TbContext; +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.rule.engine.api.*; import org.thingsboard.server.common.data.plugin.ComponentType; -import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.ExecutionException; import static org.thingsboard.common.util.DonAsynchron.withCallback; @@ -51,8 +53,7 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback; " For example requestId field can be accessed with metadata.requestId.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeSqsConfig", - iconUrl = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjQ4IiBoZWlnaHQ9IjQ4Ij48cGF0aCBkPSJNMTMuMjMgMTAuNTZWMTBjLTEuOTQgMC0zLjk5LjM5LTMuOTkgMi42NyAwIDEuMTYuNjEgMS45NSAxLjYzIDEuOTUuNzYgMCAxLjQzLS40NyAxLjg2LTEuMjIuNTItLjkzLjUtMS44LjUtMi44NG0yLjcgNi41M2MtLjE4LjE2LS40My4xNy0uNjMuMDYtLjg5LS43NC0xLjA1LTEuMDgtMS41NC0xLjc5LTEuNDcgMS41LTIuNTEgMS45NS00LjQyIDEuOTUtMi4yNSAwLTQuMDEtMS4zOS00LjAxLTQuMTcgMC0yLjE4IDEuMTctMy42NCAyLjg2LTQuMzggMS40Ni0uNjQgMy40OS0uNzYgNS4wNC0uOTNWNy41YzAtLjY2LjA1LTEuNDEtLjMzLTEuOTYtLjMyLS40OS0uOTUtLjctMS41LS43LTEuMDIgMC0xLjkzLjUzLTIuMTUgMS42MS0uMDUuMjQtLjI1LjQ4LS40Ny40OWwtMi42LS4yOGMtLjIyLS4wNS0uNDYtLjIyLS40LS41Ni42LTMuMTUgMy40NS00LjEgNi00LjEgMS4zIDAgMyAuMzUgNC4wMyAxLjMzQzE3LjExIDQuNTUgMTcgNi4xOCAxNyA3Ljk1djQuMTdjMCAxLjI1LjUgMS44MSAxIDIuNDguMTcuMjUuMjEuNTQgMCAuNzFsLTIuMDYgMS43OGgtLjAxIj48L3BhdGg+PHBhdGggZD0iTTIwLjE2IDE5LjU0QzE4IDIxLjE0IDE0LjgyIDIyIDEyLjEgMjJjLTMuODEgMC03LjI1LTEuNDEtOS44NS0zLjc2LS4yLS4xOC0uMDItLjQzLjI1LS4yOSAyLjc4IDEuNjMgNi4yNSAyLjYxIDkuODMgMi42MSAyLjQxIDAgNS4wNy0uNSA3LjUxLTEuNTMuMzctLjE2LjY2LjI0LjMyLjUxIj48L3BhdGg+PHBhdGggZD0iTTIxLjA3IDE4LjVjLS4yOC0uMzYtMS44NS0uMTctMi41Ny0uMDgtLjE5LjAyLS4yMi0uMTYtLjAzLS4zIDEuMjQtLjg4IDMuMjktLjYyIDMuNTMtLjMzLjI0LjMtLjA3IDIuMzUtMS4yNCAzLjMyLS4xOC4xNi0uMzUuMDctLjI2LS4xMS4yNi0uNjcuODUtMi4xNC41Ny0yLjV6Ij48L3BhdGg+PC9zdmc+", - ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} + iconUrl = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjQ4IiBoZWlnaHQ9IjQ4Ij48cGF0aCBkPSJNMTMuMjMgMTAuNTZWMTBjLTEuOTQgMC0zLjk5LjM5LTMuOTkgMi42NyAwIDEuMTYuNjEgMS45NSAxLjYzIDEuOTUuNzYgMCAxLjQzLS40NyAxLjg2LTEuMjIuNTItLjkzLjUtMS44LjUtMi44NG0yLjcgNi41M2MtLjE4LjE2LS40My4xNy0uNjMuMDYtLjg5LS43NC0xLjA1LTEuMDgtMS41NC0xLjc5LTEuNDcgMS41LTIuNTEgMS45NS00LjQyIDEuOTUtMi4yNSAwLTQuMDEtMS4zOS00LjAxLTQuMTcgMC0yLjE4IDEuMTctMy42NCAyLjg2LTQuMzggMS40Ni0uNjQgMy40OS0uNzYgNS4wNC0uOTNWNy41YzAtLjY2LjA1LTEuNDEtLjMzLTEuOTYtLjMyLS40OS0uOTUtLjctMS41LS43LTEuMDIgMC0xLjkzLjUzLTIuMTUgMS42MS0uMDUuMjQtLjI1LjQ4LS40Ny40OWwtMi42LS4yOGMtLjIyLS4wNS0uNDYtLjIyLS40LS41Ni42LTMuMTUgMy40NS00LjEgNi00LjEgMS4zIDAgMyAuMzUgNC4wMyAxLjMzQzE3LjExIDQuNTUgMTcgNi4xOCAxNyA3Ljk1djQuMTdjMCAxLjI1LjUgMS44MSAxIDIuNDguMTcuMjUuMjEuNTQgMCAuNzFsLTIuMDYgMS43OGgtLjAxIj48L3BhdGg+PHBhdGggZD0iTTIwLjE2IDE5LjU0QzE4IDIxLjE0IDE0LjgyIDIyIDEyLjEgMjJjLTMuODEgMC03LjI1LTEuNDEtOS44NS0zLjc2LS4yLS4xOC0uMDItLjQzLjI1LS4yOSAyLjc4IDEuNjMgNi4yNSAyLjYxIDkuODMgMi42MSAyLjQxIDAgNS4wNy0uNSA3LjUxLTEuNTMuMzctLjE2LjY2LjI0LjMyLjUxIj48L3BhdGg+PHBhdGggZD0iTTIxLjA3IDE4LjVjLS4yOC0uMzYtMS44NS0uMTctMi41Ny0uMDgtLjE5LjAyLS4yMi0uMTYtLjAzLS4zIDEuMjQtLjg4IDMuMjktLjYyIDMuNTMtLjMzLjI0LjMtLjA3IDIuMzUtMS4yNCAzLjMyLS4xOC4xNi0uMzUuMDctLjI2LS4xMS4yNi0uNjcuODUtMi4xNC41Ny0yLjV6Ij48L3BhdGg+PC9zdmc+" ) 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 0b69d647c4..1e3993192a 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 @@ -18,12 +18,16 @@ package org.thingsboard.rule.engine.debug; import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.springframework.util.StringUtils; +import org.thingsboard.rule.engine.api.RuleNode; +import org.thingsboard.rule.engine.api.ScriptEngine; +import org.thingsboard.rule.engine.api.TbContext; +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.rule.engine.api.*; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.plugin.ComponentType; -import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.queue.PartitionChangeMsg; @@ -45,8 +49,7 @@ import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; inEnabled = false, uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeGeneratorConfig", - icon = "repeat", - ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} + icon = "repeat" ) 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 0b806465b0..6f14e20300 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 @@ -24,7 +24,6 @@ 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.plugin.ComponentType; -import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.queue.ServiceQueue; @@ -34,7 +33,6 @@ import java.util.Map; import java.util.UUID; import java.util.concurrent.TimeUnit; -import static org.thingsboard.rule.engine.api.TbRelationTypes.FAILURE; import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; @Slf4j @@ -46,8 +44,7 @@ import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; nodeDetails = "Delays messages for configurable period. Please note, this node acknowledges the message from the current queue (message will be removed from queue)", icon = "pause", uiResources = {"static/rulenode/rulenode-core-config.js"}, - configDirective = "tbActionNodeMsgDelayConfig", - ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} + configDirective = "tbActionNodeMsgDelayConfig" ) public class TbMsgDelayNode implements TbNode { 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 ad757220b3..b510af10b0 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,8 @@ */ package org.thingsboard.rule.engine.edge; +import com.fasterxml.jackson.core.JsonProcessingException; +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; @@ -28,21 +30,23 @@ 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.EdgeUtils; 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.data.rule.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.session.SessionMsgType; import javax.annotation.Nullable; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.UUID; import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; @@ -55,7 +59,8 @@ import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; nodeDetails = "Pushes messages to edge, if Message Originator assigned to particular edge or is EDGE entity. This node is used only on Cloud instances to push messages from Cloud to Edge. Supports only DEVICE, ENTITY_VIEW, ASSET and EDGE Message Originator(s).", uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"}, configDirective = "tbNodeEmptyConfig", - icon = "cloud_download" + icon = "cloud_download", + ruleChainTypes = RuleChainType.CORE ) public class TbMsgPushToEdgeNode implements TbNode { @@ -72,39 +77,12 @@ public class TbMsgPushToEdgeNode implements TbNode { public void onMsg(TbContext ctx, TbMsg msg) { if (DataConstants.EDGE_MSG_SOURCE.equalsIgnoreCase(msg.getMetaData().getValue(DataConstants.MSG_SOURCE_KEY))) { log.debug("Ignoring msg from the cloud, msg [{}]", msg); + ctx.ack(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() + "'")); - } - EdgeEvent edgeEvent = new EdgeEvent(); - edgeEvent.setTenantId(ctx.getTenantId()); - edgeEvent.setEdgeId(edgeId); - edgeEvent.setEdgeEventAction(getActionTypeByMsgType(msg.getType()).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()); + processMsg(ctx, msg); } else { log.debug("Unsupported msg type {}", msg.getType()); ctx.tellFailure(msg, new RuntimeException("Unsupported msg type '" + msg.getType() + "'")); @@ -115,6 +93,101 @@ public class TbMsgPushToEdgeNode implements TbNode { } } + private void processMsg(TbContext ctx, TbMsg msg) { + ListenableFuture> getEdgeIdsFuture = ctx.getEdgeService().findRelatedEdgeIdsByEntityId(ctx.getTenantId(), msg.getOriginator()); + Futures.addCallback(getEdgeIdsFuture, new FutureCallback>() { + @Override + public void onSuccess(@Nullable List edgeIds) { + if (edgeIds != null && !edgeIds.isEmpty()) { + for (EdgeId edgeId : edgeIds) { + try { + EdgeEvent edgeEvent = buildEdgeEvent(msg, ctx); + if (edgeEvent == 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() + "'")); + } else { + edgeEvent.setEdgeId(edgeId); + 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()); + } + } catch (JsonProcessingException e) { + log.error("Failed to build edge event", e); + ctx.tellFailure(msg, e); + } + } + } + } + + @Override + public void onFailure(Throwable t) { + ctx.tellFailure(msg, t); + } + + }, ctx.getDbCallbackExecutor()); + } + + private EdgeEvent buildEdgeEvent(TbMsg msg, TbContext ctx) throws JsonProcessingException { + String msgType = msg.getType(); + if (DataConstants.ALARM.equals(msgType)) { + return buildEdgeEvent(ctx.getTenantId(), ActionType.ADDED, getUUIDFromMsgData(msg), EdgeEventType.ALARM, null); + } else { + EdgeEventType edgeEventTypeByEntityType = EdgeUtils.getEdgeEventTypeByEntityType(msg.getOriginator().getEntityType()); + if (edgeEventTypeByEntityType == null) { + return null; + } + ActionType actionType = getActionTypeByMsgType(msgType); + Map entityBody = new HashMap<>(); + Map metadata = msg.getMetaData().getData(); + JsonNode dataJson = json.readTree(msg.getData()); + switch (actionType) { + case ATTRIBUTES_UPDATED: + entityBody.put("kv", dataJson); + entityBody.put("scope", metadata.get("scope")); + if (SessionMsgType.POST_ATTRIBUTES_REQUEST.name().equals(msgType)) { + entityBody.put("isPostAttributes", true); + } + break; + case ATTRIBUTES_DELETED: + List keys = json.treeToValue(dataJson.get("attributes"), List.class); + entityBody.put("keys", keys); + entityBody.put("scope", metadata.get("scope")); + break; + case TIMESERIES_UPDATED: + entityBody.put("data", dataJson); + entityBody.put("ts", metadata.get("ts")); + break; + } + return buildEdgeEvent(ctx.getTenantId(), actionType, msg.getOriginator().getId(), edgeEventTypeByEntityType, json.valueToTree(entityBody)); + } + } + + private EdgeEvent buildEdgeEvent(TenantId tenantId, ActionType edgeEventAction, UUID entityId, EdgeEventType edgeEventType, JsonNode entityBody) { + EdgeEvent edgeEvent = new EdgeEvent(); + edgeEvent.setTenantId(tenantId); + edgeEvent.setAction(edgeEventAction.name()); + edgeEvent.setEntityId(entityId); + edgeEvent.setType(edgeEventType); + edgeEvent.setBody(entityBody); + return edgeEvent; + } + + private UUID getUUIDFromMsgData(TbMsg msg) throws JsonProcessingException { + JsonNode data = json.readTree(msg.getData()).get("id"); + String id = json.treeToValue(data.get("id"), String.class); + return UUID.fromString(id); + } + private ActionType getActionTypeByMsgType(String msgType) { ActionType actionType; if (SessionMsgType.POST_TELEMETRY_REQUEST.name().equals(msgType)) { @@ -134,6 +207,8 @@ public class TbMsgPushToEdgeNode implements TbNode { case ASSET: case ENTITY_VIEW: case DASHBOARD: + case TENANT: + case CUSTOMER: return true; default: return false; @@ -141,25 +216,11 @@ public class TbMsgPushToEdgeNode implements TbNode { } private boolean isSupportedMsgType(String msgType) { - if (SessionMsgType.POST_TELEMETRY_REQUEST.name().equals(msgType) + return 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 -> { - if (relations != null && relations.size() > 0) { - return new EdgeId(relations.get(0).getFrom().getId()); - } else { - return null; - } - }, ctx.getDbCallbackExecutor()); + || DataConstants.ATTRIBUTES_DELETED.equals(msgType) + || DataConstants.ALARM.equals(msgType); } @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 e9d54de5de..52fb7e8a77 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 @@ -24,7 +24,6 @@ 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.plugin.ComponentType; -import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; import java.util.List; @@ -40,9 +39,7 @@ import java.util.Map; nodeDetails = "If selected checkbox 'Check that all selected keys are present'\" and all keys in message data and metadata are exist - send Message via True chain, otherwise False chain is used.\n" + "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.CORE, RuleChainType.EDGE} -) + configDirective = "tbFilterNodeCheckMessageConfig") public class TbCheckMessageNode implements TbNode { private static final Gson gson = new Gson(); 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 f5912d1e5b..89cd986b7e 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 @@ -31,7 +31,6 @@ import org.thingsboard.server.common.data.plugin.ComponentType; 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.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; import java.util.List; @@ -52,9 +51,7 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback; " any relation to the originator of the message by type and direction.", 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.CORE, RuleChainType.EDGE} -) + configDirective = "tbFilterNodeCheckRelationConfig") public class TbCheckRelationNode implements TbNode { private TbCheckRelationNodeConfiguration config; 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 997a872a4a..5e6f2129f4 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 @@ -16,11 +16,14 @@ package org.thingsboard.rule.engine.filter; import lombok.extern.slf4j.Slf4j; -import org.thingsboard.common.util.ListeningExecutor; +import org.thingsboard.rule.engine.api.RuleNode; +import org.thingsboard.rule.engine.api.ScriptEngine; +import org.thingsboard.rule.engine.api.TbContext; +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.rule.engine.api.*; import org.thingsboard.server.common.data.plugin.ComponentType; -import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; import static org.thingsboard.common.util.DonAsynchron.withCallback; @@ -37,9 +40,7 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback; "Message metadata can be accessed via metadata property. For example metadata.customerName === 'John';
" + "Message type can be accessed via msgType property.", uiResources = {"static/rulenode/rulenode-core-config.js"}, - configDirective = "tbFilterNodeScriptConfig", - ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} -) + configDirective = "tbFilterNodeScriptConfig") 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 fa8f332d69..a717185e5a 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 @@ -17,10 +17,14 @@ package org.thingsboard.rule.engine.filter; import lombok.extern.slf4j.Slf4j; import org.thingsboard.common.util.ListeningExecutor; +import org.thingsboard.rule.engine.api.RuleNode; +import org.thingsboard.rule.engine.api.ScriptEngine; +import org.thingsboard.rule.engine.api.TbContext; +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.rule.engine.api.*; import org.thingsboard.server.common.data.plugin.ComponentType; -import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; import java.util.Set; @@ -40,9 +44,7 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback; "Message metadata can be accessed via metadata property. For example metadata.customerName === 'John';
" + "Message type can be accessed via msgType property.", uiResources = {"static/rulenode/rulenode-core-config.js"}, - configDirective = "tbFilterNodeSwitchConfig", - ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} -) + configDirective = "tbFilterNodeSwitchConfig") public class TbJsSwitchNode implements TbNode { private TbJsSwitchNodeConfiguration config; 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 2e9cfc4657..1bb2c410f3 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 @@ -16,10 +16,13 @@ package org.thingsboard.rule.engine.filter; import lombok.extern.slf4j.Slf4j; +import org.thingsboard.rule.engine.api.RuleNode; +import org.thingsboard.rule.engine.api.TbContext; +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.rule.engine.api.*; import org.thingsboard.server.common.data.plugin.ComponentType; -import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; /** @@ -34,9 +37,7 @@ import org.thingsboard.server.common.msg.TbMsg; nodeDescription = "Filter incoming messages by Message Type", nodeDetails = "If incoming MessageType is expected - send Message via True chain, otherwise False chain is used.", uiResources = {"static/rulenode/rulenode-core-config.js"}, - configDirective = "tbFilterNodeMessageTypeConfig", - ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} -) + configDirective = "tbFilterNodeMessageTypeConfig") public class TbMsgTypeFilterNode implements TbNode { TbMsgTypeFilterNodeConfiguration config; 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 e538331c63..c5739e24ff 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 @@ -25,7 +25,6 @@ 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.plugin.ComponentType; -import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.session.SessionMsgType; @@ -40,9 +39,7 @@ import org.thingsboard.server.common.msg.session.SessionMsgType; nodeDescription = "Route incoming messages by Message Type", 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.CORE, RuleChainType.EDGE} -) + configDirective = "tbNodeEmptyConfig") public class TbMsgTypeSwitchNode implements TbNode { EmptyNodeConfiguration config; 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 63c6e627b0..9d09ac7854 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 @@ -16,11 +16,14 @@ package org.thingsboard.rule.engine.filter; import lombok.extern.slf4j.Slf4j; +import org.thingsboard.rule.engine.api.RuleNode; +import org.thingsboard.rule.engine.api.TbContext; +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.rule.engine.api.*; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.plugin.ComponentType; -import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; @Slf4j @@ -32,9 +35,7 @@ import org.thingsboard.server.common.msg.TbMsg; nodeDescription = "Filter incoming messages by message Originator Type", 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"}, - configDirective = "tbFilterNodeOriginatorTypeConfig", - ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} -) + configDirective = "tbFilterNodeOriginatorTypeConfig") public class TbOriginatorTypeFilterNode implements TbNode { TbOriginatorTypeFilterNodeConfiguration config; 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 a35522dafa..87f76e8807 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 @@ -16,11 +16,15 @@ package org.thingsboard.rule.engine.filter; import lombok.extern.slf4j.Slf4j; +import org.thingsboard.rule.engine.api.EmptyNodeConfiguration; +import org.thingsboard.rule.engine.api.RuleNode; +import org.thingsboard.rule.engine.api.TbContext; +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.rule.engine.api.*; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.plugin.ComponentType; -import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; @Slf4j @@ -32,9 +36,7 @@ import org.thingsboard.server.common.msg.TbMsg; nodeDescription = "Route incoming messages by Message Originator Type", nodeDetails = "Routes messages to chain according to the originator type ('Device', 'Asset', etc.).", uiResources = {"static/rulenode/rulenode-core-config.js"}, - configDirective = "tbNodeEmptyConfig", - ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} -) + configDirective = "tbNodeEmptyConfig") public class TbOriginatorTypeSwitchNode implements TbNode { EmptyNodeConfiguration config; 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 036bfbd762..925ca98ced 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 @@ -26,10 +26,13 @@ import com.google.protobuf.ByteString; import com.google.pubsub.v1.ProjectTopicName; import com.google.pubsub.v1.PubsubMessage; import lombok.extern.slf4j.Slf4j; -import org.thingsboard.rule.engine.api.*; +import org.thingsboard.rule.engine.api.RuleNode; +import org.thingsboard.rule.engine.api.TbContext; +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.plugin.ComponentType; -import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; @@ -37,8 +40,6 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.concurrent.TimeUnit; -import static org.thingsboard.common.util.DonAsynchron.withCallback; - @Slf4j @RuleNode( type = ComponentType.EXTERNAL, @@ -50,8 +51,7 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback; "messageId field can be accessed with metadata.messageId.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodePubSubConfig", - iconUrl = "data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjgiIGhlaWdodD0iMTI4IiB2aWV3Qm94PSIwIDAgMTI4IDEyOCI+Cjx0aXRsZT5DbG91ZCBQdWJTdWI8L3RpdGxlPgo8Zz4KPHBhdGggZD0iTTEyNi40Nyw1OC4xMmwtMjYuMy00NS43NEExMS41NiwxMS41NiwwLDAsMCw5MC4zMSw2LjVIMzcuN2ExMS41NSwxMS41NSwwLDAsMC05Ljg2LDUuODhMMS41Myw1OGExMS40OCwxMS40OCwwLDAsMCwwLDExLjQ0bDI2LjMsNDZhMTEuNzcsMTEuNzcsMCwwLDAsOS44Niw2LjA5SDkwLjNhMTEuNzMsMTEuNzMsMCwwLDAsOS44Ny02LjA2bDI2LjMtNDUuNzRBMTEuNzMsMTEuNzMsMCwwLDAsMTI2LjQ3LDU4LjEyWiIgc3R5bGU9ImZpbGw6ICM3MzViMmYiLz4KPHBhdGggZD0iTTg5LjIyLDQ3Ljc0LDgzLjM2LDQ5bC0xNC42LTE0LjZMNjQuMDksNDMuMSw2MS41NSw1My4ybDQuMjksNC4yOUw1Ny42LDU5LjE4LDQ2LjMsNDcuODhsLTcuNjcsNy4zOEw1Mi43Niw2OS4zN2wtMTUsMTEuOUw3OCwxMjEuNUg5MC4zYTExLjczLDExLjczLDAsMCwwLDkuODctNi4wNmwyMC43Mi0zNloiIHN0eWxlPSJvcGFjaXR5OiAwLjA3MDAwMDAwMDI5ODAyMztpc29sYXRpb246IGlzb2xhdGUiLz4KPHBhdGggZD0iTTgyLjg2LDQ3YTUuMzIsNS4zMiwwLDEsMS0xLjk1LDcuMjdBNS4zMiw1LjMyLDAsMCwxLDgyLjg2LDQ3IiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8cGF0aCBkPSJNMzkuODIsNTYuMThhNS4zMiw1LjMyLDAsMSwxLDcuMjctMS45NSw1LjMyLDUuMzIsMCwwLDEtNy4yNywxLjk1IiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8cGF0aCBkPSJNNjkuMzIsODguODVBNS4zMiw1LjMyLDAsMSwxLDY0LDgzLjUyYTUuMzIsNS4zMiwwLDAsMSw1LjMyLDUuMzIiIHN0eWxlPSJmaWxsOiAjZmZmIi8+CjxnPgo8cGF0aCBkPSJNNjQsNTIuOTRhMTEuMDYsMTEuMDYsMCwwLDEsMi40Ni4yOFYzOS4xNUg2MS41NFY1My4yMkExMS4wNiwxMS4wNiwwLDAsMSw2NCw1Mi45NFoiIHN0eWxlPSJmaWxsOiAjZmZmIi8+CjxwYXRoIGQ9Ik03NC41Nyw2Ny4yNmExMSwxMSwwLDAsMS0yLjQ3LDQuMjVsMTIuMTksNywyLjQ2LTQuMjZaIiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8cGF0aCBkPSJNNTMuNDMsNjcuMjZsLTEyLjE4LDcsMi40Niw0LjI2LDEyLjE5LTdBMTEsMTEsMCwwLDEsNTMuNDMsNjcuMjZaIiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8L2c+CjxwYXRoIGQ9Ik03Mi42LDY0QTguNiw4LjYsMCwxLDEsNjQsNTUuNCw4LjYsOC42LDAsMCwxLDcyLjYsNjQiIHN0eWxlPSJmaWxsOiAjZmZmIi8+CjxwYXRoIGQ9Ik0zOS4xLDcwLjU3YTYuNzYsNi43NiwwLDEsMS0yLjQ3LDkuMjMsNi43Niw2Ljc2LDAsMCwxLDIuNDctOS4yMyIgc3R5bGU9ImZpbGw6ICNmZmYiLz4KPHBhdGggZD0iTTgyLjE0LDgyLjI3YTYuNzYsNi43NiwwLDEsMSw5LjIzLTIuNDcsNi43NSw2Ljc1LDAsMCwxLTkuMjMsMi40NyIgc3R5bGU9ImZpbGw6ICNmZmYiLz4KPHBhdGggZD0iTTcwLjc2LDM5LjE1QTYuNzYsNi43NiwwLDEsMSw2NCwzMi4zOWE2Ljc2LDYuNzYsMCwwLDEsNi43Niw2Ljc2IiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8L2c+Cjwvc3ZnPgo=", - ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} + iconUrl = "data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjgiIGhlaWdodD0iMTI4IiB2aWV3Qm94PSIwIDAgMTI4IDEyOCI+Cjx0aXRsZT5DbG91ZCBQdWJTdWI8L3RpdGxlPgo8Zz4KPHBhdGggZD0iTTEyNi40Nyw1OC4xMmwtMjYuMy00NS43NEExMS41NiwxMS41NiwwLDAsMCw5MC4zMSw2LjVIMzcuN2ExMS41NSwxMS41NSwwLDAsMC05Ljg2LDUuODhMMS41Myw1OGExMS40OCwxMS40OCwwLDAsMCwwLDExLjQ0bDI2LjMsNDZhMTEuNzcsMTEuNzcsMCwwLDAsOS44Niw2LjA5SDkwLjNhMTEuNzMsMTEuNzMsMCwwLDAsOS44Ny02LjA2bDI2LjMtNDUuNzRBMTEuNzMsMTEuNzMsMCwwLDAsMTI2LjQ3LDU4LjEyWiIgc3R5bGU9ImZpbGw6ICM3MzViMmYiLz4KPHBhdGggZD0iTTg5LjIyLDQ3Ljc0LDgzLjM2LDQ5bC0xNC42LTE0LjZMNjQuMDksNDMuMSw2MS41NSw1My4ybDQuMjksNC4yOUw1Ny42LDU5LjE4LDQ2LjMsNDcuODhsLTcuNjcsNy4zOEw1Mi43Niw2OS4zN2wtMTUsMTEuOUw3OCwxMjEuNUg5MC4zYTExLjczLDExLjczLDAsMCwwLDkuODctNi4wNmwyMC43Mi0zNloiIHN0eWxlPSJvcGFjaXR5OiAwLjA3MDAwMDAwMDI5ODAyMztpc29sYXRpb246IGlzb2xhdGUiLz4KPHBhdGggZD0iTTgyLjg2LDQ3YTUuMzIsNS4zMiwwLDEsMS0xLjk1LDcuMjdBNS4zMiw1LjMyLDAsMCwxLDgyLjg2LDQ3IiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8cGF0aCBkPSJNMzkuODIsNTYuMThhNS4zMiw1LjMyLDAsMSwxLDcuMjctMS45NSw1LjMyLDUuMzIsMCwwLDEtNy4yNywxLjk1IiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8cGF0aCBkPSJNNjkuMzIsODguODVBNS4zMiw1LjMyLDAsMSwxLDY0LDgzLjUyYTUuMzIsNS4zMiwwLDAsMSw1LjMyLDUuMzIiIHN0eWxlPSJmaWxsOiAjZmZmIi8+CjxnPgo8cGF0aCBkPSJNNjQsNTIuOTRhMTEuMDYsMTEuMDYsMCwwLDEsMi40Ni4yOFYzOS4xNUg2MS41NFY1My4yMkExMS4wNiwxMS4wNiwwLDAsMSw2NCw1Mi45NFoiIHN0eWxlPSJmaWxsOiAjZmZmIi8+CjxwYXRoIGQ9Ik03NC41Nyw2Ny4yNmExMSwxMSwwLDAsMS0yLjQ3LDQuMjVsMTIuMTksNywyLjQ2LTQuMjZaIiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8cGF0aCBkPSJNNTMuNDMsNjcuMjZsLTEyLjE4LDcsMi40Niw0LjI2LDEyLjE5LTdBMTEsMTEsMCwwLDEsNTMuNDMsNjcuMjZaIiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8L2c+CjxwYXRoIGQ9Ik03Mi42LDY0QTguNiw4LjYsMCwxLDEsNjQsNTUuNCw4LjYsOC42LDAsMCwxLDcyLjYsNjQiIHN0eWxlPSJmaWxsOiAjZmZmIi8+CjxwYXRoIGQ9Ik0zOS4xLDcwLjU3YTYuNzYsNi43NiwwLDEsMS0yLjQ3LDkuMjMsNi43Niw2Ljc2LDAsMCwxLDIuNDctOS4yMyIgc3R5bGU9ImZpbGw6ICNmZmYiLz4KPHBhdGggZD0iTTgyLjE0LDgyLjI3YTYuNzYsNi43NiwwLDEsMSw5LjIzLTIuNDcsNi43NSw2Ljc1LDAsMCwxLTkuMjMsMi40NyIgc3R5bGU9ImZpbGw6ICNmZmYiLz4KPHBhdGggZD0iTTcwLjc2LDM5LjE1QTYuNzYsNi43NiwwLDEsMSw2NCwzMi4zOWE2Ljc2LDYuNzYsMCwwLDEsNi43Niw2Ljc2IiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8L2c+Cjwvc3ZnPgo=" ) 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 bb2e0bbca9..832a86b5cb 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 @@ -28,7 +28,6 @@ import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.StringDataEntry; import org.thingsboard.server.common.data.plugin.ComponentType; -import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; import java.util.Collections; @@ -52,8 +51,7 @@ import java.util.concurrent.TimeoutException; nodeDescription = "Produces incoming messages using GPS based geofencing", 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.CORE, RuleChainType.EDGE} + configDirective = "tbActionNodeGpsGeofencingConfig" ) 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 a40f416f92..fe5f1448c5 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 @@ -15,32 +15,13 @@ */ package org.thingsboard.rule.engine.geo; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; import lombok.extern.slf4j.Slf4j; -import org.locationtech.spatial4j.context.jts.JtsSpatialContext; -import org.locationtech.spatial4j.context.jts.JtsSpatialContextFactory; -import org.locationtech.spatial4j.shape.Point; -import org.locationtech.spatial4j.shape.Shape; -import org.locationtech.spatial4j.shape.ShapeFactory; -import org.locationtech.spatial4j.shape.SpatialRelation; -import org.springframework.util.StringUtils; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; -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.rule.engine.filter.TbMsgTypeFilterNodeConfiguration; import org.thingsboard.server.common.data.plugin.ComponentType; -import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; -import java.util.Collections; -import java.util.List; - /** * Created by ashvayka on 19.01.18. */ @@ -53,9 +34,7 @@ import java.util.List; nodeDescription = "Filter incoming messages by GPS based geofencing", 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.CORE, RuleChainType.EDGE} -) + configDirective = "tbFilterNodeGpsGeofencingConfig") public class TbGpsGeofencingFilterNode extends AbstractGeofencingNode { @Override 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 2945b09ab8..967d8e4a50 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 @@ -33,7 +33,6 @@ import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.TbRelationTypes; import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.server.common.data.plugin.ComponentType; -import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; @@ -52,8 +51,7 @@ import java.util.Properties; " from the Kafka in the Message Metadata. For example partition field can be accessed with metadata.partition.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeKafkaConfig", - iconUrl = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTUzOCIgaGVpZ2h0PSIyNTAwIiB2aWV3Qm94PSIwIDAgMjU2IDQxNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCI+PHBhdGggZD0iTTIwMS44MTYgMjMwLjIxNmMtMTYuMTg2IDAtMzAuNjk3IDcuMTcxLTQwLjYzNCAxOC40NjFsLTI1LjQ2My0xOC4wMjZjMi43MDMtNy40NDIgNC4yNTUtMTUuNDMzIDQuMjU1LTIzLjc5NyAwLTguMjE5LTEuNDk4LTE2LjA3Ni00LjExMi0yMy40MDhsMjUuNDA2LTE3LjgzNWM5LjkzNiAxMS4yMzMgMjQuNDA5IDE4LjM2NSA0MC41NDggMTguMzY1IDI5Ljg3NSAwIDU0LjE4NC0yNC4zMDUgNTQuMTg0LTU0LjE4NCAwLTI5Ljg3OS0yNC4zMDktNTQuMTg0LTU0LjE4NC01NC4xODQtMjkuODc1IDAtNTQuMTg0IDI0LjMwNS01NC4xODQgNTQuMTg0IDAgNS4zNDguODA4IDEwLjUwNSAyLjI1OCAxNS4zODlsLTI1LjQyMyAxNy44NDRjLTEwLjYyLTEzLjE3NS0yNS45MTEtMjIuMzc0LTQzLjMzMy0yNS4xODJ2LTMwLjY0YzI0LjU0NC01LjE1NSA0My4wMzctMjYuOTYyIDQzLjAzNy01My4wMTlDMTI0LjE3MSAyNC4zMDUgOTkuODYyIDAgNjkuOTg3IDAgNDAuMTEyIDAgMTUuODAzIDI0LjMwNSAxNS44MDMgNTQuMTg0YzAgMjUuNzA4IDE4LjAxNCA0Ny4yNDYgNDIuMDY3IDUyLjc2OXYzMS4wMzhDMjUuMDQ0IDE0My43NTMgMCAxNzIuNDAxIDAgMjA2Ljg1NGMwIDM0LjYyMSAyNS4yOTIgNjMuMzc0IDU4LjM1NSA2OC45NHYzMi43NzRjLTI0LjI5OSA1LjM0MS00Mi41NTIgMjcuMDExLTQyLjU1MiA1Mi44OTQgMCAyOS44NzkgMjQuMzA5IDU0LjE4NCA1NC4xODQgNTQuMTg0IDI5Ljg3NSAwIDU0LjE4NC0yNC4zMDUgNTQuMTg0LTU0LjE4NCAwLTI1Ljg4My0xOC4yNTMtNDcuNTUzLTQyLjU1Mi01Mi44OTR2LTMyLjc3NWE2OS45NjUgNjkuOTY1IDAgMCAwIDQyLjYtMjQuNzc2bDI1LjYzMyAxOC4xNDNjLTEuNDIzIDQuODQtMi4yMiA5Ljk0Ni0yLjIyIDE1LjI0IDAgMjkuODc5IDI0LjMwOSA1NC4xODQgNTQuMTg0IDU0LjE4NCAyOS44NzUgMCA1NC4xODQtMjQuMzA1IDU0LjE4NC01NC4xODQgMC0yOS44NzktMjQuMzA5LTU0LjE4NC01NC4xODQtNTQuMTg0em0wLTEyNi42OTVjMTQuNDg3IDAgMjYuMjcgMTEuNzg4IDI2LjI3IDI2LjI3MXMtMTEuNzgzIDI2LjI3LTI2LjI3IDI2LjI3LTI2LjI3LTExLjc4Ny0yNi4yNy0yNi4yN2MwLTE0LjQ4MyAxMS43ODMtMjYuMjcxIDI2LjI3LTI2LjI3MXptLTE1OC4xLTQ5LjMzN2MwLTE0LjQ4MyAxMS43ODQtMjYuMjcgMjYuMjcxLTI2LjI3czI2LjI3IDExLjc4NyAyNi4yNyAyNi4yN2MwIDE0LjQ4My0xMS43ODMgMjYuMjctMjYuMjcgMjYuMjdzLTI2LjI3MS0xMS43ODctMjYuMjcxLTI2LjI3em01Mi41NDEgMzA3LjI3OGMwIDE0LjQ4My0xMS43ODMgMjYuMjctMjYuMjcgMjYuMjdzLTI2LjI3MS0xMS43ODctMjYuMjcxLTI2LjI3YzAtMTQuNDgzIDExLjc4NC0yNi4yNyAyNi4yNzEtMjYuMjdzMjYuMjcgMTEuNzg3IDI2LjI3IDI2LjI3em0tMjYuMjcyLTExNy45N2MtMjAuMjA1IDAtMzYuNjQyLTE2LjQzNC0zNi42NDItMzYuNjM4IDAtMjAuMjA1IDE2LjQzNy0zNi42NDIgMzYuNjQyLTM2LjY0MiAyMC4yMDQgMCAzNi42NDEgMTYuNDM3IDM2LjY0MSAzNi42NDIgMCAyMC4yMDQtMTYuNDM3IDM2LjYzOC0zNi42NDEgMzYuNjM4em0xMzEuODMxIDY3LjE3OWMtMTQuNDg3IDAtMjYuMjctMTEuNzg4LTI2LjI3LTI2LjI3MXMxMS43ODMtMjYuMjcgMjYuMjctMjYuMjcgMjYuMjcgMTEuNzg3IDI2LjI3IDI2LjI3YzAgMTQuNDgzLTExLjc4MyAyNi4yNzEtMjYuMjcgMjYuMjcxeiIvPjwvc3ZnPg==", - ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} + iconUrl = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTUzOCIgaGVpZ2h0PSIyNTAwIiB2aWV3Qm94PSIwIDAgMjU2IDQxNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCI+PHBhdGggZD0iTTIwMS44MTYgMjMwLjIxNmMtMTYuMTg2IDAtMzAuNjk3IDcuMTcxLTQwLjYzNCAxOC40NjFsLTI1LjQ2My0xOC4wMjZjMi43MDMtNy40NDIgNC4yNTUtMTUuNDMzIDQuMjU1LTIzLjc5NyAwLTguMjE5LTEuNDk4LTE2LjA3Ni00LjExMi0yMy40MDhsMjUuNDA2LTE3LjgzNWM5LjkzNiAxMS4yMzMgMjQuNDA5IDE4LjM2NSA0MC41NDggMTguMzY1IDI5Ljg3NSAwIDU0LjE4NC0yNC4zMDUgNTQuMTg0LTU0LjE4NCAwLTI5Ljg3OS0yNC4zMDktNTQuMTg0LTU0LjE4NC01NC4xODQtMjkuODc1IDAtNTQuMTg0IDI0LjMwNS01NC4xODQgNTQuMTg0IDAgNS4zNDguODA4IDEwLjUwNSAyLjI1OCAxNS4zODlsLTI1LjQyMyAxNy44NDRjLTEwLjYyLTEzLjE3NS0yNS45MTEtMjIuMzc0LTQzLjMzMy0yNS4xODJ2LTMwLjY0YzI0LjU0NC01LjE1NSA0My4wMzctMjYuOTYyIDQzLjAzNy01My4wMTlDMTI0LjE3MSAyNC4zMDUgOTkuODYyIDAgNjkuOTg3IDAgNDAuMTEyIDAgMTUuODAzIDI0LjMwNSAxNS44MDMgNTQuMTg0YzAgMjUuNzA4IDE4LjAxNCA0Ny4yNDYgNDIuMDY3IDUyLjc2OXYzMS4wMzhDMjUuMDQ0IDE0My43NTMgMCAxNzIuNDAxIDAgMjA2Ljg1NGMwIDM0LjYyMSAyNS4yOTIgNjMuMzc0IDU4LjM1NSA2OC45NHYzMi43NzRjLTI0LjI5OSA1LjM0MS00Mi41NTIgMjcuMDExLTQyLjU1MiA1Mi44OTQgMCAyOS44NzkgMjQuMzA5IDU0LjE4NCA1NC4xODQgNTQuMTg0IDI5Ljg3NSAwIDU0LjE4NC0yNC4zMDUgNTQuMTg0LTU0LjE4NCAwLTI1Ljg4My0xOC4yNTMtNDcuNTUzLTQyLjU1Mi01Mi44OTR2LTMyLjc3NWE2OS45NjUgNjkuOTY1IDAgMCAwIDQyLjYtMjQuNzc2bDI1LjYzMyAxOC4xNDNjLTEuNDIzIDQuODQtMi4yMiA5Ljk0Ni0yLjIyIDE1LjI0IDAgMjkuODc5IDI0LjMwOSA1NC4xODQgNTQuMTg0IDU0LjE4NCAyOS44NzUgMCA1NC4xODQtMjQuMzA1IDU0LjE4NC01NC4xODQgMC0yOS44NzktMjQuMzA5LTU0LjE4NC01NC4xODQtNTQuMTg0em0wLTEyNi42OTVjMTQuNDg3IDAgMjYuMjcgMTEuNzg4IDI2LjI3IDI2LjI3MXMtMTEuNzgzIDI2LjI3LTI2LjI3IDI2LjI3LTI2LjI3LTExLjc4Ny0yNi4yNy0yNi4yN2MwLTE0LjQ4MyAxMS43ODMtMjYuMjcxIDI2LjI3LTI2LjI3MXptLTE1OC4xLTQ5LjMzN2MwLTE0LjQ4MyAxMS43ODQtMjYuMjcgMjYuMjcxLTI2LjI3czI2LjI3IDExLjc4NyAyNi4yNyAyNi4yN2MwIDE0LjQ4My0xMS43ODMgMjYuMjctMjYuMjcgMjYuMjdzLTI2LjI3MS0xMS43ODctMjYuMjcxLTI2LjI3em01Mi41NDEgMzA3LjI3OGMwIDE0LjQ4My0xMS43ODMgMjYuMjctMjYuMjcgMjYuMjdzLTI2LjI3MS0xMS43ODctMjYuMjcxLTI2LjI3YzAtMTQuNDgzIDExLjc4NC0yNi4yNyAyNi4yNzEtMjYuMjdzMjYuMjcgMTEuNzg3IDI2LjI3IDI2LjI3em0tMjYuMjcyLTExNy45N2MtMjAuMjA1IDAtMzYuNjQyLTE2LjQzNC0zNi42NDItMzYuNjM4IDAtMjAuMjA1IDE2LjQzNy0zNi42NDIgMzYuNjQyLTM2LjY0MiAyMC4yMDQgMCAzNi42NDEgMTYuNDM3IDM2LjY0MSAzNi42NDIgMCAyMC4yMDQtMTYuNDM3IDM2LjYzOC0zNi42NDEgMzYuNjM4em0xMzEuODMxIDY3LjE3OWMtMTQuNDg3IDAtMjYuMjctMTEuNzg4LTI2LjI3LTI2LjI3MXMxMS43ODMtMjYuMjcgMjYuMjctMjYuMjcgMjYuMjcgMTEuNzg3IDI2LjI3IDI2LjI3YzAgMTQuNDgzLTExLjc4MyAyNi4yNzEtMjYuMjcgMjYuMjcxeiIvPjwvc3ZnPg==" ) 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 1a4fb244b3..b57394ca4e 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 @@ -19,10 +19,13 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.util.StringUtils; +import org.thingsboard.rule.engine.api.RuleNode; +import org.thingsboard.rule.engine.api.TbContext; +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.rule.engine.api.*; import org.thingsboard.server.common.data.plugin.ComponentType; -import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; @@ -41,8 +44,7 @@ import static org.thingsboard.rule.engine.mail.TbSendEmailNode.SEND_EMAIL_TYPE; "Set 'SEND_EMAIL' output message type.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbTransformationNodeToEmailConfig", - icon = "email", - ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} + icon = "email" ) 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 043b5008c4..3ca2d5dbab 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 @@ -27,7 +27,6 @@ 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.plugin.ComponentType; -import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; import javax.mail.internet.MimeMessage; @@ -47,8 +46,7 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback; "with to Email Node using Successful chain.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeSendEmailConfig", - icon = "send", - ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} + icon = "send" ) 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 92aca60332..e56f38ce19 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 @@ -18,14 +18,13 @@ package org.thingsboard.rule.engine.metadata; 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.util.TbNodeUtils; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; 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.id.EntityId; import org.thingsboard.server.common.data.plugin.ComponentType; -import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; /** @@ -41,9 +40,7 @@ import org.thingsboard.server.common.msg.TbMsg; "To access those attributes in other nodes this template can be used " + "metadata.cs_temperature or metadata.shared_limit ", uiResources = {"static/rulenode/rulenode-core-config.js"}, - configDirective = "tbEnrichmentNodeOriginatorAttributesConfig", - ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} -) + configDirective = "tbEnrichmentNodeOriginatorAttributesConfig") public class TbGetAttributesNode extends TbAbstractGetAttributesNode { @Override 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 77ae6acccc..f1c2b57fe6 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 @@ -22,7 +22,6 @@ import org.thingsboard.rule.engine.util.EntitiesCustomerIdAsyncLoader; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.plugin.ComponentType; -import org.thingsboard.server.common.data.rule.RuleChainType; @RuleNode( type = ComponentType.ENRICHMENT, @@ -34,9 +33,7 @@ import org.thingsboard.server.common.data.rule.RuleChainType; "To access those attributes in other nodes this template can be used " + "metadata.temperature.", uiResources = {"static/rulenode/rulenode-core-config.js"}, - configDirective = "tbEnrichmentNodeCustomerAttributesConfig", - ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} -) + configDirective = "tbEnrichmentNodeCustomerAttributesConfig") public class TbGetCustomerAttributeNode extends TbEntityGetAttrNode { @Override 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 93a03498d7..7659facf63 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 @@ -31,7 +31,6 @@ import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.plugin.ComponentType; -import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; @Slf4j @@ -43,9 +42,7 @@ import org.thingsboard.server.common.msg.TbMsg; "Note: only Device, Asset, and Entity View type are allowed.

" + "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.CORE, RuleChainType.EDGE} -) + configDirective = "tbEnrichmentNodeEntityDetailsConfig") public class TbGetCustomerDetailsNode extends TbAbstractGetEntityDetailsNode { private static final String CUSTOMER_PREFIX = "customer_"; 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 af6cbf9265..b3c1bbbae3 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 @@ -17,16 +17,14 @@ package org.thingsboard.rule.engine.metadata; import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; -import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; 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.rule.engine.util.EntitiesRelatedDeviceIdAsyncLoader; import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.plugin.ComponentType; -import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; @Slf4j @@ -39,9 +37,7 @@ import org.thingsboard.server.common.msg.TbMsg; "To access those attributes in other nodes this template can be used " + "metadata.cs_temperature or metadata.shared_limit ", uiResources = {"static/rulenode/rulenode-core-config.js"}, - configDirective = "tbEnrichmentNodeDeviceAttributesConfig", - ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} -) + configDirective = "tbEnrichmentNodeDeviceAttributesConfig") public class TbGetDeviceAttrNode extends TbAbstractGetAttributesNode { @Override 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 6f240d2df6..fc2c574d5d 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 @@ -28,11 +28,9 @@ import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.rule.engine.util.EntitiesFieldsAsyncLoader; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.plugin.ComponentType; -import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; import static org.thingsboard.common.util.DonAsynchron.withCallback; -import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; /** * Created by ashvayka on 19.01.18. @@ -44,9 +42,7 @@ import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; nodeDescription = "Add Message Originator fields values into Message Metadata", 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.CORE, RuleChainType.EDGE} -) + configDirective = "tbEnrichmentNodeOriginatorFieldsConfig") public class TbGetOriginatorFieldsNode implements TbNode { private TbGetOriginatorFieldsConfiguration config; 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 9603c8afe4..320833d311 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 @@ -16,13 +16,14 @@ package org.thingsboard.rule.engine.metadata; import com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.rule.engine.api.RuleNode; +import org.thingsboard.rule.engine.api.TbContext; +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.rule.engine.api.*; import org.thingsboard.rule.engine.util.EntitiesRelatedEntityIdAsyncLoader; - import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.plugin.ComponentType; -import org.thingsboard.server.common.data.rule.RuleChainType; @RuleNode( type = ComponentType.ENRICHMENT, @@ -36,9 +37,7 @@ import org.thingsboard.server.common.data.rule.RuleChainType; "To access those attributes in other nodes this template can be used " + "metadata.temperature.", uiResources = {"static/rulenode/rulenode-core-config.js"}, - configDirective = "tbEnrichmentNodeRelatedAttributesConfig", - ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} -) + configDirective = "tbEnrichmentNodeRelatedAttributesConfig") 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 5d56a6faf8..245728b0ca 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 @@ -38,7 +38,6 @@ import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; import org.thingsboard.server.common.data.kv.ReadTsKvQuery; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.plugin.ComponentType; -import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; import java.io.IOException; @@ -47,7 +46,6 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; import static org.thingsboard.rule.engine.metadata.TbGetTelemetryNodeConfiguration.FETCH_MODE_ALL; import static org.thingsboard.rule.engine.metadata.TbGetTelemetryNodeConfiguration.FETCH_MODE_FIRST; import static org.thingsboard.rule.engine.metadata.TbGetTelemetryNodeConfiguration.MAX_FETCH_SIZE; @@ -67,9 +65,7 @@ import static org.thingsboard.server.common.data.kv.Aggregation.NONE; "Also, the rule node allows you to select telemetry sampling order: ASC or DESC.
" + "Note: The maximum size of the fetched array is 1000 records.\n ", uiResources = {"static/rulenode/rulenode-core-config.js"}, - configDirective = "tbEnrichmentNodeGetTelemetryFromDatabase", - ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} -) + configDirective = "tbEnrichmentNodeGetTelemetryFromDatabase") public class TbGetTelemetryNode implements TbNode { private static final String DESC_ORDER = "DESC"; 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 979588035d..e5e4cb5667 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 @@ -23,7 +23,6 @@ import org.thingsboard.rule.engine.util.EntitiesTenantIdAsyncLoader; 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.rule.RuleChainType; @Slf4j @RuleNode( @@ -36,9 +35,7 @@ import org.thingsboard.server.common.data.rule.RuleChainType; "To access those attributes in other nodes this template can be used " + "metadata.temperature.", uiResources = {"static/rulenode/rulenode-core-config.js"}, - configDirective = "tbEnrichmentNodeTenantAttributesConfig", - ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} -) + configDirective = "tbEnrichmentNodeTenantAttributesConfig") public class TbGetTenantAttributeNode extends TbEntityGetAttrNode { @Override 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 cf4bcaf064..f7ba2a8597 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 @@ -26,7 +26,6 @@ import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.server.common.data.ContactBased; import org.thingsboard.server.common.data.plugin.ComponentType; -import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; @Slf4j @@ -38,9 +37,7 @@ import org.thingsboard.server.common.msg.TbMsg; "Note: only Device, Asset, and Entity View type are allowed.

" + "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.CORE, RuleChainType.EDGE} -) + configDirective = "tbEnrichmentNodeEntityDetailsConfig") public class TbGetTenantDetailsNode extends TbAbstractGetEntityDetailsNode { private static final String TENANT_PREFIX = "tenant_"; 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 0e541fc7f8..d317d79731 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 @@ -21,14 +21,17 @@ import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContextBuilder; import io.netty.util.concurrent.Future; import lombok.extern.slf4j.Slf4j; +import org.springframework.util.StringUtils; import org.thingsboard.mqtt.MqttClient; import org.thingsboard.mqtt.MqttClientConfig; import org.thingsboard.mqtt.MqttConnectResult; -import org.springframework.util.StringUtils; +import org.thingsboard.rule.engine.api.RuleNode; +import org.thingsboard.rule.engine.api.TbContext; +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.rule.engine.api.*; import org.thingsboard.server.common.data.plugin.ComponentType; -import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; @@ -47,8 +50,7 @@ import java.util.concurrent.TimeoutException; nodeDetails = "Will publish message payload to the MQTT broker with QoS AT_LEAST_ONCE.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeMqttConfig", - icon = "call_split", - ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} + icon = "call_split" ) 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 78ac75a642..141a70022c 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 @@ -16,18 +16,24 @@ package org.thingsboard.rule.engine.rabbitmq; import com.google.common.util.concurrent.ListenableFuture; -import com.rabbitmq.client.*; +import com.rabbitmq.client.AMQP; +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.Connection; +import com.rabbitmq.client.ConnectionFactory; +import com.rabbitmq.client.MessageProperties; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; +import org.thingsboard.rule.engine.api.RuleNode; +import org.thingsboard.rule.engine.api.TbContext; +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.rule.engine.api.*; import org.thingsboard.server.common.data.plugin.ComponentType; -import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; import java.nio.charset.Charset; -import java.util.concurrent.ExecutionException; import static org.thingsboard.common.util.DonAsynchron.withCallback; @@ -40,8 +46,7 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback; nodeDetails = "Will publish message payload to RabbitMQ queue.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeRabbitMqConfig", - iconUrl = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbDpzcGFjZT0icHJlc2VydmUiIHZlcnNpb249IjEuMSIgeT0iMHB4IiB4PSIwcHgiIHZpZXdCb3g9IjAgMCAxMDAwIDEwMDAiPjxwYXRoIHN0cm9rZS13aWR0aD0iLjg0OTU2IiBkPSJtODYwLjQ3IDQxNi4zMmgtMjYyLjAxYy0xMi45MTMgMC0yMy42MTgtMTAuNzA0LTIzLjYxOC0yMy42MTh2LTI3Mi43MWMwLTIwLjMwNS0xNi4yMjctMzYuMjc2LTM2LjI3Ni0zNi4yNzZoLTkzLjc5MmMtMjAuMzA1IDAtMzYuMjc2IDE2LjIyNy0zNi4yNzYgMzYuMjc2djI3MC44NGMtMC4yNTQ4NyAxNC4xMDMtMTEuNDY5IDI1LjU3Mi0yNS43NDIgMjUuNTcybC04NS42MzYgMC42Nzk2NWMtMTQuMTAzIDAtMjUuNTcyLTExLjQ2OS0yNS41NzItMjUuNTcybDAuNjc5NjUtMjcxLjUyYzAtMjAuMzA1LTE2LjIyNy0zNi4yNzYtMzYuMjc2LTM2LjI3NmgtOTMuNTM3Yy0yMC4zMDUgMC0zNi4yNzYgMTYuMjI3LTM2LjI3NiAzNi4yNzZ2NzYzLjg0YzAgMTguMDk2IDE0Ljc4MiAzMi40NTMgMzIuNDUzIDMyLjQ1M2g3MjIuODFjMTguMDk2IDAgMzIuNDUzLTE0Ljc4MiAzMi40NTMtMzIuNDUzdi00MzUuMzFjLTEuMTg5NC0xOC4xODEtMTUuMjkyLTMyLjE5OC0zMy4zODgtMzIuMTk4em0tMTIyLjY4IDI4Ny4wN2MwIDIzLjYxOC0xOC44NiA0Mi40NzgtNDIuNDc4IDQyLjQ3OGgtNzMuOTk3Yy0yMy42MTggMC00Mi40NzgtMTguODYtNDIuNDc4LTQyLjQ3OHYtNzQuMjUyYzAtMjMuNjE4IDE4Ljg2LTQyLjQ3OCA0Mi40NzgtNDIuNDc4aDczLjk5N2MyMy42MTggMCA0Mi40NzggMTguODYgNDIuNDc4IDQyLjQ3OHoiLz48L3N2Zz4=", - ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} + iconUrl = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbDpzcGFjZT0icHJlc2VydmUiIHZlcnNpb249IjEuMSIgeT0iMHB4IiB4PSIwcHgiIHZpZXdCb3g9IjAgMCAxMDAwIDEwMDAiPjxwYXRoIHN0cm9rZS13aWR0aD0iLjg0OTU2IiBkPSJtODYwLjQ3IDQxNi4zMmgtMjYyLjAxYy0xMi45MTMgMC0yMy42MTgtMTAuNzA0LTIzLjYxOC0yMy42MTh2LTI3Mi43MWMwLTIwLjMwNS0xNi4yMjctMzYuMjc2LTM2LjI3Ni0zNi4yNzZoLTkzLjc5MmMtMjAuMzA1IDAtMzYuMjc2IDE2LjIyNy0zNi4yNzYgMzYuMjc2djI3MC44NGMtMC4yNTQ4NyAxNC4xMDMtMTEuNDY5IDI1LjU3Mi0yNS43NDIgMjUuNTcybC04NS42MzYgMC42Nzk2NWMtMTQuMTAzIDAtMjUuNTcyLTExLjQ2OS0yNS41NzItMjUuNTcybDAuNjc5NjUtMjcxLjUyYzAtMjAuMzA1LTE2LjIyNy0zNi4yNzYtMzYuMjc2LTM2LjI3NmgtOTMuNTM3Yy0yMC4zMDUgMC0zNi4yNzYgMTYuMjI3LTM2LjI3NiAzNi4yNzZ2NzYzLjg0YzAgMTguMDk2IDE0Ljc4MiAzMi40NTMgMzIuNDUzIDMyLjQ1M2g3MjIuODFjMTguMDk2IDAgMzIuNDUzLTE0Ljc4MiAzMi40NTMtMzIuNDUzdi00MzUuMzFjLTEuMTg5NC0xOC4xODEtMTUuMjkyLTMyLjE5OC0zMy4zODgtMzIuMTk4em0tMTIyLjY4IDI4Ny4wN2MwIDIzLjYxOC0xOC44NiA0Mi40NzgtNDIuNDc4IDQyLjQ3OGgtNzMuOTk3Yy0yMy42MTggMC00Mi40NzgtMTguODYtNDIuNDc4LTQyLjQ3OHYtNzQuMjUyYzAtMjMuNjE4IDE4Ljg2LTQyLjQ3OCA0Mi40NzgtNDIuNDc4aDczLjk5N2MyMy42MTggMCA0Mi40NzggMTguODYgNDIuNDc4IDQyLjQ3OHoiLz48L3N2Zz4=" ) 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 ca3136ec3a..773fe4f9bf 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 @@ -23,7 +23,6 @@ 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.plugin.ComponentType; -import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; @Slf4j @@ -42,8 +41,7 @@ import org.thingsboard.server.common.msg.TbMsg; "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+", - ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} + iconUrl = "data:image/svg+xml;base64,PHN2ZyBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbDpzcGFjZT0icHJlc2VydmUiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiB2ZXJzaW9uPSIxLjEiIHk9IjBweCIgeD0iMHB4Ij48ZyB0cmFuc2Zvcm09Im1hdHJpeCguOTQ5NzUgMCAwIC45NDk3NSAxNy4xMiAyNi40OTIpIj48cGF0aCBkPSJtMTY5LjExIDEwOC41NGMtOS45MDY2IDAuMDczNC0xOS4wMTQgNi41NzI0LTIyLjAxNCAxNi40NjlsLTY5Ljk5MyAyMzEuMDhjLTMuNjkwNCAxMi4xODEgMy4yODkyIDI1LjIyIDE1LjQ2OSAyOC45MSAyLjIyNTkgMC42NzQ4MSA0LjQ5NjkgMSA2LjcyODUgMSA5Ljk3MjEgMCAxOS4xNjUtNi41MTUzIDIyLjE4Mi0xNi40NjdhNi41MjI0IDYuNTIyNCAwIDAgMCAwLjAwMiAtMC4wMDJsNjkuOTktMjMxLjA3YTYuNTIyNCA2LjUyMjQgMCAwIDAgMCAtMC4wMDJjMy42ODU1LTEyLjE4MS0zLjI4Ny0yNS4yMjUtMTUuNDcxLTI4LjkxMi0yLjI4MjUtMC42OTE0NS00LjYxMTYtMS4wMTY5LTYuODk4NC0xem04NC45ODggMGMtOS45MDQ4IDAuMDczNC0xOS4wMTggNi41Njc1LTIyLjAxOCAxNi40NjlsLTY5Ljk4NiAyMzEuMDhjLTMuNjg5OCAxMi4xNzkgMy4yODUzIDI1LjIxNyAxNS40NjUgMjguOTA4IDIuMjI5NyAwLjY3NjQ3IDQuNTAwOCAxLjAwMiA2LjczMjQgMS4wMDIgOS45NzIxIDAgMTkuMTY1LTYuNTE1MyAyMi4xODItMTYuNDY3YTYuNTIyNCA2LjUyMjQgMCAwIDAgMC4wMDIgLTAuMDAybDY5Ljk4OC0yMzEuMDdjMy42OTA4LTEyLjE4MS0zLjI4NTItMjUuMjIzLTE1LjQ2Ny0yOC45MTItMi4yODE0LTAuNjkyMzEtNC42MTA4LTEuMDE4OS02Ljg5ODQtMS4wMDJ6bS0yMTcuMjkgNDIuMjNjLTEyLjcyOS0wLjAwMDg3LTIzLjE4OCAxMC40NTYtMjMuMTg4IDIzLjE4NiAwLjAwMSAxMi43MjggMTAuNDU5IDIzLjE4NiAyMy4xODggMjMuMTg2IDEyLjcyNy0wLjAwMSAyMy4xODMtMTAuNDU5IDIzLjE4NC0yMy4xODYgMC4wMDA4NzYtMTIuNzI4LTEwLjQ1Ni0yMy4xODUtMjMuMTg0LTIzLjE4NnptMCAxNDYuNjRjLTEyLjcyNy0wLjAwMDg3LTIzLjE4NiAxMC40NTUtMjMuMTg4IDIzLjE4NC0wLjAwMDg3MyAxMi43MjkgMTAuNDU4IDIzLjE4OCAyMy4xODggMjMuMTg4IDEyLjcyOC0wLjAwMSAyMy4xODQtMTAuNDYgMjMuMTg0LTIzLjE4OC0wLjAwMS0xMi43MjYtMTAuNDU3LTIzLjE4My0yMy4xODQtMjMuMTg0em0yNzAuNzkgNDIuMjExYy0xMi43MjcgMC0yMy4xODQgMTAuNDU3LTIzLjE4NCAyMy4xODRzMTAuNDU1IDIzLjE4OCAyMy4xODQgMjMuMTg4aDE1NC45OGMxMi43MjkgMCAyMy4xODYtMTAuNDYgMjMuMTg2LTIzLjE4OCAwLjAwMS0xMi43MjgtMTAuNDU4LTIzLjE4NC0yMy4xODYtMjMuMTg0eiIgdHJhbnNmb3JtPSJtYXRyaXgoMS4wMzc2IDAgMCAxLjAzNzYgLTcuNTY3NiAtMTQuOTI1KSIgc3Ryb2tlLXdpZHRoPSIxLjI2OTMiLz48L2c+PC9zdmc+" ) 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 2fc84e1c32..fea7356b32 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 @@ -17,16 +17,14 @@ package org.thingsboard.rule.engine.rpc; import lombok.extern.slf4j.Slf4j; import org.springframework.util.StringUtils; -import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; 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.EntityType; -import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.plugin.ComponentType; -import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; import java.util.UUID; @@ -40,8 +38,7 @@ import java.util.UUID; nodeDetails = "Expects messages with any message type. Will forward message body to the device.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeRpcReplyConfig", - icon = "call_merge", - ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} + icon = "call_merge" ) 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 eff8c1fa62..e91d509b4f 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 @@ -16,13 +16,16 @@ package org.thingsboard.rule.engine.rpc; import com.datastax.oss.driver.api.core.uuid.Uuids; +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.gson.Gson; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import lombok.extern.slf4j.Slf4j; import org.springframework.util.StringUtils; -import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.rule.engine.api.RuleEngineDeviceRpcRequest; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; @@ -30,13 +33,21 @@ 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.TbRelationTypes; +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.DeviceId; +import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.plugin.ComponentType; -import org.thingsboard.server.common.data.rule.RuleChainType; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.msg.TbMsg; +import javax.annotation.Nullable; +import java.util.List; import java.util.Random; import java.util.UUID; import java.util.concurrent.TimeUnit; @@ -51,11 +62,11 @@ import java.util.concurrent.TimeUnit; "If the RPC call request is originated by REST API call from user, will forward the response to user immediately.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeRpcRequestConfig", - icon = "call_made", - ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} + icon = "call_made" ) public class TbSendRPCRequestNode implements TbNode { + private static final ObjectMapper json = new ObjectMapper(); private Random random = new Random(); private Gson gson = new Gson(); private JsonParser jsonParser = new JsonParser(); @@ -112,19 +123,59 @@ public class TbSendRPCRequestNode implements TbNode { .restApiCall(restApiCall) .build(); - ctx.getRpcService().sendRpcRequestToDevice(request, ruleEngineDeviceRpcResponse -> { - if (!ruleEngineDeviceRpcResponse.getError().isPresent()) { - TbMsg next = ctx.newMsg(msg.getQueueName(), msg.getType(), msg.getOriginator(), msg.getMetaData(), ruleEngineDeviceRpcResponse.getResponse().orElse("{}")); - ctx.enqueueForTellNext(next, TbRelationTypes.SUCCESS); - } else { - TbMsg next = ctx.newMsg(msg.getQueueName(), msg.getType(), msg.getOriginator(), msg.getMetaData(), wrap("error", ruleEngineDeviceRpcResponse.getError().get().name())); - ctx.tellFailure(next, new RuntimeException(ruleEngineDeviceRpcResponse.getError().get().name())); - } - }); + EdgeId edgeId = findRelatedEdgeId(ctx, msg); + if (edgeId != null) { + sendRpcRequestToEdgeDevice(ctx, msg, edgeId, request); + } else { + ctx.getRpcService().sendRpcRequestToDevice(request, ruleEngineDeviceRpcResponse -> { + if (!ruleEngineDeviceRpcResponse.getError().isPresent()) { + TbMsg next = ctx.newMsg(msg.getQueueName(), msg.getType(), msg.getOriginator(), msg.getMetaData(), ruleEngineDeviceRpcResponse.getResponse().orElse("{}")); + ctx.enqueueForTellNext(next, TbRelationTypes.SUCCESS); + } else { + TbMsg next = ctx.newMsg(msg.getQueueName(), msg.getType(), msg.getOriginator(), msg.getMetaData(), wrap("error", ruleEngineDeviceRpcResponse.getError().get().name())); + ctx.tellFailure(next, new RuntimeException(ruleEngineDeviceRpcResponse.getError().get().name())); + } + }); + } ctx.ack(msg); } } + private EdgeId findRelatedEdgeId(TbContext ctx, TbMsg msg) { + List result = + ctx.getRelationService().findByToAndType(ctx.getTenantId(), msg.getOriginator(), EntityRelation.EDGE_TYPE, RelationTypeGroup.COMMON); + if (result != null && result.size() > 0) { + EntityRelation relationToEdge = result.get(0); + if (relationToEdge.getFrom() != null && relationToEdge.getFrom().getId() != null) { + return new EdgeId(relationToEdge.getFrom().getId()); + } + } + return null; + } + + private void sendRpcRequestToEdgeDevice(TbContext ctx, TbMsg msg, EdgeId edgeId, RuleEngineDeviceRpcRequest request) { + EdgeEvent edgeEvent = new EdgeEvent(); + edgeEvent.setTenantId(ctx.getTenantId()); + edgeEvent.setAction(ActionType.RPC_CALL.name()); + edgeEvent.setEntityId(request.getDeviceId().getId()); + edgeEvent.setType(EdgeEventType.DEVICE); + edgeEvent.setBody(json.valueToTree(request)); + edgeEvent.setEdgeId(edgeId); + ListenableFuture saveFuture = ctx.getEdgeEventService().saveAsync(edgeEvent); + Futures.addCallback(saveFuture, new FutureCallback() { + @Override + public void onSuccess(@Nullable EdgeEvent event) { + ctx.tellSuccess(msg); + } + + @Override + public void onFailure(Throwable th) { + log.error("Could not save edge event", th); + ctx.tellFailure(msg, th); + } + }, ctx.getDbCallbackExecutor()); + } + @Override public void destroy() { } 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 cb0600af1f..e86ef2982e 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 @@ -17,7 +17,7 @@ package org.thingsboard.rule.engine.telemetry; import com.google.gson.JsonParser; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; +import org.springframework.util.StringUtils; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNode; @@ -26,7 +26,6 @@ import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.plugin.ComponentType; -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.common.transport.adaptor.JsonConverter; @@ -43,13 +42,14 @@ import java.util.Set; nodeDetails = "Saves entity attributes based on configurable scope parameter. Expects messages with 'POST_ATTRIBUTES_REQUEST' message type", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeAttributesConfig", - icon = "file_upload", - ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} + icon = "file_upload" ) public class TbMsgAttributesNode implements TbNode { private TbMsgAttributesNodeConfiguration config; + private static final String SCOPE = "scope"; + @Override public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { this.config = TbNodeUtils.convert(configuration, TbMsgAttributesNodeConfiguration.class); @@ -66,6 +66,7 @@ public class TbMsgAttributesNode implements TbNode { } String src = msg.getData(); Set attributes = JsonConverter.convertToAttributes(new JsonParser().parse(src)); + msg.getMetaData().putValue(SCOPE, config.getScope()); String notifyDeviceStr = msg.getMetaData().getValue("notifyDevice"); ctx.getTelemetryService().saveAndNotify( ctx.getTenantId(), 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 c5f453f52b..fcf2e92440 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 @@ -18,17 +18,16 @@ package org.thingsboard.rule.engine.telemetry; import com.google.gson.JsonParser; import lombok.extern.slf4j.Slf4j; import org.springframework.util.StringUtils; -import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; 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.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.KvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.plugin.ComponentType; -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.common.transport.adaptor.JsonConverter; @@ -46,8 +45,7 @@ import java.util.Map; nodeDetails = "Saves timeseries telemetry data based on configurable TTL parameter. Expects messages with 'POST_TELEMETRY_REQUEST' message type", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeTimeseriesConfig", - icon = "file_upload", - ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} + icon = "file_upload" ) 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 9aabfb020c..c05008167c 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 @@ -23,7 +23,6 @@ import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.server.common.data.plugin.ComponentType; -import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; @Slf4j @@ -36,8 +35,7 @@ import org.thingsboard.server.common.msg.TbMsg; "Subsequent messages will not be processed until the previous message processing is completed or timeout event occurs.\n" + "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.CORE, RuleChainType.EDGE}) + configDirective = "tbNodeEmptyConfig") @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 83d221285a..fd7a6b6c60 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 @@ -22,15 +22,9 @@ import org.thingsboard.rule.engine.api.TbContext; 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.plugin.ComponentType; -import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; -import java.util.concurrent.ExecutionException; - -import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; - @Slf4j @RuleNode( type = ComponentType.ACTION, @@ -39,8 +33,7 @@ import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; nodeDescription = "This Node is now deprecated. Use \"Checkpoint\" instead.", nodeDetails = "", uiResources = {"static/rulenode/rulenode-core-config.js"}, - configDirective = ("tbNodeEmptyConfig"), - ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} + configDirective = ("tbNodeEmptyConfig") ) @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 40b49458a6..9c9201ccab 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 @@ -20,18 +20,17 @@ import com.google.common.collect.Sets; 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.util.TbNodeUtils; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; 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.rule.engine.util.EntitiesAlarmOriginatorIdAsyncLoader; import org.thingsboard.rule.engine.util.EntitiesCustomerIdAsyncLoader; import org.thingsboard.rule.engine.util.EntitiesRelatedEntityIdAsyncLoader; import org.thingsboard.rule.engine.util.EntitiesTenantIdAsyncLoader; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.plugin.ComponentType; -import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; import java.util.HashSet; @@ -47,8 +46,7 @@ import java.util.HashSet; "Alarm Originator found only in case original Originator is Alarm entity.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbTransformationNodeChangeOriginatorConfig", - icon = "find_replace", - ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} + icon = "find_replace" ) 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 d8713e976c..4739aa5bfd 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 @@ -16,15 +16,15 @@ package org.thingsboard.rule.engine.transform; import com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.rule.engine.api.RuleNode; +import org.thingsboard.rule.engine.api.ScriptEngine; +import org.thingsboard.rule.engine.api.TbContext; +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.rule.engine.api.*; import org.thingsboard.server.common.data.plugin.ComponentType; -import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.msg.TbMsg; -import static org.thingsboard.rule.engine.api.TbRelationTypes.FAILURE; -import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; - @RuleNode( type = ComponentType.TRANSFORMATION, name = "script", @@ -38,9 +38,7 @@ import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; "{ msg: new payload,
   metadata: new metadata,
   msgType: new msgType }

" + "All fields in resulting object are optional and will be taken from original message if not specified.", uiResources = {"static/rulenode/rulenode-core-config.js"}, - configDirective = "tbTransformationNodeScriptConfig", - ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} -) + configDirective = "tbTransformationNodeScriptConfig") public class TbTransformMsgNode extends TbAbstractTransformNode { private TbTransformMsgNodeConfiguration config; diff --git a/ui/src/app/api/edge.service.js b/ui/src/app/api/edge.service.js index d2123e7576..528dea960c 100644 --- a/ui/src/app/api/edge.service.js +++ b/ui/src/app/api/edge.service.js @@ -32,7 +32,9 @@ function EdgeService($http, $q, customerService) { assignEdgeToCustomer: assignEdgeToCustomer, unassignEdgeFromCustomer: unassignEdgeFromCustomer, makeEdgePublic: makeEdgePublic, - setRootRuleChain: setRootRuleChain + setRootRuleChain: setRootRuleChain, + getEdgeEvents: getEdgeEvents, + syncEdge: syncEdge }; return service; @@ -241,4 +243,35 @@ function EdgeService($http, $q, customerService) { }); return deferred.promise; } + + function getEdgeEvents(edgeId, pageLink) { + var deferred = $q.defer(); + var url = '/api/edge/' + edgeId + '/events' + '?limit=' + pageLink.limit; + if (angular.isDefined(pageLink.startTime) && pageLink.startTime != null) { + url += '&startTime=' + pageLink.startTime; + } + if (angular.isDefined(pageLink.endTime) && pageLink.endTime != null) { + url += '&endTime=' + pageLink.endTime; + } + if (angular.isDefined(pageLink.idOffset) && pageLink.idOffset != null) { + url += '&offset=' + pageLink.idOffset; + } + $http.get(url, null).then(function success(response) { + deferred.resolve(response.data); + }, function fail(response) { + deferred.reject(response.data); + }); + return deferred.promise; + } + + function syncEdge(edgeId) { + var deferred = $q.defer(); + var url = '/api/edge/sync'; + $http.post(url, edgeId).then(function success(response) { + deferred.resolve(response); + }, function fail(response) { + deferred.reject(response.data); + }); + return deferred.promise; + } } diff --git a/ui/src/app/asset/add-assets-to-edge.tpl.html b/ui/src/app/asset/add-assets-to-edge.tpl.html index 2e24411211..4c3a6c42de 100644 --- a/ui/src/app/asset/add-assets-to-edge.tpl.html +++ b/ui/src/app/asset/add-assets-to-edge.tpl.html @@ -15,7 +15,7 @@ limitations under the License. --> - +
diff --git a/ui/src/app/dashboard/add-dashboards-to-edge.controller.js b/ui/src/app/dashboard/add-dashboards-to-edge.controller.js index ff579d492f..e5dcc00b33 100644 --- a/ui/src/app/dashboard/add-dashboards-to-edge.controller.js +++ b/ui/src/app/dashboard/add-dashboards-to-edge.controller.js @@ -53,13 +53,7 @@ export default function AddDashboardsToEdgeController(dashboardService, types, $ fetchMoreItems_: function () { if (vm.dashboards.hasNext && !vm.dashboards.pending) { vm.dashboards.pending = true; - var fetchDashboardsPromise; - if (edgeCustomerId === vm.types.id.nullUid) { - fetchDashboardsPromise = dashboardService.getTenantDashboards(vm.dashboards.nextPageLink); - } else { - fetchDashboardsPromise = dashboardService.getCustomerDashboards(edgeCustomerId, vm.dashboards.nextPageLink); - } - fetchDashboardsPromise.then( + dashboardService.getTenantDashboards(vm.dashboards.nextPageLink).then( function success(dashboards) { vm.dashboards.data = vm.dashboards.data.concat(dashboards.data); vm.dashboards.nextPageLink = dashboards.nextPageLink; diff --git a/ui/src/app/device/add-devices-to-edge.tpl.html b/ui/src/app/device/add-devices-to-edge.tpl.html index 97ed4c937b..72bdfdbb62 100644 --- a/ui/src/app/device/add-devices-to-edge.tpl.html +++ b/ui/src/app/device/add-devices-to-edge.tpl.html @@ -15,7 +15,7 @@ limitations under the License. --> - +
diff --git a/ui/src/app/edge/edge-fieldset.tpl.html b/ui/src/app/edge/edge-fieldset.tpl.html index b54e1cb651..bf0f09d972 100644 --- a/ui/src/app/edge/edge-fieldset.tpl.html +++ b/ui/src/app/edge/edge-fieldset.tpl.html @@ -21,6 +21,21 @@ {{ isPublic ? 'edge.make-private' : 'edge.unassign-from-customer' | translate }} +{{ 'edge.manage-edge-assets' | translate }} +{{ 'edge.manage-edge-devices' | translate }} +{{ 'edge.manage-edge-entity-views' | translate }} +{{ 'edge.manage-edge-dashboards' | translate }} +{{ 'edge.manage-edge-rulechains' | translate }} {{ 'edge.delete' | translate }} @@ -33,6 +48,12 @@ edge.copy-id + + + edge.sync +
@@ -68,6 +89,20 @@ + + + +
+
edge.edge-license-key-required
+
+
+ + + +
+
edge.cloud-endpoint-required
+
+
diff --git a/ui/src/app/edge/edge.controller.js b/ui/src/app/edge/edge.controller.js index 51269ea753..94f076c1af 100644 --- a/ui/src/app/edge/edge.controller.js +++ b/ui/src/app/edge/edge.controller.js @@ -129,6 +129,11 @@ export function EdgeController($rootScope, userService, edgeService, customerSer vm.assignToCustomer = assignToCustomer; vm.makePublic = makePublic; vm.unassignFromCustomer = unassignFromCustomer; + vm.openEdgeAssets = openEdgeAssets; + vm.openEdgeDevices = openEdgeDevices; + vm.openEdgeEntityViews = openEdgeEntityViews; + vm.openEdgeDashboards = openEdgeDashboards; + vm.openEdgeRuleChains = openEdgeRuleChains; initController(); @@ -547,7 +552,7 @@ export function EdgeController($rootScope, userService, edgeService, customerSer $event.stopPropagation(); } var pageSize = 10; - ruleChainService.getRuleChains({limit: pageSize, textSearch: ''}).then( + ruleChainService.getEdgesRuleChains({limit: pageSize, textSearch: ''}).then( function success(_ruleChains) { var ruleChains = { pageSize: pageSize, diff --git a/ui/src/app/edge/edge.directive.js b/ui/src/app/edge/edge.directive.js index 0cbe32f79e..04165418e9 100644 --- a/ui/src/app/edge/edge.directive.js +++ b/ui/src/app/edge/edge.directive.js @@ -20,7 +20,7 @@ import edgeFieldsetTemplate from './edge-fieldset.tpl.html'; /* eslint-enable import/no-unresolved, import/default */ /*@ngInject*/ -export default function EdgeDirective($compile, $templateCache, $translate, $mdDialog, $document, utils, toast, types, customerService) { +export default function EdgeDirective($compile, $templateCache, $translate, $mdDialog, $document, utils, toast, types, customerService, edgeService) { var linker = function (scope, element) { var template = $templateCache.get(edgeFieldsetTemplate); element.html(template); @@ -35,6 +35,7 @@ export default function EdgeDirective($compile, $templateCache, $translate, $mdD if (!scope.edge.id) { scope.edge.routingKey = utils.guid(''); scope.edge.secret = generateSecret(20); + scope.edge.cloudEndpoint = utils.baseUrl(); } if (scope.edge.customerId && scope.edge.customerId.id !== types.id.nullUid) { scope.isAssignedToCustomer = true; @@ -68,6 +69,17 @@ export default function EdgeDirective($compile, $templateCache, $translate, $mdD toast.showSuccess($translate.instant('edge.id-copied-message'), 750, angular.element(element).parent().parent(), 'bottom left'); }; + scope.onEdgeSync = function (edgeId) { + edgeService.syncEdge(edgeId).then( + function success() { + toast.showSuccess($translate.instant('edge.sync-message'), 750, angular.element(element).parent().parent(), 'bottom left'); + }, + function fail(error) { + toast.showError(error); + } + ); + } + $compile(element.contents())(scope); scope.onEdgeInfoCopied = function(type) { @@ -96,6 +108,11 @@ export default function EdgeDirective($compile, $templateCache, $translate, $mdD onAssignToCustomer: '&', onMakePublic: '&', onUnassignFromCustomer: '&', + onManageEdgeAssets: '&', + onManageEdgeDevices: '&', + onManageEdgeEntityViews: '&', + onManageEdgeDashboards: '&', + onManageEdgeRuleChains: '&', onDeleteEdge: '&' } }; diff --git a/ui/src/app/edge/edge.routes.js b/ui/src/app/edge/edge.routes.js index 1afd319609..bcba2cffe1 100644 --- a/ui/src/app/edge/edge.routes.js +++ b/ui/src/app/edge/edge.routes.js @@ -21,6 +21,8 @@ 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"; +import ruleChainsTemplate from "../rulechain/rulechains.tpl.html"; +import ruleChainTemplate from "../rulechain/rulechain.tpl.html"; /* eslint-enable import/no-unresolved, import/default */ @@ -49,8 +51,7 @@ export default function EdgeRoutes($stateProvider, types) { ncyBreadcrumb: { label: '{"icon": "transform", "label": "edge.edges"}' } - }) - .state('home.edges.entityViews', { + }).state('home.edges.entityViews', { url: '/:edgeId/entityViews', params: {'topIndex': 0}, module: 'private', @@ -72,8 +73,7 @@ export default function EdgeRoutes($stateProvider, types) { ncyBreadcrumb: { label: '{"icon": "view_quilt", "label": "edge.entity-views"}' } - }) - .state('home.edges.devices', { + }).state('home.edges.devices', { url: '/:edgeId/devices', params: {'topIndex': 0}, module: 'private', @@ -95,8 +95,7 @@ export default function EdgeRoutes($stateProvider, types) { ncyBreadcrumb: { label: '{"icon": "devices_other", "label": "edge.devices"}' } - }) - .state('home.edges.assets', { + }).state('home.edges.assets', { url: '/:edgeId/assets', params: {'topIndex': 0}, module: 'private', @@ -118,29 +117,27 @@ export default function EdgeRoutes($stateProvider, types) { 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' + }).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"}' } - }, - data: { - dashboardsType: 'edge', - searchEnabled: true, - pageTitle: 'edge.dashboards' - }, - ncyBreadcrumb: { - label: '{"icon": "dashboard", "label": "edge.dashboards"}' - } - }) - .state('home.edges.dashboards.dashboard', { + }).state('home.edges.dashboards.dashboard', { url: '/:dashboardId?state', reloadOnSearch: false, module: 'private', @@ -161,5 +158,84 @@ export default function EdgeRoutes($stateProvider, types) { ncyBreadcrumb: { label: '{"icon": "dashboard", "label": "{{ vm.dashboard.title }}", "translate": "false"}' } + }).state('home.customers.edges', { + url: '/:customerId/edges', + params: {'topIndex': 0}, + module: 'private', + auth: ['TENANT_ADMIN'], + views: { + "content@home": { + templateUrl: edgesTemplate, + controllerAs: 'vm', + controller: 'EdgeController' + } + }, + data: { + edgesType: 'customer', + searchEnabled: true, + searchByEntitySubtype: true, + searchEntityType: types.entityType.edge, + pageTitle: 'customer.edges' + }, + ncyBreadcrumb: { + label: '{"icon": "router", "label": "{{ vm.customerEdgesTitle }}", "translate": "false"}' + } + }).state('home.edges.ruleChains', { + url: '/:edgeId/ruleChains', + params: {'topIndex': 0}, + module: 'private', + auth: ['TENANT_ADMIN'], + views: { + "content@home": { + templateUrl: ruleChainsTemplate, + controllerAs: 'vm', + controller: 'RuleChainsController' + } + }, + data: { + searchEnabled: true, + pageTitle: 'edge.rulechains', + ruleChainsType: 'edge' + }, + ncyBreadcrumb: { + label: '{"icon": "settings_ethernet", "label": "rulechain.edge-rulechains"}' + } + }).state('home.edges.ruleChains.ruleChain', { + url: '/:ruleChainId', + reloadOnSearch: false, + module: 'private', + auth: ['SYS_ADMIN', 'TENANT_ADMIN'], + views: { + "content@home": { + templateUrl: ruleChainTemplate, + controller: 'RuleChainController', + controllerAs: 'vm' + } + }, + resolve: { + ruleChain: + /*@ngInject*/ + function($stateParams, ruleChainService) { + return ruleChainService.getRuleChain($stateParams.ruleChainId); + }, + ruleChainMetaData: + /*@ngInject*/ + function($stateParams, ruleChainService) { + return ruleChainService.getRuleChainMetaData($stateParams.ruleChainId); + }, + ruleNodeComponents: + /*@ngInject*/ + function($stateParams, ruleChainService) { + return ruleChainService.getRuleNodeComponents(types.ruleChainType.edge); + } + }, + data: { + import: false, + searchEnabled: false, + pageTitle: 'edge.rulechain' + }, + ncyBreadcrumb: { + label: '{"icon": "settings_ethernet", "label": "{{ vm.ruleChain.name }}", "translate": "false"}' + } }); } diff --git a/ui/src/app/edge/edges.tpl.html b/ui/src/app/edge/edges.tpl.html index 4fc3f5d75e..2600413255 100644 --- a/ui/src/app/edge/edges.tpl.html +++ b/ui/src/app/edge/edges.tpl.html @@ -29,6 +29,11 @@ on-assign-to-customer="vm.assignToCustomer(event, [ vm.grid.detailsConfig.currentItem.id.id ])" on-make-public="vm.makePublic(event, vm.grid.detailsConfig.currentItem)" on-unassign-from-customer="vm.unassignFromCustomer(event, vm.grid.detailsConfig.currentItem, isPublic)" + on-manage-edge-assets="vm.openEdgeAssets(event, vm.grid.detailsConfig.currentItem)" + on-manage-edge-devices="vm.openEdgeDevices(event, vm.grid.detailsConfig.currentItem)" + on-manage-edge-entity-views="vm.openEdgeEntityViews(event, vm.grid.detailsConfig.currentItem)" + on-manage-edge-dashboards="vm.openEdgeDashboards(event, vm.grid.detailsConfig.currentItem)" + on-manage-edge-rule-chains="vm.openEdgeRuleChains(event, vm.grid.detailsConfig.currentItem)" on-delete-edge="vm.grid.deleteItem(event, vm.grid.detailsConfig.currentItem)"> diff --git a/ui/src/app/edge/set-root-rule-chain-to-edges.controller.js b/ui/src/app/edge/set-root-rule-chain-to-edges.controller.js index a474fde6a7..a8d134b20e 100644 --- a/ui/src/app/edge/set-root-rule-chain-to-edges.controller.js +++ b/ui/src/app/edge/set-root-rule-chain-to-edges.controller.js @@ -53,7 +53,7 @@ export default function SetRootRuleChainToEdgesController(ruleChainService, edge fetchMoreItems_: function () { if (vm.ruleChains.hasNext && !vm.ruleChains.pending) { vm.ruleChains.pending = true; - ruleChainService.getRuleChains(vm.ruleChains.nextPageLink).then( + ruleChainService.getEdgesRuleChains(vm.ruleChains.nextPageLink).then( function success(ruleChains) { vm.ruleChains.data = vm.ruleChains.data.concat(ruleChains.data); vm.ruleChains.nextPageLink = ruleChains.nextPageLink; diff --git a/ui/src/app/entity-view/add-entity-views-to-edge.tpl.html b/ui/src/app/entity-view/add-entity-views-to-edge.tpl.html index 1f054d928e..4b64163ec1 100644 --- a/ui/src/app/entity-view/add-entity-views-to-edge.tpl.html +++ b/ui/src/app/entity-view/add-entity-views-to-edge.tpl.html @@ -15,7 +15,7 @@ limitations under the License. --> - +
diff --git a/ui/src/app/event/event-header-edge-event.tpl.html b/ui/src/app/event/event-header-edge-event.tpl.html new file mode 100644 index 0000000000..e84922f6a5 --- /dev/null +++ b/ui/src/app/event/event-header-edge-event.tpl.html @@ -0,0 +1,23 @@ + +
event.event-time
+
event.event-type
+
edge.event-action
+
edge.entity-id
+
edge.status
+
edge.entity-info
diff --git a/ui/src/app/event/event-row-edge-event.tpl.html b/ui/src/app/event/event-row-edge-event.tpl.html new file mode 100644 index 0000000000..6cc06d0cc3 --- /dev/null +++ b/ui/src/app/event/event-row-edge-event.tpl.html @@ -0,0 +1,37 @@ + +
{{ event.createdTime | date : 'yyyy-MM-dd HH:mm:ss' }}
+
{{ event.type }}
+
{{ event.action }}
+
{{ event.entityId }}
+
{{ updateStatus(event.createdTime) | translate }}
+
+ + + {{ 'action.view' | translate }} + + + more_horiz + + +
+ + diff --git a/ui/src/app/rulechain/add-rulechains-to-edge.controller.js b/ui/src/app/rulechain/add-rulechains-to-edge.controller.js index 236c44b091..df05c47eee 100644 --- a/ui/src/app/rulechain/add-rulechains-to-edge.controller.js +++ b/ui/src/app/rulechain/add-rulechains-to-edge.controller.js @@ -54,7 +54,7 @@ export default function AddRuleChainsToEdgeController(ruleChainService, $mdDialo vm.ruleChains.pending = true; ruleChainService.getEdgesRuleChains(vm.ruleChains.nextPageLink).then( function success(ruleChains) { - vm.ruleChains.data = ruleChains.data; + vm.ruleChains.data = vm.ruleChains.data.concat(ruleChains.data); vm.ruleChains.nextPageLink = ruleChains.nextPageLink; vm.ruleChains.hasNext = ruleChains.hasNext; if (vm.ruleChains.hasNext) {