Showing
56 changed files
with
4096 additions
and
44 deletions
Too many changes to show.
To preserve performance only 56 of 112 files are displayed.
... | ... | @@ -89,6 +89,10 @@ |
89 | 89 | <artifactId>queue</artifactId> |
90 | 90 | </dependency> |
91 | 91 | <dependency> |
92 | + <groupId>org.thingsboard.common</groupId> | |
93 | + <artifactId>edge-api</artifactId> | |
94 | + </dependency> | |
95 | + <dependency> | |
92 | 96 | <groupId>org.thingsboard</groupId> |
93 | 97 | <artifactId>dao</artifactId> |
94 | 98 | <type>test-jar</type> | ... | ... |
1 | +-- | |
2 | +-- Copyright © 2016-2020 The Thingsboard Authors | |
3 | +-- | |
4 | +-- Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | +-- you may not use this file except in compliance with the License. | |
6 | +-- You may obtain a copy of the License at | |
7 | +-- | |
8 | +-- http://www.apache.org/licenses/LICENSE-2.0 | |
9 | +-- | |
10 | +-- Unless required by applicable law or agreed to in writing, software | |
11 | +-- distributed under the License is distributed on an "AS IS" BASIS, | |
12 | +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | +-- See the License for the specific language governing permissions and | |
14 | +-- limitations under the License. | |
15 | +-- | |
16 | + | |
17 | +CREATE TABLE IF NOT EXISTS edge ( | |
18 | + id varchar(31) NOT NULL CONSTRAINT edge_pkey PRIMARY KEY, | |
19 | + additional_info varchar, | |
20 | + customer_id varchar(31), | |
21 | + root_rule_chain_id varchar(31), | |
22 | + configuration varchar(10000000), | |
23 | + type varchar(255), | |
24 | + name varchar(255), | |
25 | + label varchar(255), | |
26 | + routing_key varchar(255), | |
27 | + secret varchar(255), | |
28 | + search_text varchar(255), | |
29 | + tenant_id varchar(31) | |
30 | +); | |
\ No newline at end of file | ... | ... |
... | ... | @@ -56,6 +56,7 @@ import org.thingsboard.server.dao.cassandra.CassandraCluster; |
56 | 56 | import org.thingsboard.server.dao.customer.CustomerService; |
57 | 57 | import org.thingsboard.server.dao.dashboard.DashboardService; |
58 | 58 | import org.thingsboard.server.dao.device.DeviceService; |
59 | +import org.thingsboard.server.dao.edge.EdgeService; | |
59 | 60 | import org.thingsboard.server.dao.entityview.EntityViewService; |
60 | 61 | import org.thingsboard.server.dao.event.EventService; |
61 | 62 | import org.thingsboard.server.dao.nosql.CassandraBufferedRateExecutor; |
... | ... | @@ -248,6 +249,11 @@ public class ActorSystemContext { |
248 | 249 | @Getter |
249 | 250 | private RuleChainTransactionService ruleChainTransactionService; |
250 | 251 | |
252 | + @Lazy | |
253 | + @Autowired | |
254 | + @Getter | |
255 | + private EdgeService edgeService; | |
256 | + | |
251 | 257 | @Value("${cluster.partition_id}") |
252 | 258 | @Getter |
253 | 259 | private long queuePartitionId; | ... | ... |
... | ... | @@ -60,6 +60,7 @@ import org.thingsboard.server.dao.cassandra.CassandraCluster; |
60 | 60 | import org.thingsboard.server.dao.customer.CustomerService; |
61 | 61 | import org.thingsboard.server.dao.dashboard.DashboardService; |
62 | 62 | import org.thingsboard.server.dao.device.DeviceService; |
63 | +import org.thingsboard.server.dao.edge.EdgeService; | |
63 | 64 | import org.thingsboard.server.dao.entityview.EntityViewService; |
64 | 65 | import org.thingsboard.server.dao.nosql.CassandraStatementTask; |
65 | 66 | import org.thingsboard.server.dao.relation.RelationService; |
... | ... | @@ -185,15 +186,26 @@ class DefaultTbContext implements TbContext { |
185 | 186 | } |
186 | 187 | |
187 | 188 | public TbMsg alarmCreatedMsg(Alarm alarm, RuleNodeId ruleNodeId) { |
189 | + return alarmMsg(alarm, ruleNodeId, DataConstants.ENTITY_CREATED); | |
190 | + } | |
191 | + | |
192 | + public TbMsg alarmUpdatedMsg(Alarm alarm, RuleNodeId ruleNodeId) { | |
193 | + return alarmMsg(alarm, ruleNodeId, DataConstants.ENTITY_UPDATED); | |
194 | + } | |
195 | + | |
196 | + public TbMsg alarmClearedMsg(Alarm alarm, RuleNodeId ruleNodeId) { | |
197 | + return alarmMsg(alarm, ruleNodeId, DataConstants.ALARM_CLEAR); | |
198 | + } | |
199 | + | |
200 | + private TbMsg alarmMsg(Alarm alarm, RuleNodeId ruleNodeId, String type) { | |
188 | 201 | try { |
189 | 202 | ObjectNode entityNode = mapper.valueToTree(alarm); |
190 | - return new TbMsg(UUIDs.timeBased(), DataConstants.ENTITY_CREATED, alarm.getId(), getActionMetaData(ruleNodeId), mapper.writeValueAsString(entityNode), null, null, 0L); | |
203 | + return new TbMsg(UUIDs.timeBased(), type, alarm.getId(), getActionMetaData(ruleNodeId), mapper.writeValueAsString(entityNode), null, null, 0L); | |
191 | 204 | } catch (JsonProcessingException | IllegalArgumentException e) { |
192 | - throw new RuntimeException("Failed to process alarm created msg: " + e); | |
205 | + throw new RuntimeException("Failed to process alarm created, updated or cleared msg: " + e); | |
193 | 206 | } |
194 | 207 | } |
195 | 208 | |
196 | - | |
197 | 209 | @Override |
198 | 210 | public RuleNodeId getSelfId() { |
199 | 211 | return nodeCtx.getSelf().getId(); |
... | ... | @@ -326,6 +338,11 @@ class DefaultTbContext implements TbContext { |
326 | 338 | } |
327 | 339 | |
328 | 340 | @Override |
341 | + public EdgeService getEdgeService() { | |
342 | + return mainCtx.getEdgeService(); | |
343 | + } | |
344 | + | |
345 | + @Override | |
329 | 346 | public EventLoopGroup getSharedEventLoop() { |
330 | 347 | return mainCtx.getSharedEventLoopGroupService().getSharedEventLoopGroup(); |
331 | 348 | } | ... | ... |
... | ... | @@ -24,10 +24,12 @@ import com.datastax.driver.core.utils.UUIDs; |
24 | 24 | import java.util.Optional; |
25 | 25 | |
26 | 26 | import lombok.extern.slf4j.Slf4j; |
27 | +import com.google.common.util.concurrent.FutureCallback; | |
27 | 28 | import org.thingsboard.server.actors.ActorSystemContext; |
28 | 29 | import org.thingsboard.server.actors.device.DeviceActorToRuleEngineMsg; |
29 | 30 | import org.thingsboard.server.actors.service.DefaultActorService; |
30 | 31 | import org.thingsboard.server.actors.shared.ComponentMsgProcessor; |
32 | +import org.thingsboard.server.common.data.DataConstants; | |
31 | 33 | import org.thingsboard.server.common.data.EntityType; |
32 | 34 | import org.thingsboard.server.common.data.id.EntityId; |
33 | 35 | import org.thingsboard.server.common.data.id.RuleChainId; |
... | ... | @@ -37,14 +39,17 @@ import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; |
37 | 39 | import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; |
38 | 40 | import org.thingsboard.server.common.data.relation.EntityRelation; |
39 | 41 | import org.thingsboard.server.common.data.rule.RuleChain; |
42 | +import org.thingsboard.server.common.data.rule.RuleChainType; | |
40 | 43 | import org.thingsboard.server.common.data.rule.RuleNode; |
41 | 44 | import org.thingsboard.server.common.msg.TbMsg; |
42 | 45 | import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; |
43 | 46 | import org.thingsboard.server.common.msg.cluster.ServerAddress; |
44 | 47 | import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; |
45 | 48 | import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; |
49 | +import org.thingsboard.server.dao.edge.EdgeService; | |
46 | 50 | import org.thingsboard.server.dao.rule.RuleChainService; |
47 | 51 | |
52 | +import javax.annotation.Nullable; | |
48 | 53 | import java.util.ArrayList; |
49 | 54 | import java.util.Collections; |
50 | 55 | import java.util.HashMap; |
... | ... | @@ -65,6 +70,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh |
65 | 70 | private final Map<RuleNodeId, RuleNodeCtx> nodeActors; |
66 | 71 | private final Map<RuleNodeId, List<RuleNodeRelation>> nodeRoutes; |
67 | 72 | private final RuleChainService service; |
73 | + private final EdgeService edgeService; | |
68 | 74 | |
69 | 75 | private RuleNodeId firstId; |
70 | 76 | private RuleNodeCtx firstNode; |
... | ... | @@ -79,6 +85,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh |
79 | 85 | this.nodeActors = new HashMap<>(); |
80 | 86 | this.nodeRoutes = new HashMap<>(); |
81 | 87 | this.service = systemContext.getRuleChainService(); |
88 | + this.edgeService = systemContext.getEdgeService(); | |
82 | 89 | this.ruleChainName = ruleChainId.toString(); |
83 | 90 | } |
84 | 91 | |
... | ... | @@ -92,17 +99,19 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh |
92 | 99 | if (!started) { |
93 | 100 | RuleChain ruleChain = service.findRuleChainById(tenantId, entityId); |
94 | 101 | if (ruleChain != null) { |
95 | - ruleChainName = ruleChain.getName(); | |
96 | - List<RuleNode> ruleNodeList = service.getRuleChainNodes(tenantId, entityId); | |
97 | - log.trace("[{}][{}] Starting rule chain with {} nodes", tenantId, entityId, ruleNodeList.size()); | |
98 | - // Creating and starting the actors; | |
99 | - for (RuleNode ruleNode : ruleNodeList) { | |
100 | - log.trace("[{}][{}] Creating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode); | |
101 | - ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode); | |
102 | - nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode)); | |
102 | + if (ruleChain.getType().equals(RuleChainType.SYSTEM)) { | |
103 | + ruleChainName = ruleChain.getName(); | |
104 | + List<RuleNode> ruleNodeList = service.getRuleChainNodes(tenantId, entityId); | |
105 | + log.trace("[{}][{}] Starting rule chain with {} nodes", tenantId, entityId, ruleNodeList.size()); | |
106 | + // Creating and starting the actors; | |
107 | + for (RuleNode ruleNode : ruleNodeList) { | |
108 | + log.trace("[{}][{}] Creating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode); | |
109 | + ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode); | |
110 | + nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode)); | |
111 | + } | |
112 | + initRoutes(ruleChain, ruleNodeList); | |
113 | + started = true; | |
103 | 114 | } |
104 | - initRoutes(ruleChain, ruleNodeList); | |
105 | - started = true; | |
106 | 115 | } |
107 | 116 | } else { |
108 | 117 | onUpdate(context); |
... | ... | @@ -113,31 +122,36 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh |
113 | 122 | public void onUpdate(ActorContext context) { |
114 | 123 | RuleChain ruleChain = service.findRuleChainById(tenantId, entityId); |
115 | 124 | if (ruleChain != null) { |
116 | - ruleChainName = ruleChain.getName(); | |
117 | - List<RuleNode> ruleNodeList = service.getRuleChainNodes(tenantId, entityId); | |
118 | - log.trace("[{}][{}] Updating rule chain with {} nodes", tenantId, entityId, ruleNodeList.size()); | |
119 | - for (RuleNode ruleNode : ruleNodeList) { | |
120 | - RuleNodeCtx existing = nodeActors.get(ruleNode.getId()); | |
121 | - if (existing == null) { | |
122 | - log.trace("[{}][{}] Creating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode); | |
123 | - ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode); | |
124 | - nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode)); | |
125 | - } else { | |
126 | - log.trace("[{}][{}] Updating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode); | |
127 | - existing.setSelf(ruleNode); | |
128 | - existing.getSelfActor().tell(new ComponentLifecycleMsg(tenantId, existing.getSelf().getId(), ComponentLifecycleEvent.UPDATED), self); | |
125 | + if (ruleChain.getType().equals(RuleChainType.SYSTEM)) { | |
126 | + ruleChainName = ruleChain.getName(); | |
127 | + List<RuleNode> ruleNodeList = service.getRuleChainNodes(tenantId, entityId); | |
128 | + log.trace("[{}][{}] Updating rule chain with {} nodes", tenantId, entityId, ruleNodeList.size()); | |
129 | + for (RuleNode ruleNode : ruleNodeList) { | |
130 | + RuleNodeCtx existing = nodeActors.get(ruleNode.getId()); | |
131 | + if (existing == null) { | |
132 | + log.trace("[{}][{}] Creating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode); | |
133 | + ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode); | |
134 | + nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode)); | |
135 | + } else { | |
136 | + log.trace("[{}][{}] Updating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode); | |
137 | + existing.setSelf(ruleNode); | |
138 | + existing.getSelfActor().tell(new ComponentLifecycleMsg(tenantId, existing.getSelf().getId(), ComponentLifecycleEvent.UPDATED), self); | |
139 | + } | |
129 | 140 | } |
130 | - } | |
131 | 141 | |
132 | - Set<RuleNodeId> existingNodes = ruleNodeList.stream().map(RuleNode::getId).collect(Collectors.toSet()); | |
133 | - List<RuleNodeId> removedRules = nodeActors.keySet().stream().filter(node -> !existingNodes.contains(node)).collect(Collectors.toList()); | |
134 | - removedRules.forEach(ruleNodeId -> { | |
135 | - log.trace("[{}][{}] Removing rule node [{}]", tenantId, entityId, ruleNodeId); | |
136 | - RuleNodeCtx removed = nodeActors.remove(ruleNodeId); | |
137 | - removed.getSelfActor().tell(new ComponentLifecycleMsg(tenantId, removed.getSelf().getId(), ComponentLifecycleEvent.DELETED), self); | |
138 | - }); | |
142 | + Set<RuleNodeId> existingNodes = ruleNodeList.stream().map(RuleNode::getId).collect(Collectors.toSet()); | |
143 | + List<RuleNodeId> removedRules = nodeActors.keySet().stream().filter(node -> !existingNodes.contains(node)).collect(Collectors.toList()); | |
144 | + removedRules.forEach(ruleNodeId -> { | |
145 | + log.trace("[{}][{}] Removing rule node [{}]", tenantId, entityId, ruleNodeId); | |
146 | + RuleNodeCtx removed = nodeActors.remove(ruleNodeId); | |
147 | + removed.getSelfActor().tell(new ComponentLifecycleMsg(tenantId, removed.getSelf().getId(), ComponentLifecycleEvent.DELETED), self); | |
148 | + }); | |
139 | 149 | |
140 | - initRoutes(ruleChain, ruleNodeList); | |
150 | + initRoutes(ruleChain, ruleNodeList); | |
151 | + | |
152 | + } else if (ruleChain.getType().equals(RuleChainType.EDGE)){ | |
153 | + stop(context); | |
154 | + } | |
141 | 155 | } |
142 | 156 | } |
143 | 157 | |
... | ... | @@ -326,6 +340,30 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh |
326 | 340 | if (nodeCtx != null) { |
327 | 341 | nodeCtx.getSelfActor().tell(new RuleChainToRuleNodeMsg(new DefaultTbContext(systemContext, nodeCtx), msg, fromRelationType), self); |
328 | 342 | } |
343 | + pushUpdatesToEdges(msg); | |
344 | + } | |
345 | + | |
346 | + private void pushUpdatesToEdges(TbMsg msg) { | |
347 | + switch (msg.getType()) { | |
348 | + case DataConstants.ENTITY_CREATED: | |
349 | + case DataConstants.ENTITY_UPDATED: | |
350 | + case DataConstants.ENTITY_DELETED: | |
351 | + case DataConstants.ENTITY_ASSIGNED_TO_EDGE: | |
352 | + case DataConstants.ENTITY_UNASSIGNED_FROM_EDGE: | |
353 | + case DataConstants.ALARM_ACK: | |
354 | + case DataConstants.ALARM_CLEAR: | |
355 | + edgeService.pushEventToEdge(tenantId, msg, new FutureCallback<Void>() { | |
356 | + @Override | |
357 | + public void onSuccess(@Nullable Void aVoid) { | |
358 | + log.debug("Event saved successfully!"); | |
359 | + } | |
360 | + | |
361 | + @Override | |
362 | + public void onFailure(Throwable t) { | |
363 | + log.debug("Failure during event save", t); | |
364 | + } | |
365 | + }); | |
366 | + } | |
329 | 367 | } |
330 | 368 | |
331 | 369 | private TbMsg enrichWithRuleChainId(TbMsg tbMsg) { | ... | ... |
... | ... | @@ -39,6 +39,7 @@ import org.thingsboard.server.common.data.id.RuleChainId; |
39 | 39 | import org.thingsboard.server.common.data.id.TenantId; |
40 | 40 | import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; |
41 | 41 | import org.thingsboard.server.common.data.rule.RuleChain; |
42 | +import org.thingsboard.server.common.data.rule.RuleChainType; | |
42 | 43 | import org.thingsboard.server.common.msg.TbActorMsg; |
43 | 44 | import org.thingsboard.server.common.msg.aware.DeviceAwareMsg; |
44 | 45 | import org.thingsboard.server.common.msg.aware.RuleChainAwareMsg; |
... | ... | @@ -139,11 +140,18 @@ public class TenantActor extends RuleChainManagerActor { |
139 | 140 | } |
140 | 141 | |
141 | 142 | private void onComponentLifecycleMsg(ComponentLifecycleMsg msg) { |
143 | + RuleChain ruleChain = null; | |
144 | + if (msg.getEntityId().getEntityType() == EntityType.RULE_CHAIN) { | |
145 | + ruleChain = systemContext.getRuleChainService().findRuleChainById(tenantId, new RuleChainId(msg.getEntityId().getId())); | |
146 | + if (ruleChain !=null && !RuleChainType.SYSTEM.equals(ruleChain.getType())) { | |
147 | + log.debug("[{}] Non SYSTEM rule chains are ignored and not started. Current rule chain type [{}]", tenantId, ruleChain.getType()); | |
148 | + return; | |
149 | + } | |
150 | + } | |
151 | + | |
142 | 152 | ActorRef target = getEntityActorRef(msg.getEntityId()); |
143 | 153 | if (target != null) { |
144 | - if (msg.getEntityId().getEntityType() == EntityType.RULE_CHAIN) { | |
145 | - RuleChain ruleChain = systemContext.getRuleChainService(). | |
146 | - findRuleChainById(tenantId, new RuleChainId(msg.getEntityId().getId())); | |
154 | + if (msg.getEntityId().getEntityType() == EntityType.RULE_CHAIN && ruleChain != null) { | |
147 | 155 | ruleChainManager.visit(ruleChain, target); |
148 | 156 | } |
149 | 157 | target.tell(msg, ActorRef.noSender()); | ... | ... |
... | ... | @@ -33,10 +33,12 @@ import org.thingsboard.server.common.data.asset.Asset; |
33 | 33 | import org.thingsboard.server.common.data.asset.AssetInfo; |
34 | 34 | import org.thingsboard.server.common.data.asset.AssetSearchQuery; |
35 | 35 | import org.thingsboard.server.common.data.audit.ActionType; |
36 | +import org.thingsboard.server.common.data.edge.Edge; | |
36 | 37 | import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; |
37 | 38 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
38 | 39 | import org.thingsboard.server.common.data.id.AssetId; |
39 | 40 | import org.thingsboard.server.common.data.id.CustomerId; |
41 | +import org.thingsboard.server.common.data.id.EdgeId; | |
40 | 42 | import org.thingsboard.server.common.data.id.TenantId; |
41 | 43 | import org.thingsboard.server.common.data.page.PageData; |
42 | 44 | import org.thingsboard.server.common.data.page.PageLink; |
... | ... | @@ -51,6 +53,8 @@ import java.util.ArrayList; |
51 | 53 | import java.util.List; |
52 | 54 | import java.util.stream.Collectors; |
53 | 55 | |
56 | +import static org.thingsboard.server.controller.EdgeController.EDGE_ID; | |
57 | + | |
54 | 58 | @RestController |
55 | 59 | @RequestMapping("/api") |
56 | 60 | public class AssetController extends BaseController { |
... | ... | @@ -396,4 +400,93 @@ public class AssetController extends BaseController { |
396 | 400 | throw handleException(e); |
397 | 401 | } |
398 | 402 | } |
403 | + | |
404 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | |
405 | + @RequestMapping(value = "/edge/{edgeId}/asset/{assetId}", method = RequestMethod.POST) | |
406 | + @ResponseBody | |
407 | + public Asset assignAssetToEdge(@PathVariable(EDGE_ID) String strEdgeId, | |
408 | + @PathVariable(ASSET_ID) String strAssetId) throws ThingsboardException { | |
409 | + checkParameter(EDGE_ID, strEdgeId); | |
410 | + checkParameter(ASSET_ID, strAssetId); | |
411 | + try { | |
412 | + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); | |
413 | + Edge edge = checkEdgeId(edgeId, Operation.READ); | |
414 | + | |
415 | + AssetId assetId = new AssetId(toUUID(strAssetId)); | |
416 | + checkAssetId(assetId, Operation.ASSIGN_TO_EDGE); | |
417 | + | |
418 | + Asset savedAsset = checkNotNull(assetService.assignAssetToEdge(getTenantId(), assetId, edgeId)); | |
419 | + | |
420 | + logEntityAction(assetId, savedAsset, | |
421 | + savedAsset.getCustomerId(), | |
422 | + ActionType.ASSIGNED_TO_EDGE, null, strAssetId, strEdgeId, edge.getName()); | |
423 | + | |
424 | + return savedAsset; | |
425 | + } catch (Exception e) { | |
426 | + | |
427 | + logEntityAction(emptyId(EntityType.ASSET), null, | |
428 | + null, | |
429 | + ActionType.ASSIGNED_TO_EDGE, e, strAssetId, strEdgeId); | |
430 | + | |
431 | + throw handleException(e); | |
432 | + } | |
433 | + } | |
434 | + | |
435 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | |
436 | + @RequestMapping(value = "/edge/asset/{assetId}", method = RequestMethod.DELETE) | |
437 | + @ResponseBody | |
438 | + public Asset unassignAssetFromEdge(@PathVariable(ASSET_ID) String strAssetId) throws ThingsboardException { | |
439 | + checkParameter(ASSET_ID, strAssetId); | |
440 | + try { | |
441 | + AssetId assetId = new AssetId(toUUID(strAssetId)); | |
442 | + Asset asset = checkAssetId(assetId, Operation.UNASSIGN_FROM_EDGE); | |
443 | + if (asset.getEdgeId() == null || asset.getEdgeId().getId().equals(ModelConstants.NULL_UUID)) { | |
444 | + throw new IncorrectParameterException("Asset isn't assigned to any edge!"); | |
445 | + } | |
446 | + | |
447 | + Edge edge = checkEdgeId(asset.getEdgeId(), Operation.READ); | |
448 | + | |
449 | + Asset savedAsset = checkNotNull(assetService.unassignAssetFromEdge(getTenantId(), assetId)); | |
450 | + | |
451 | + logEntityAction(assetId, asset, | |
452 | + asset.getCustomerId(), | |
453 | + ActionType.UNASSIGNED_FROM_EDGE, null, strAssetId, edge.getId().toString(), edge.getName()); | |
454 | + | |
455 | + return savedAsset; | |
456 | + } catch (Exception e) { | |
457 | + | |
458 | + logEntityAction(emptyId(EntityType.ASSET), null, | |
459 | + null, | |
460 | + ActionType.UNASSIGNED_FROM_EDGE, e, strAssetId); | |
461 | + | |
462 | + throw handleException(e); | |
463 | + } | |
464 | + } | |
465 | + | |
466 | + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") | |
467 | + @RequestMapping(value = "/edge/{edgeId}/assets", params = {"pageSize", "page"}, method = RequestMethod.GET) | |
468 | + @ResponseBody | |
469 | + public PageData<Asset> getEdgeAssets( | |
470 | + @PathVariable("edgeId") String strEdgeId, | |
471 | + @RequestParam int pageSize, | |
472 | + @RequestParam int page, | |
473 | + @RequestParam(required = false) String type, | |
474 | + @RequestParam(required = false) String textSearch, | |
475 | + @RequestParam(required = false) String sortProperty, | |
476 | + @RequestParam(required = false) String sortOrder) throws ThingsboardException { | |
477 | + checkParameter("edgeId", strEdgeId); | |
478 | + try { | |
479 | + TenantId tenantId = getCurrentUser().getTenantId(); | |
480 | + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); | |
481 | + checkEdgeId(edgeId, Operation.READ); | |
482 | + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); | |
483 | + if (type != null && type.trim().length()>0) { | |
484 | + return checkNotNull(assetService.findAssetsByTenantIdAndEdgeIdAndType(tenantId, edgeId, type, pageLink)); | |
485 | + } else { | |
486 | + return checkNotNull(assetService.findAssetsByTenantIdAndEdgeId(tenantId, edgeId, pageLink)); | |
487 | + } | |
488 | + } catch (Exception e) { | |
489 | + throw handleException(e); | |
490 | + } | |
491 | + } | |
399 | 492 | } | ... | ... |
... | ... | @@ -35,12 +35,14 @@ import org.thingsboard.server.common.data.alarm.AlarmInfo; |
35 | 35 | import org.thingsboard.server.common.data.asset.Asset; |
36 | 36 | import org.thingsboard.server.common.data.asset.AssetInfo; |
37 | 37 | import org.thingsboard.server.common.data.audit.ActionType; |
38 | +import org.thingsboard.server.common.data.edge.Edge; | |
38 | 39 | import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; |
39 | 40 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
40 | 41 | import org.thingsboard.server.common.data.id.AssetId; |
41 | 42 | import org.thingsboard.server.common.data.id.CustomerId; |
42 | 43 | import org.thingsboard.server.common.data.id.DashboardId; |
43 | 44 | import org.thingsboard.server.common.data.id.DeviceId; |
45 | +import org.thingsboard.server.common.data.id.EdgeId; | |
44 | 46 | import org.thingsboard.server.common.data.id.EntityId; |
45 | 47 | import org.thingsboard.server.common.data.id.EntityIdFactory; |
46 | 48 | import org.thingsboard.server.common.data.id.EntityViewId; |
... | ... | @@ -75,6 +77,7 @@ import org.thingsboard.server.dao.dashboard.DashboardService; |
75 | 77 | import org.thingsboard.server.dao.device.ClaimDevicesService; |
76 | 78 | import org.thingsboard.server.dao.device.DeviceCredentialsService; |
77 | 79 | import org.thingsboard.server.dao.device.DeviceService; |
80 | +import org.thingsboard.server.dao.edge.EdgeService; | |
78 | 81 | import org.thingsboard.server.dao.entityview.EntityViewService; |
79 | 82 | import org.thingsboard.server.dao.exception.DataValidationException; |
80 | 83 | import org.thingsboard.server.dao.exception.IncorrectParameterException; |
... | ... | @@ -178,6 +181,9 @@ public abstract class BaseController { |
178 | 181 | @Autowired |
179 | 182 | protected ClaimDevicesService claimDevicesService; |
180 | 183 | |
184 | + @Autowired | |
185 | + protected EdgeService edgeService; | |
186 | + | |
181 | 187 | @Value("${server.log_controller_error_stack_trace}") |
182 | 188 | @Getter |
183 | 189 | private boolean logControllerErrorStackTrace; |
... | ... | @@ -353,6 +359,9 @@ public abstract class BaseController { |
353 | 359 | case ENTITY_VIEW: |
354 | 360 | checkEntityViewId(new EntityViewId(entityId.getId()), operation); |
355 | 361 | return; |
362 | + case EDGE: | |
363 | + checkEdgeId(new EdgeId(entityId.getId()), operation); | |
364 | + return; | |
356 | 365 | default: |
357 | 366 | throw new IllegalArgumentException("Unsupported entity type: " + entityId.getEntityType()); |
358 | 367 | } |
... | ... | @@ -548,6 +557,17 @@ public abstract class BaseController { |
548 | 557 | return ruleNode; |
549 | 558 | } |
550 | 559 | |
560 | + Edge checkEdgeId(EdgeId edgeId, Operation operation) throws ThingsboardException { | |
561 | + try { | |
562 | + validateId(edgeId, "Incorrect edgeId " + edgeId); | |
563 | + Edge edge = edgeService.findEdgeById(getTenantId(), edgeId); | |
564 | + checkNotNull(edge); | |
565 | + accessControlService.checkPermission(getCurrentUser(), Resource.EDGE, operation, edgeId, edge); | |
566 | + return edge; | |
567 | + } catch (Exception e) { | |
568 | + throw handleException(e, false); | |
569 | + } | |
570 | + } | |
551 | 571 | |
552 | 572 | protected String constructBaseUrl(HttpServletRequest request) { |
553 | 573 | String scheme = request.getScheme(); |
... | ... | @@ -637,6 +657,12 @@ public abstract class BaseController { |
637 | 657 | case ALARM_CLEAR: |
638 | 658 | msgType = DataConstants.ALARM_CLEAR; |
639 | 659 | break; |
660 | + case ASSIGNED_TO_EDGE: | |
661 | + msgType = DataConstants.ENTITY_ASSIGNED_TO_EDGE; | |
662 | + break; | |
663 | + case UNASSIGNED_FROM_EDGE: | |
664 | + msgType = DataConstants.ENTITY_UNASSIGNED_FROM_EDGE; | |
665 | + break; | |
640 | 666 | } |
641 | 667 | if (!StringUtils.isEmpty(msgType)) { |
642 | 668 | try { |
... | ... | @@ -656,6 +682,12 @@ public abstract class BaseController { |
656 | 682 | String strCustomerName = extractParameter(String.class, 2, additionalInfo); |
657 | 683 | metaData.putValue("unassignedCustomerId", strCustomerId); |
658 | 684 | metaData.putValue("unassignedCustomerName", strCustomerName); |
685 | + } else if (actionType == ActionType.ASSIGNED_TO_EDGE) { | |
686 | + String strEdgeId = extractParameter(String.class, 1, additionalInfo); | |
687 | + metaData.putValue("assignedEdgeId", strEdgeId); | |
688 | + } else if (actionType == ActionType.UNASSIGNED_FROM_EDGE) { | |
689 | + String strEdgeId = extractParameter(String.class, 1, additionalInfo); | |
690 | + metaData.putValue("unassignedEdgeId", strEdgeId); | |
659 | 691 | } |
660 | 692 | ObjectNode entityNode; |
661 | 693 | if (entity != null) { | ... | ... |
... | ... | @@ -32,10 +32,13 @@ import org.thingsboard.server.common.data.Dashboard; |
32 | 32 | import org.thingsboard.server.common.data.DashboardInfo; |
33 | 33 | import org.thingsboard.server.common.data.EntityType; |
34 | 34 | import org.thingsboard.server.common.data.ShortCustomerInfo; |
35 | +import org.thingsboard.server.common.data.ShortEdgeInfo; | |
35 | 36 | import org.thingsboard.server.common.data.audit.ActionType; |
37 | +import org.thingsboard.server.common.data.edge.Edge; | |
36 | 38 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
37 | 39 | import org.thingsboard.server.common.data.id.CustomerId; |
38 | 40 | import org.thingsboard.server.common.data.id.DashboardId; |
41 | +import org.thingsboard.server.common.data.id.EdgeId; | |
39 | 42 | import org.thingsboard.server.common.data.id.TenantId; |
40 | 43 | import org.thingsboard.server.common.data.page.PageData; |
41 | 44 | import org.thingsboard.server.common.data.page.PageLink; |
... | ... | @@ -474,4 +477,241 @@ public class DashboardController extends BaseController { |
474 | 477 | throw handleException(e); |
475 | 478 | } |
476 | 479 | } |
480 | + | |
481 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | |
482 | + @RequestMapping(value = "/edge/{edgeId}/dashboard/{dashboardId}", method = RequestMethod.POST) | |
483 | + @ResponseBody | |
484 | + public Dashboard assignDashboardToEdge(@PathVariable("edgeId") String strEdgeId, | |
485 | + @PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException { | |
486 | + checkParameter("edgeId", strEdgeId); | |
487 | + checkParameter(DASHBOARD_ID, strDashboardId); | |
488 | + try { | |
489 | + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); | |
490 | + Edge edge = checkEdgeId(edgeId, Operation.READ); | |
491 | + | |
492 | + DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); | |
493 | + checkDashboardId(dashboardId, Operation.ASSIGN_TO_EDGE); | |
494 | + | |
495 | + Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToEdge(getCurrentUser().getTenantId(), dashboardId, edgeId)); | |
496 | + | |
497 | + logEntityAction(dashboardId, savedDashboard, | |
498 | + null, | |
499 | + ActionType.ASSIGNED_TO_EDGE, null, strDashboardId, strEdgeId, edge.getName()); | |
500 | + | |
501 | + | |
502 | + return savedDashboard; | |
503 | + } catch (Exception e) { | |
504 | + | |
505 | + logEntityAction(emptyId(EntityType.DASHBOARD), null, | |
506 | + null, | |
507 | + ActionType.ASSIGNED_TO_EDGE, e, strDashboardId, strEdgeId); | |
508 | + | |
509 | + throw handleException(e); | |
510 | + } | |
511 | + } | |
512 | + | |
513 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | |
514 | + @RequestMapping(value = "/edge/{edgeId}/dashboard/{dashboardId}", method = RequestMethod.DELETE) | |
515 | + @ResponseBody | |
516 | + public Dashboard unassignDashboardFromEdge(@PathVariable("edgeId") String strEdgeId, | |
517 | + @PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException { | |
518 | + checkParameter("edgeId", strEdgeId); | |
519 | + checkParameter(DASHBOARD_ID, strDashboardId); | |
520 | + try { | |
521 | + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); | |
522 | + Edge edge = checkEdgeId(edgeId, Operation.READ); | |
523 | + DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); | |
524 | + Dashboard dashboard = checkDashboardId(dashboardId, Operation.UNASSIGN_FROM_EDGE); | |
525 | + | |
526 | + Dashboard savedDashboard = checkNotNull(dashboardService.unassignDashboardFromEdge(getCurrentUser().getTenantId(), dashboardId, edgeId)); | |
527 | + | |
528 | + logEntityAction(dashboardId, dashboard, | |
529 | + null, | |
530 | + ActionType.UNASSIGNED_FROM_EDGE, null, strDashboardId, edge.getId().toString(), edge.getName()); | |
531 | + | |
532 | + return savedDashboard; | |
533 | + } catch (Exception e) { | |
534 | + | |
535 | + logEntityAction(emptyId(EntityType.DASHBOARD), null, | |
536 | + null, | |
537 | + ActionType.UNASSIGNED_FROM_EDGE, e, strDashboardId); | |
538 | + | |
539 | + throw handleException(e); | |
540 | + } | |
541 | + } | |
542 | + | |
543 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | |
544 | + @RequestMapping(value = "/dashboard/{dashboardId}/edges", method = RequestMethod.POST) | |
545 | + @ResponseBody | |
546 | + public Dashboard updateDashboardEdges(@PathVariable(DASHBOARD_ID) String strDashboardId, | |
547 | + @RequestBody String[] strEdgeIds) throws ThingsboardException { | |
548 | + checkParameter(DASHBOARD_ID, strDashboardId); | |
549 | + try { | |
550 | + DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); | |
551 | + Dashboard dashboard = checkDashboardId(dashboardId, Operation.ASSIGN_TO_EDGE); | |
552 | + | |
553 | + Set<EdgeId> edgeIds = new HashSet<>(); | |
554 | + if (strEdgeIds != null) { | |
555 | + for (String strEdgeId : strEdgeIds) { | |
556 | + edgeIds.add(new EdgeId(toUUID(strEdgeId))); | |
557 | + } | |
558 | + } | |
559 | + | |
560 | + Set<EdgeId> addedEdgeIds = new HashSet<>(); | |
561 | + Set<EdgeId> removedEdgeIds = new HashSet<>(); | |
562 | + for (EdgeId edgeId : edgeIds) { | |
563 | + if (!dashboard.isAssignedToEdge(edgeId)) { | |
564 | + addedEdgeIds.add(edgeId); | |
565 | + } | |
566 | + } | |
567 | + | |
568 | + Set<ShortEdgeInfo> assignedEdges = dashboard.getAssignedEdges(); | |
569 | + if (assignedEdges != null) { | |
570 | + for (ShortEdgeInfo edgeInfo : assignedEdges) { | |
571 | + if (!edgeIds.contains(edgeInfo.getEdgeId())) { | |
572 | + removedEdgeIds.add(edgeInfo.getEdgeId()); | |
573 | + } | |
574 | + } | |
575 | + } | |
576 | + | |
577 | + if (addedEdgeIds.isEmpty() && removedEdgeIds.isEmpty()) { | |
578 | + return dashboard; | |
579 | + } else { | |
580 | + Dashboard savedDashboard = null; | |
581 | + for (EdgeId edgeId : addedEdgeIds) { | |
582 | + savedDashboard = checkNotNull(dashboardService.assignDashboardToEdge(getCurrentUser().getTenantId(), dashboardId, edgeId)); | |
583 | + ShortEdgeInfo edgeInfo = savedDashboard.getAssignedEdgeInfo(edgeId); | |
584 | + logEntityAction(dashboardId, savedDashboard, | |
585 | + null, | |
586 | + ActionType.ASSIGNED_TO_EDGE, null, strDashboardId, edgeId.toString(), edgeInfo.getTitle()); | |
587 | + } | |
588 | + for (EdgeId edgeId : removedEdgeIds) { | |
589 | + ShortEdgeInfo edgeInfo = dashboard.getAssignedEdgeInfo(edgeId); | |
590 | + savedDashboard = checkNotNull(dashboardService.unassignDashboardFromEdge(getCurrentUser().getTenantId(), dashboardId, edgeId)); | |
591 | + logEntityAction(dashboardId, dashboard, | |
592 | + null, | |
593 | + ActionType.UNASSIGNED_FROM_EDGE, null, strDashboardId, edgeId.toString(), edgeInfo.getTitle()); | |
594 | + | |
595 | + } | |
596 | + return savedDashboard; | |
597 | + } | |
598 | + } catch (Exception e) { | |
599 | + | |
600 | + logEntityAction(emptyId(EntityType.DASHBOARD), null, | |
601 | + null, | |
602 | + ActionType.ASSIGNED_TO_EDGE, e, strDashboardId); | |
603 | + | |
604 | + throw handleException(e); | |
605 | + } | |
606 | + } | |
607 | + | |
608 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | |
609 | + @RequestMapping(value = "/dashboard/{dashboardId}/edges/add", method = RequestMethod.POST) | |
610 | + @ResponseBody | |
611 | + public Dashboard addDashboardEdges(@PathVariable(DASHBOARD_ID) String strDashboardId, | |
612 | + @RequestBody String[] strEdgeIds) throws ThingsboardException { | |
613 | + checkParameter(DASHBOARD_ID, strDashboardId); | |
614 | + try { | |
615 | + DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); | |
616 | + Dashboard dashboard = checkDashboardId(dashboardId, Operation.ASSIGN_TO_EDGE); | |
617 | + | |
618 | + Set<EdgeId> edgeIds = new HashSet<>(); | |
619 | + if (strEdgeIds != null) { | |
620 | + for (String strEdgeId : strEdgeIds) { | |
621 | + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); | |
622 | + if (!dashboard.isAssignedToEdge(edgeId)) { | |
623 | + edgeIds.add(edgeId); | |
624 | + } | |
625 | + } | |
626 | + } | |
627 | + | |
628 | + if (edgeIds.isEmpty()) { | |
629 | + return dashboard; | |
630 | + } else { | |
631 | + Dashboard savedDashboard = null; | |
632 | + for (EdgeId edgeId : edgeIds) { | |
633 | + savedDashboard = checkNotNull(dashboardService.assignDashboardToEdge(getCurrentUser().getTenantId(), dashboardId, edgeId)); | |
634 | + ShortEdgeInfo edgeInfo = savedDashboard.getAssignedEdgeInfo(edgeId); | |
635 | + logEntityAction(dashboardId, savedDashboard, | |
636 | + null, | |
637 | + ActionType.ASSIGNED_TO_EDGE, null, strDashboardId, edgeId.toString(), edgeInfo.getTitle()); | |
638 | + } | |
639 | + return savedDashboard; | |
640 | + } | |
641 | + } catch (Exception e) { | |
642 | + | |
643 | + logEntityAction(emptyId(EntityType.DASHBOARD), null, | |
644 | + null, | |
645 | + ActionType.ASSIGNED_TO_EDGE, e, strDashboardId); | |
646 | + | |
647 | + throw handleException(e); | |
648 | + } | |
649 | + } | |
650 | + | |
651 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | |
652 | + @RequestMapping(value = "/dashboard/{dashboardId}/edges/remove", method = RequestMethod.POST) | |
653 | + @ResponseBody | |
654 | + public Dashboard removeDashboardEdges(@PathVariable(DASHBOARD_ID) String strDashboardId, | |
655 | + @RequestBody String[] strEdgeIds) throws ThingsboardException { | |
656 | + checkParameter(DASHBOARD_ID, strDashboardId); | |
657 | + try { | |
658 | + DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); | |
659 | + Dashboard dashboard = checkDashboardId(dashboardId, Operation.UNASSIGN_FROM_EDGE); | |
660 | + | |
661 | + Set<EdgeId> edgeIds = new HashSet<>(); | |
662 | + if (strEdgeIds != null) { | |
663 | + for (String strEdgeId : strEdgeIds) { | |
664 | + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); | |
665 | + if (dashboard.isAssignedToEdge(edgeId)) { | |
666 | + edgeIds.add(edgeId); | |
667 | + } | |
668 | + } | |
669 | + } | |
670 | + | |
671 | + if (edgeIds.isEmpty()) { | |
672 | + return dashboard; | |
673 | + } else { | |
674 | + Dashboard savedDashboard = null; | |
675 | + for (EdgeId edgeId : edgeIds) { | |
676 | + ShortEdgeInfo edgeInfo = dashboard.getAssignedEdgeInfo(edgeId); | |
677 | + savedDashboard = checkNotNull(dashboardService.unassignDashboardFromEdge(getCurrentUser().getTenantId(), dashboardId, edgeId)); | |
678 | + logEntityAction(dashboardId, dashboard, | |
679 | + null, | |
680 | + ActionType.UNASSIGNED_FROM_EDGE, null, strDashboardId, edgeId.toString(), edgeInfo.getTitle()); | |
681 | + | |
682 | + } | |
683 | + return savedDashboard; | |
684 | + } | |
685 | + } catch (Exception e) { | |
686 | + | |
687 | + logEntityAction(emptyId(EntityType.DASHBOARD), null, | |
688 | + null, | |
689 | + ActionType.UNASSIGNED_FROM_EDGE, e, strDashboardId); | |
690 | + | |
691 | + throw handleException(e); | |
692 | + } | |
693 | + } | |
694 | + | |
695 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | |
696 | + @RequestMapping(value = "/edge/{edgeId}/dashboards", params = {"pageSize", "page"}, method = RequestMethod.GET) | |
697 | + @ResponseBody | |
698 | + public PageData<DashboardInfo> getEdgeDashboards( | |
699 | + @PathVariable("edgeId") String strEdgeId, | |
700 | + @RequestParam int pageSize, | |
701 | + @RequestParam int page, | |
702 | + @RequestParam(required = false) String type, | |
703 | + @RequestParam(required = false) String textSearch, | |
704 | + @RequestParam(required = false) String sortProperty, | |
705 | + @RequestParam(required = false) String sortOrder) throws ThingsboardException { | |
706 | + checkParameter("edgeId", strEdgeId); | |
707 | + try { | |
708 | + TenantId tenantId = getCurrentUser().getTenantId(); | |
709 | + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); | |
710 | + checkEdgeId(edgeId, Operation.READ); | |
711 | + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); | |
712 | + return checkNotNull(dashboardService.findDashboardsByTenantIdAndEdgeId(tenantId, edgeId, pageLink)); | |
713 | + } catch (Exception e) { | |
714 | + throw handleException(e); | |
715 | + } | |
716 | + } | |
477 | 717 | } | ... | ... |
... | ... | @@ -33,9 +33,11 @@ import org.springframework.web.context.request.async.DeferredResult; |
33 | 33 | import org.thingsboard.server.common.data.*; |
34 | 34 | import org.thingsboard.server.common.data.audit.ActionType; |
35 | 35 | import org.thingsboard.server.common.data.device.DeviceSearchQuery; |
36 | +import org.thingsboard.server.common.data.edge.Edge; | |
36 | 37 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
37 | 38 | import org.thingsboard.server.common.data.id.CustomerId; |
38 | 39 | import org.thingsboard.server.common.data.id.DeviceId; |
40 | +import org.thingsboard.server.common.data.id.EdgeId; | |
39 | 41 | import org.thingsboard.server.common.data.id.TenantId; |
40 | 42 | import org.thingsboard.server.common.data.page.PageData; |
41 | 43 | import org.thingsboard.server.common.data.page.PageLink; |
... | ... | @@ -55,6 +57,8 @@ import java.util.ArrayList; |
55 | 57 | import java.util.List; |
56 | 58 | import java.util.stream.Collectors; |
57 | 59 | |
60 | +import static org.thingsboard.server.controller.EdgeController.EDGE_ID; | |
61 | + | |
58 | 62 | @RestController |
59 | 63 | @RequestMapping("/api") |
60 | 64 | public class DeviceController extends BaseController { |
... | ... | @@ -541,4 +545,88 @@ public class DeviceController extends BaseController { |
541 | 545 | } |
542 | 546 | return DataConstants.DEFAULT_SECRET_KEY; |
543 | 547 | } |
548 | + | |
549 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | |
550 | + @RequestMapping(value = "/edge/{edgeId}/device/{deviceId}", method = RequestMethod.POST) | |
551 | + @ResponseBody | |
552 | + public Device assignDeviceToEdge(@PathVariable(EDGE_ID) String strEdgeId, | |
553 | + @PathVariable(DEVICE_ID) String strDeviceId) throws ThingsboardException { | |
554 | + checkParameter(EDGE_ID, strEdgeId); | |
555 | + checkParameter(DEVICE_ID, strDeviceId); | |
556 | + try { | |
557 | + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); | |
558 | + Edge edge = checkEdgeId(edgeId, Operation.READ); | |
559 | + | |
560 | + DeviceId deviceId = new DeviceId(toUUID(strDeviceId)); | |
561 | + checkDeviceId(deviceId, Operation.ASSIGN_TO_EDGE); | |
562 | + | |
563 | + Device savedDevice = checkNotNull(deviceService.assignDeviceToEdge(getCurrentUser().getTenantId(), deviceId, edgeId)); | |
564 | + | |
565 | + logEntityAction(deviceId, savedDevice, | |
566 | + savedDevice.getCustomerId(), | |
567 | + ActionType.ASSIGNED_TO_EDGE, null, strDeviceId, strEdgeId, edge.getName()); | |
568 | + | |
569 | + return savedDevice; | |
570 | + } catch (Exception e) { | |
571 | + logEntityAction(emptyId(EntityType.DEVICE), null, | |
572 | + null, | |
573 | + ActionType.ASSIGNED_TO_EDGE, e, strDeviceId, strEdgeId); | |
574 | + throw handleException(e); | |
575 | + } | |
576 | + } | |
577 | + | |
578 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | |
579 | + @RequestMapping(value = "/edge/device/{deviceId}", method = RequestMethod.DELETE) | |
580 | + @ResponseBody | |
581 | + public Device unassignDeviceFromEdge(@PathVariable(DEVICE_ID) String strDeviceId) throws ThingsboardException { | |
582 | + checkParameter(DEVICE_ID, strDeviceId); | |
583 | + try { | |
584 | + DeviceId deviceId = new DeviceId(toUUID(strDeviceId)); | |
585 | + Device device = checkDeviceId(deviceId, Operation.UNASSIGN_FROM_EDGE); | |
586 | + if (device.getEdgeId() == null || device.getEdgeId().getId().equals(ModelConstants.NULL_UUID)) { | |
587 | + throw new IncorrectParameterException("Device isn't assigned to any edge!"); | |
588 | + } | |
589 | + Edge edge = checkEdgeId(device.getEdgeId(), Operation.READ); | |
590 | + | |
591 | + Device savedDevice = checkNotNull(deviceService.unassignDeviceFromEdge(getCurrentUser().getTenantId(), deviceId)); | |
592 | + | |
593 | + logEntityAction(deviceId, device, | |
594 | + device.getCustomerId(), | |
595 | + ActionType.UNASSIGNED_FROM_EDGE, null, strDeviceId, edge.getId().toString(), edge.getName()); | |
596 | + | |
597 | + return savedDevice; | |
598 | + } catch (Exception e) { | |
599 | + logEntityAction(emptyId(EntityType.DEVICE), null, | |
600 | + null, | |
601 | + ActionType.UNASSIGNED_FROM_EDGE, e, strDeviceId); | |
602 | + throw handleException(e); | |
603 | + } | |
604 | + } | |
605 | + | |
606 | + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") | |
607 | + @RequestMapping(value = "/edge/{edgeId}/devices", params = {"pageSize", "page"}, method = RequestMethod.GET) | |
608 | + @ResponseBody | |
609 | + public PageData<Device> getEdgeDevices( | |
610 | + @PathVariable("edgeId") String strEdgeId, | |
611 | + @RequestParam int pageSize, | |
612 | + @RequestParam int page, | |
613 | + @RequestParam(required = false) String type, | |
614 | + @RequestParam(required = false) String textSearch, | |
615 | + @RequestParam(required = false) String sortProperty, | |
616 | + @RequestParam(required = false) String sortOrder) throws ThingsboardException { | |
617 | + checkParameter("edgeId", strEdgeId); | |
618 | + try { | |
619 | + TenantId tenantId = getCurrentUser().getTenantId(); | |
620 | + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); | |
621 | + checkEdgeId(edgeId, Operation.READ); | |
622 | + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); | |
623 | + if (type != null && type.trim().length()>0) { | |
624 | + return checkNotNull(deviceService.findDevicesByTenantIdAndEdgeIdAndType(tenantId, edgeId, type, pageLink)); | |
625 | + } else { | |
626 | + return checkNotNull(deviceService.findDevicesByTenantIdAndEdgeId(tenantId, edgeId, pageLink)); | |
627 | + } | |
628 | + } catch (Exception e) { | |
629 | + throw handleException(e); | |
630 | + } | |
631 | + } | |
544 | 632 | } | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2020 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.controller; | |
17 | + | |
18 | +import com.google.common.util.concurrent.ListenableFuture; | |
19 | +import org.springframework.http.HttpStatus; | |
20 | +import org.springframework.security.access.prepost.PreAuthorize; | |
21 | +import org.springframework.web.bind.annotation.PathVariable; | |
22 | +import org.springframework.web.bind.annotation.RequestBody; | |
23 | +import org.springframework.web.bind.annotation.RequestMapping; | |
24 | +import org.springframework.web.bind.annotation.RequestMethod; | |
25 | +import org.springframework.web.bind.annotation.RequestParam; | |
26 | +import org.springframework.web.bind.annotation.ResponseBody; | |
27 | +import org.springframework.web.bind.annotation.ResponseStatus; | |
28 | +import org.springframework.web.bind.annotation.RestController; | |
29 | +import org.thingsboard.server.common.data.Customer; | |
30 | +import org.thingsboard.server.common.data.EntitySubtype; | |
31 | +import org.thingsboard.server.common.data.EntityType; | |
32 | +import org.thingsboard.server.common.data.audit.ActionType; | |
33 | +import org.thingsboard.server.common.data.edge.Edge; | |
34 | +import org.thingsboard.server.common.data.edge.EdgeSearchQuery; | |
35 | +import org.thingsboard.server.common.data.exception.ThingsboardException; | |
36 | +import org.thingsboard.server.common.data.id.CustomerId; | |
37 | +import org.thingsboard.server.common.data.id.EdgeId; | |
38 | +import org.thingsboard.server.common.data.id.RuleChainId; | |
39 | +import org.thingsboard.server.common.data.id.TenantId; | |
40 | +import org.thingsboard.server.common.data.page.PageData; | |
41 | +import org.thingsboard.server.common.data.page.PageLink; | |
42 | +import org.thingsboard.server.common.data.rule.RuleChain; | |
43 | +import org.thingsboard.server.dao.exception.IncorrectParameterException; | |
44 | +import org.thingsboard.server.dao.model.ModelConstants; | |
45 | +import org.thingsboard.server.service.security.model.SecurityUser; | |
46 | +import org.thingsboard.server.service.security.permission.Operation; | |
47 | +import org.thingsboard.server.service.security.permission.Resource; | |
48 | + | |
49 | +import java.util.ArrayList; | |
50 | +import java.util.List; | |
51 | +import java.util.stream.Collectors; | |
52 | + | |
53 | +@RestController | |
54 | +@RequestMapping("/api") | |
55 | +public class EdgeController extends BaseController { | |
56 | + | |
57 | + public static final String EDGE_ID = "edgeId"; | |
58 | + | |
59 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | |
60 | + @RequestMapping(value = "/edge/{edgeId}", method = RequestMethod.GET) | |
61 | + @ResponseBody | |
62 | + public Edge getEdgeById(@PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException { | |
63 | + checkParameter(EDGE_ID, strEdgeId); | |
64 | + try { | |
65 | + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); | |
66 | + return checkEdgeId(edgeId, Operation.READ); | |
67 | + } catch (Exception e) { | |
68 | + throw handleException(e); | |
69 | + } | |
70 | + } | |
71 | + | |
72 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | |
73 | + @RequestMapping(value = "/edge", method = RequestMethod.POST) | |
74 | + @ResponseBody | |
75 | + public Edge saveEdge(@RequestBody Edge edge) throws ThingsboardException { | |
76 | + try { | |
77 | + TenantId tenantId = getCurrentUser().getTenantId(); | |
78 | + edge.setTenantId(tenantId); | |
79 | + boolean created = edge.getId() == null; | |
80 | + | |
81 | + Operation operation = created ? Operation.CREATE : Operation.WRITE; | |
82 | + | |
83 | + accessControlService.checkPermission(getCurrentUser(), Resource.EDGE, operation, | |
84 | + edge.getId(), edge); | |
85 | + | |
86 | + Edge result = checkNotNull(edgeService.saveEdge(edge)); | |
87 | + | |
88 | + if (created) { | |
89 | + RuleChain rootTenantRuleChain = ruleChainService.getRootTenantRuleChain(tenantId); | |
90 | + ruleChainService.assignRuleChainToEdge(tenantId, rootTenantRuleChain.getId(), result.getId()); | |
91 | + edgeService.setRootRuleChain(tenantId, result, rootTenantRuleChain.getId()); | |
92 | + } | |
93 | + | |
94 | + logEntityAction(result.getId(), result, null, created ? ActionType.ADDED : ActionType.UPDATED, null); | |
95 | + return result; | |
96 | + } catch (Exception e) { | |
97 | + logEntityAction(emptyId(EntityType.EDGE), edge, | |
98 | + null, edge.getId() == null ? ActionType.ADDED : ActionType.UPDATED, e); | |
99 | + throw handleException(e); | |
100 | + } | |
101 | + } | |
102 | + | |
103 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | |
104 | + @RequestMapping(value = "/edge/{edgeId}", method = RequestMethod.DELETE) | |
105 | + @ResponseStatus(value = HttpStatus.OK) | |
106 | + public void deleteEdge(@PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException { | |
107 | + checkParameter(EDGE_ID, strEdgeId); | |
108 | + try { | |
109 | + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); | |
110 | + Edge edge = checkEdgeId(edgeId, Operation.DELETE); | |
111 | + edgeService.deleteEdge(getTenantId(), edgeId); | |
112 | + | |
113 | + logEntityAction(edgeId, edge, | |
114 | + null, | |
115 | + ActionType.DELETED, null, strEdgeId); | |
116 | + | |
117 | + } catch (Exception e) { | |
118 | + | |
119 | + logEntityAction(emptyId(EntityType.EDGE), | |
120 | + null, | |
121 | + null, | |
122 | + ActionType.DELETED, e, strEdgeId); | |
123 | + | |
124 | + throw handleException(e); | |
125 | + } | |
126 | + } | |
127 | + | |
128 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | |
129 | + @RequestMapping(value = "/edges", params = {"pageSize", "page"}, method = RequestMethod.GET) | |
130 | + @ResponseBody | |
131 | + public PageData<Edge> getEdges(@RequestParam int pageSize, | |
132 | + @RequestParam int page, | |
133 | + @RequestParam(required = false) String textSearch, | |
134 | + @RequestParam(required = false) String sortProperty, | |
135 | + @RequestParam(required = false) String sortOrder) throws ThingsboardException { | |
136 | + try { | |
137 | + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); | |
138 | + TenantId tenantId = getCurrentUser().getTenantId(); | |
139 | + return checkNotNull(edgeService.findEdgesByTenantId(tenantId, pageLink)); | |
140 | + } catch (Exception e) { | |
141 | + throw handleException(e); | |
142 | + } | |
143 | + } | |
144 | + | |
145 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | |
146 | + @RequestMapping(value = "/customer/{customerId}/edge/{edgeId}", method = RequestMethod.POST) | |
147 | + @ResponseBody | |
148 | + public Edge assignEdgeToCustomer(@PathVariable("customerId") String strCustomerId, | |
149 | + @PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException { | |
150 | + checkParameter("customerId", strCustomerId); | |
151 | + checkParameter(EDGE_ID, strEdgeId); | |
152 | + try { | |
153 | + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); | |
154 | + Customer customer = checkCustomerId(customerId, Operation.READ); | |
155 | + | |
156 | + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); | |
157 | + checkEdgeId(edgeId, Operation.ASSIGN_TO_CUSTOMER); | |
158 | + | |
159 | + Edge savedEdge = checkNotNull(edgeService.assignEdgeToCustomer(getCurrentUser().getTenantId(), edgeId, customerId)); | |
160 | + | |
161 | + logEntityAction(edgeId, savedEdge, | |
162 | + savedEdge.getCustomerId(), | |
163 | + ActionType.ASSIGNED_TO_CUSTOMER, null, strEdgeId, strCustomerId, customer.getName()); | |
164 | + | |
165 | + return savedEdge; | |
166 | + } catch (Exception e) { | |
167 | + logEntityAction(emptyId(EntityType.EDGE), null, | |
168 | + null, | |
169 | + ActionType.ASSIGNED_TO_CUSTOMER, e, strEdgeId, strCustomerId); | |
170 | + throw handleException(e); | |
171 | + } | |
172 | + } | |
173 | + | |
174 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | |
175 | + @RequestMapping(value = "/customer/edge/{edgeId}", method = RequestMethod.DELETE) | |
176 | + @ResponseBody | |
177 | + public Edge unassignEdgeFromCustomer(@PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException { | |
178 | + checkParameter(EDGE_ID, strEdgeId); | |
179 | + try { | |
180 | + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); | |
181 | + Edge edge = checkEdgeId(edgeId, Operation.UNASSIGN_FROM_CUSTOMER); | |
182 | + if (edge.getCustomerId() == null || edge.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) { | |
183 | + throw new IncorrectParameterException("Edge isn't assigned to any customer!"); | |
184 | + } | |
185 | + Customer customer = checkCustomerId(edge.getCustomerId(), Operation.READ); | |
186 | + | |
187 | + Edge savedEdge = checkNotNull(edgeService.unassignEdgeFromCustomer(getCurrentUser().getTenantId(), edgeId)); | |
188 | + | |
189 | + logEntityAction(edgeId, edge, | |
190 | + edge.getCustomerId(), | |
191 | + ActionType.UNASSIGNED_FROM_CUSTOMER, null, strEdgeId, customer.getId().toString(), customer.getName()); | |
192 | + | |
193 | + return savedEdge; | |
194 | + } catch (Exception e) { | |
195 | + logEntityAction(emptyId(EntityType.EDGE), null, | |
196 | + null, | |
197 | + ActionType.UNASSIGNED_FROM_CUSTOMER, e, strEdgeId); | |
198 | + throw handleException(e); | |
199 | + } | |
200 | + } | |
201 | + | |
202 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | |
203 | + @RequestMapping(value = "/customer/public/edge/{edgeId}", method = RequestMethod.POST) | |
204 | + @ResponseBody | |
205 | + public Edge assignEdgeToPublicCustomer(@PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException { | |
206 | + checkParameter(EDGE_ID, strEdgeId); | |
207 | + try { | |
208 | + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); | |
209 | + Edge edge = checkEdgeId(edgeId, Operation.ASSIGN_TO_CUSTOMER); | |
210 | + Customer publicCustomer = customerService.findOrCreatePublicCustomer(edge.getTenantId()); | |
211 | + Edge savedEdge = checkNotNull(edgeService.assignEdgeToCustomer(getCurrentUser().getTenantId(), edgeId, publicCustomer.getId())); | |
212 | + | |
213 | + logEntityAction(edgeId, savedEdge, | |
214 | + savedEdge.getCustomerId(), | |
215 | + ActionType.ASSIGNED_TO_CUSTOMER, null, strEdgeId, publicCustomer.getId().toString(), publicCustomer.getName()); | |
216 | + | |
217 | + return savedEdge; | |
218 | + } catch (Exception e) { | |
219 | + logEntityAction(emptyId(EntityType.EDGE), null, | |
220 | + null, | |
221 | + ActionType.ASSIGNED_TO_CUSTOMER, e, strEdgeId); | |
222 | + throw handleException(e); | |
223 | + } | |
224 | + } | |
225 | + | |
226 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | |
227 | + @RequestMapping(value = "/tenant/edges", params = {"pageSize", "page"}, method = RequestMethod.GET) | |
228 | + @ResponseBody | |
229 | + public PageData<Edge> getTenantEdges( | |
230 | + @RequestParam int pageSize, | |
231 | + @RequestParam int page, | |
232 | + @RequestParam(required = false) String type, | |
233 | + @RequestParam(required = false) String textSearch, | |
234 | + @RequestParam(required = false) String sortProperty, | |
235 | + @RequestParam(required = false) String sortOrder) throws ThingsboardException { | |
236 | + try { | |
237 | + TenantId tenantId = getCurrentUser().getTenantId(); | |
238 | + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); | |
239 | + if (type != null && type.trim().length() > 0) { | |
240 | + return checkNotNull(edgeService.findEdgesByTenantIdAndType(tenantId, type, pageLink)); | |
241 | + } else { | |
242 | + return checkNotNull(edgeService.findEdgesByTenantId(tenantId, pageLink)); | |
243 | + } | |
244 | + } catch (Exception e) { | |
245 | + throw handleException(e); | |
246 | + } | |
247 | + } | |
248 | + | |
249 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | |
250 | + @RequestMapping(value = "/tenant/edges", params = {"edgeName"}, method = RequestMethod.GET) | |
251 | + @ResponseBody | |
252 | + public Edge getTenantEdge( | |
253 | + @RequestParam String edgeName) throws ThingsboardException { | |
254 | + try { | |
255 | + TenantId tenantId = getCurrentUser().getTenantId(); | |
256 | + return checkNotNull(edgeService.findEdgeByTenantIdAndName(tenantId, edgeName)); | |
257 | + } catch (Exception e) { | |
258 | + throw handleException(e); | |
259 | + } | |
260 | + } | |
261 | + | |
262 | + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") | |
263 | + @RequestMapping(value = "/edge/{edgeId}/{ruleChainId}/root", method = RequestMethod.POST) | |
264 | + @ResponseBody | |
265 | + public Edge setRootRuleChain(@PathVariable(EDGE_ID) String strEdgeId, | |
266 | + @PathVariable("ruleChainId") String strRuleChainId) throws ThingsboardException { | |
267 | + checkParameter(EDGE_ID, strEdgeId); | |
268 | + checkParameter("ruleChainId", strRuleChainId); | |
269 | + try { | |
270 | + RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); | |
271 | + checkRuleChain(ruleChainId, Operation.WRITE); | |
272 | + | |
273 | + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); | |
274 | + Edge edge = checkEdgeId(edgeId, Operation.WRITE); | |
275 | + accessControlService.checkPermission(getCurrentUser(), Resource.EDGE, Operation.WRITE, | |
276 | + edge.getId(), edge); | |
277 | + | |
278 | + Edge updatedEdge = edgeService.setRootRuleChain(getTenantId(), edge, ruleChainId); | |
279 | + | |
280 | + logEntityAction(updatedEdge.getId(), updatedEdge, null, ActionType.UPDATED, null); | |
281 | + | |
282 | + return updatedEdge; | |
283 | + } catch (Exception e) { | |
284 | + logEntityAction(emptyId(EntityType.EDGE), | |
285 | + null, | |
286 | + null, | |
287 | + ActionType.UPDATED, e, strEdgeId); | |
288 | + throw handleException(e); | |
289 | + } | |
290 | + } | |
291 | + | |
292 | + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | |
293 | + @RequestMapping(value = "/customer/{customerId}/edges", params = {"pageSize", "page"}, method = RequestMethod.GET) | |
294 | + @ResponseBody | |
295 | + public PageData<Edge> getCustomerEdges( | |
296 | + @PathVariable("customerId") String strCustomerId, | |
297 | + @RequestParam int pageSize, | |
298 | + @RequestParam int page, | |
299 | + @RequestParam(required = false) String type, | |
300 | + @RequestParam(required = false) String textSearch, | |
301 | + @RequestParam(required = false) String sortProperty, | |
302 | + @RequestParam(required = false) String sortOrder) throws ThingsboardException { | |
303 | + checkParameter("customerId", strCustomerId); | |
304 | + try { | |
305 | + TenantId tenantId = getCurrentUser().getTenantId(); | |
306 | + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); | |
307 | + checkCustomerId(customerId, Operation.READ); | |
308 | + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); | |
309 | + if (type != null && type.trim().length() > 0) { | |
310 | + return checkNotNull(edgeService.findEdgesByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink)); | |
311 | + } else { | |
312 | + return checkNotNull(edgeService.findEdgesByTenantIdAndCustomerId(tenantId, customerId, pageLink)); | |
313 | + } | |
314 | + } catch (Exception e) { | |
315 | + throw handleException(e); | |
316 | + } | |
317 | + } | |
318 | + | |
319 | + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | |
320 | + @RequestMapping(value = "/edges", params = {"edgeIds"}, method = RequestMethod.GET) | |
321 | + @ResponseBody | |
322 | + public List<Edge> getEdgesByIds( | |
323 | + @RequestParam("edgeIds") String[] strEdgeIds) throws ThingsboardException { | |
324 | + checkArrayParameter("edgeIds", strEdgeIds); | |
325 | + try { | |
326 | + SecurityUser user = getCurrentUser(); | |
327 | + TenantId tenantId = user.getTenantId(); | |
328 | + CustomerId customerId = user.getCustomerId(); | |
329 | + List<EdgeId> edgeIds = new ArrayList<>(); | |
330 | + for (String strEdgeId : strEdgeIds) { | |
331 | + edgeIds.add(new EdgeId(toUUID(strEdgeId))); | |
332 | + } | |
333 | + ListenableFuture<List<Edge>> edges; | |
334 | + if (customerId == null || customerId.isNullUid()) { | |
335 | + edges = edgeService.findEdgesByTenantIdAndIdsAsync(tenantId, edgeIds); | |
336 | + } else { | |
337 | + edges = edgeService.findEdgesByTenantIdCustomerIdAndIdsAsync(tenantId, customerId, edgeIds); | |
338 | + } | |
339 | + return checkNotNull(edges.get()); | |
340 | + } catch (Exception e) { | |
341 | + throw handleException(e); | |
342 | + } | |
343 | + } | |
344 | + | |
345 | + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | |
346 | + @RequestMapping(value = "/edges", method = RequestMethod.POST) | |
347 | + @ResponseBody | |
348 | + public List<Edge> findByQuery(@RequestBody EdgeSearchQuery query) throws ThingsboardException { | |
349 | + checkNotNull(query); | |
350 | + checkNotNull(query.getParameters()); | |
351 | + checkNotNull(query.getEdgeTypes()); | |
352 | + checkEntityId(query.getParameters().getEntityId(), Operation.READ); | |
353 | + try { | |
354 | + List<Edge> edges = checkNotNull(edgeService.findEdgesByQuery(getCurrentUser().getTenantId(), query).get()); | |
355 | + edges = edges.stream().filter(edge -> { | |
356 | + try { | |
357 | + accessControlService.checkPermission(getCurrentUser(), Resource.EDGE, Operation.READ, edge.getId(), edge); | |
358 | + return true; | |
359 | + } catch (ThingsboardException e) { | |
360 | + return false; | |
361 | + } | |
362 | + }).collect(Collectors.toList()); | |
363 | + return edges; | |
364 | + } catch (Exception e) { | |
365 | + throw handleException(e); | |
366 | + } | |
367 | + } | |
368 | + | |
369 | + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | |
370 | + @RequestMapping(value = "/edge/types", method = RequestMethod.GET) | |
371 | + @ResponseBody | |
372 | + public List<EntitySubtype> getEdgeTypes() throws ThingsboardException { | |
373 | + try { | |
374 | + SecurityUser user = getCurrentUser(); | |
375 | + TenantId tenantId = user.getTenantId(); | |
376 | + ListenableFuture<List<EntitySubtype>> edgeTypes = edgeService.findEdgeTypesByTenantId(tenantId); | |
377 | + return checkNotNull(edgeTypes.get()); | |
378 | + } catch (Exception e) { | |
379 | + throw handleException(e); | |
380 | + } | |
381 | + } | |
382 | + | |
383 | +} | |
\ No newline at end of file | ... | ... |
... | ... | @@ -31,9 +31,11 @@ import org.springframework.web.bind.annotation.ResponseStatus; |
31 | 31 | import org.springframework.web.bind.annotation.RestController; |
32 | 32 | import org.thingsboard.server.common.data.*; |
33 | 33 | import org.thingsboard.server.common.data.audit.ActionType; |
34 | +import org.thingsboard.server.common.data.edge.Edge; | |
34 | 35 | import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery; |
35 | 36 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
36 | 37 | import org.thingsboard.server.common.data.id.CustomerId; |
38 | +import org.thingsboard.server.common.data.id.EdgeId; | |
37 | 39 | import org.thingsboard.server.common.data.id.EntityId; |
38 | 40 | import org.thingsboard.server.common.data.id.EntityViewId; |
39 | 41 | import org.thingsboard.server.common.data.id.TenantId; |
... | ... | @@ -55,6 +57,7 @@ import java.util.concurrent.ExecutionException; |
55 | 57 | import java.util.stream.Collectors; |
56 | 58 | |
57 | 59 | import static org.thingsboard.server.controller.CustomerController.CUSTOMER_ID; |
60 | +import static org.thingsboard.server.controller.EdgeController.EDGE_ID; | |
58 | 61 | |
59 | 62 | /** |
60 | 63 | * Created by Victor Basanets on 8/28/2017. |
... | ... | @@ -426,4 +429,84 @@ public class EntityViewController extends BaseController { |
426 | 429 | throw handleException(e); |
427 | 430 | } |
428 | 431 | } |
432 | + | |
433 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | |
434 | + @RequestMapping(value = "/edge/{edgeId}/entityView/{entityViewId}", method = RequestMethod.POST) | |
435 | + @ResponseBody | |
436 | + public EntityView assignEntityViewToEdge(@PathVariable(EDGE_ID) String strEdgeId, | |
437 | + @PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException { | |
438 | + checkParameter(EDGE_ID, strEdgeId); | |
439 | + checkParameter(ENTITY_VIEW_ID, strEntityViewId); | |
440 | + try { | |
441 | + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); | |
442 | + Edge edge = checkEdgeId(edgeId, Operation.READ); | |
443 | + | |
444 | + EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId)); | |
445 | + checkEntityViewId(entityViewId, Operation.ASSIGN_TO_EDGE); | |
446 | + | |
447 | + EntityView savedEntityView = checkNotNull(entityViewService.assignEntityViewToEdge(getTenantId(), entityViewId, edgeId)); | |
448 | + logEntityAction(entityViewId, savedEntityView, | |
449 | + savedEntityView.getCustomerId(), | |
450 | + ActionType.ASSIGNED_TO_EDGE, null, strEntityViewId, strEdgeId, edge.getName()); | |
451 | + return savedEntityView; | |
452 | + } catch (Exception e) { | |
453 | + logEntityAction(emptyId(EntityType.ENTITY_VIEW), null, | |
454 | + null, | |
455 | + ActionType.ASSIGNED_TO_EDGE, e, strEntityViewId, strEdgeId); | |
456 | + throw handleException(e); | |
457 | + } | |
458 | + } | |
459 | + | |
460 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | |
461 | + @RequestMapping(value = "/edge/entityView/{entityViewId}", method = RequestMethod.DELETE) | |
462 | + @ResponseBody | |
463 | + public EntityView unassignEntityViewFromEdge(@PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException { | |
464 | + checkParameter(ENTITY_VIEW_ID, strEntityViewId); | |
465 | + try { | |
466 | + EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId)); | |
467 | + EntityView entityView = checkEntityViewId(entityViewId, Operation.UNASSIGN_FROM_EDGE); | |
468 | + if (entityView.getEdgeId() == null || entityView.getEdgeId().getId().equals(ModelConstants.NULL_UUID)) { | |
469 | + throw new IncorrectParameterException("Entity View isn't assigned to any edge!"); | |
470 | + } | |
471 | + Edge edge = checkEdgeId(entityView.getEdgeId(), Operation.READ); | |
472 | + EntityView savedEntityView = checkNotNull(entityViewService.unassignEntityViewFromEdge(getTenantId(), entityViewId)); | |
473 | + logEntityAction(entityViewId, entityView, | |
474 | + entityView.getCustomerId(), | |
475 | + ActionType.UNASSIGNED_FROM_EDGE, null, strEntityViewId, edge.getId().toString(), edge.getName()); | |
476 | + | |
477 | + return savedEntityView; | |
478 | + } catch (Exception e) { | |
479 | + logEntityAction(emptyId(EntityType.ENTITY_VIEW), null, | |
480 | + null, | |
481 | + ActionType.UNASSIGNED_FROM_EDGE, e, strEntityViewId); | |
482 | + throw handleException(e); | |
483 | + } | |
484 | + } | |
485 | + | |
486 | + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") | |
487 | + @RequestMapping(value = "/edge/{edgeId}/entityViews", params = {"pageSize", "page"}, method = RequestMethod.GET) | |
488 | + @ResponseBody | |
489 | + public PageData<EntityView> getEdgeEntityViews( | |
490 | + @PathVariable("edgeId") String strEdgeId, | |
491 | + @RequestParam int pageSize, | |
492 | + @RequestParam int page, | |
493 | + @RequestParam(required = false) String type, | |
494 | + @RequestParam(required = false) String textSearch, | |
495 | + @RequestParam(required = false) String sortProperty, | |
496 | + @RequestParam(required = false) String sortOrder) throws ThingsboardException { | |
497 | + checkParameter("edgeId", strEdgeId); | |
498 | + try { | |
499 | + TenantId tenantId = getCurrentUser().getTenantId(); | |
500 | + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); | |
501 | + checkEdgeId(edgeId, Operation.READ); | |
502 | + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); | |
503 | + if (type != null && type.trim().length()>0) { | |
504 | + return checkNotNull(entityViewService.findEntityViewsByTenantIdAndEdgeIdAndType(tenantId, edgeId, type, pageLink)); | |
505 | + } else { | |
506 | + return checkNotNull(entityViewService.findEntityViewsByTenantIdAndEdgeId(tenantId, edgeId, pageLink)); | |
507 | + } | |
508 | + } catch (Exception e) { | |
509 | + throw handleException(e); | |
510 | + } | |
511 | + } | |
429 | 512 | } | ... | ... |
... | ... | @@ -40,16 +40,21 @@ import org.thingsboard.server.actors.tenant.DebugTbRateLimits; |
40 | 40 | import org.thingsboard.server.common.data.DataConstants; |
41 | 41 | import org.thingsboard.server.common.data.EntityType; |
42 | 42 | import org.thingsboard.server.common.data.Event; |
43 | +import org.thingsboard.server.common.data.ShortEdgeInfo; | |
43 | 44 | import org.thingsboard.server.common.data.audit.ActionType; |
45 | +import org.thingsboard.server.common.data.edge.Edge; | |
44 | 46 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
47 | +import org.thingsboard.server.common.data.id.EdgeId; | |
45 | 48 | import org.thingsboard.server.common.data.id.RuleChainId; |
46 | 49 | import org.thingsboard.server.common.data.id.RuleNodeId; |
47 | 50 | import org.thingsboard.server.common.data.id.TenantId; |
48 | 51 | import org.thingsboard.server.common.data.page.PageData; |
49 | 52 | import org.thingsboard.server.common.data.page.PageLink; |
53 | +import org.thingsboard.server.common.data.page.TimePageLink; | |
50 | 54 | import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; |
51 | 55 | import org.thingsboard.server.common.data.rule.RuleChain; |
52 | 56 | import org.thingsboard.server.common.data.rule.RuleChainMetaData; |
57 | +import org.thingsboard.server.common.data.rule.RuleChainType; | |
53 | 58 | import org.thingsboard.server.common.data.rule.RuleNode; |
54 | 59 | import org.thingsboard.server.common.msg.TbMsg; |
55 | 60 | import org.thingsboard.server.common.msg.TbMsgMetaData; |
... | ... | @@ -59,6 +64,7 @@ import org.thingsboard.server.service.script.RuleNodeJsScriptEngine; |
59 | 64 | import org.thingsboard.server.service.security.permission.Operation; |
60 | 65 | import org.thingsboard.server.service.security.permission.Resource; |
61 | 66 | |
67 | +import java.util.HashSet; | |
62 | 68 | import java.util.List; |
63 | 69 | import java.util.Map; |
64 | 70 | import java.util.Set; |
... | ... | @@ -225,13 +231,19 @@ public class RuleChainController extends BaseController { |
225 | 231 | public PageData<RuleChain> getRuleChains( |
226 | 232 | @RequestParam int pageSize, |
227 | 233 | @RequestParam int page, |
234 | + @RequestParam(value = "type", required = false) String typeStr, | |
228 | 235 | @RequestParam(required = false) String textSearch, |
229 | 236 | @RequestParam(required = false) String sortProperty, |
230 | 237 | @RequestParam(required = false) String sortOrder) throws ThingsboardException { |
231 | 238 | try { |
232 | 239 | TenantId tenantId = getCurrentUser().getTenantId(); |
233 | 240 | PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); |
234 | - return checkNotNull(ruleChainService.findTenantRuleChains(tenantId, pageLink)); | |
241 | + if (typeStr != null && typeStr.trim().length() > 0) { | |
242 | + RuleChainType type = RuleChainType.valueOf(typeStr); | |
243 | + return checkNotNull(ruleChainService.findTenantRuleChainsByType(tenantId, type, pageLink)); | |
244 | + } else { | |
245 | + return checkNotNull(ruleChainService.findTenantRuleChains(tenantId, pageLink)); | |
246 | + } | |
235 | 247 | } catch (Exception e) { |
236 | 248 | throw handleException(e); |
237 | 249 | } |
... | ... | @@ -372,4 +384,242 @@ public class RuleChainController extends BaseController { |
372 | 384 | return objectMapper.writeValueAsString(msgData); |
373 | 385 | } |
374 | 386 | |
387 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | |
388 | + @RequestMapping(value = "/edge/{edgeId}/ruleChain/{ruleChainId}", method = RequestMethod.POST) | |
389 | + @ResponseBody | |
390 | + public RuleChain assignRuleChainToEdge(@PathVariable("edgeId") String strEdgeId, | |
391 | + @PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException { | |
392 | + checkParameter("edgeId", strEdgeId); | |
393 | + checkParameter(RULE_CHAIN_ID, strRuleChainId); | |
394 | + try { | |
395 | + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); | |
396 | + Edge edge = checkEdgeId(edgeId, Operation.READ); | |
397 | + | |
398 | + RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); | |
399 | + checkRuleChain(ruleChainId, Operation.ASSIGN_TO_EDGE); | |
400 | + | |
401 | + RuleChain savedRuleChain = checkNotNull(ruleChainService.assignRuleChainToEdge(getCurrentUser().getTenantId(), ruleChainId, edgeId)); | |
402 | + | |
403 | + logEntityAction(ruleChainId, savedRuleChain, | |
404 | + null, | |
405 | + ActionType.ASSIGNED_TO_EDGE, null, strRuleChainId, strEdgeId, edge.getName()); | |
406 | + | |
407 | + | |
408 | + return savedRuleChain; | |
409 | + } catch (Exception e) { | |
410 | + | |
411 | + logEntityAction(emptyId(EntityType.RULE_CHAIN), null, | |
412 | + null, | |
413 | + ActionType.ASSIGNED_TO_EDGE, e, strRuleChainId, strEdgeId); | |
414 | + | |
415 | + throw handleException(e); | |
416 | + } | |
417 | + } | |
418 | + | |
419 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | |
420 | + @RequestMapping(value = "/edge/{edgeId}/ruleChain/{ruleChainId}", method = RequestMethod.DELETE) | |
421 | + @ResponseBody | |
422 | + public RuleChain unassignRuleChainFromEdge(@PathVariable("edgeId") String strEdgeId, | |
423 | + @PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException { | |
424 | + checkParameter("edgeId", strEdgeId); | |
425 | + checkParameter(RULE_CHAIN_ID, strRuleChainId); | |
426 | + try { | |
427 | + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); | |
428 | + Edge edge = checkEdgeId(edgeId, Operation.READ); | |
429 | + RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); | |
430 | + RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.UNASSIGN_FROM_EDGE); | |
431 | + | |
432 | + RuleChain savedRuleChain = checkNotNull(ruleChainService.unassignRuleChainFromEdge(getCurrentUser().getTenantId(), ruleChainId, edgeId)); | |
433 | + | |
434 | + logEntityAction(ruleChainId, ruleChain, | |
435 | + null, | |
436 | + ActionType.UNASSIGNED_FROM_EDGE, null, strRuleChainId, edge.getId().toString(), edge.getName()); | |
437 | + | |
438 | + return savedRuleChain; | |
439 | + } catch (Exception e) { | |
440 | + | |
441 | + logEntityAction(emptyId(EntityType.RULE_CHAIN), null, | |
442 | + null, | |
443 | + ActionType.UNASSIGNED_FROM_EDGE, e, strRuleChainId); | |
444 | + | |
445 | + throw handleException(e); | |
446 | + } | |
447 | + } | |
448 | + | |
449 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | |
450 | + @RequestMapping(value = "/ruleChain/{ruleChainId}/edges", method = RequestMethod.POST) | |
451 | + @ResponseBody | |
452 | + public RuleChain updateRuleChainEdges(@PathVariable(RULE_CHAIN_ID) String strRuleChainId, | |
453 | + @RequestBody String[] strEdgeIds) throws ThingsboardException { | |
454 | + checkParameter(RULE_CHAIN_ID, strRuleChainId); | |
455 | + try { | |
456 | + RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); | |
457 | + RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.ASSIGN_TO_EDGE); | |
458 | + | |
459 | + Set<EdgeId> edgeIds = new HashSet<>(); | |
460 | + if (strEdgeIds != null) { | |
461 | + for (String strEdgeId : strEdgeIds) { | |
462 | + edgeIds.add(new EdgeId(toUUID(strEdgeId))); | |
463 | + } | |
464 | + } | |
465 | + | |
466 | + Set<EdgeId> addedEdgeIds = new HashSet<>(); | |
467 | + Set<EdgeId> removedEdgeIds = new HashSet<>(); | |
468 | + for (EdgeId edgeId : edgeIds) { | |
469 | + if (!ruleChain.isAssignedToEdge(edgeId)) { | |
470 | + addedEdgeIds.add(edgeId); | |
471 | + } | |
472 | + } | |
473 | + | |
474 | + Set<ShortEdgeInfo> assignedEdges = ruleChain.getAssignedEdges(); | |
475 | + if (assignedEdges != null) { | |
476 | + for (ShortEdgeInfo edgeInfo : assignedEdges) { | |
477 | + if (!edgeIds.contains(edgeInfo.getEdgeId())) { | |
478 | + removedEdgeIds.add(edgeInfo.getEdgeId()); | |
479 | + } | |
480 | + } | |
481 | + } | |
482 | + | |
483 | + if (addedEdgeIds.isEmpty() && removedEdgeIds.isEmpty()) { | |
484 | + return ruleChain; | |
485 | + } else { | |
486 | + RuleChain savedRuleChain = null; | |
487 | + for (EdgeId edgeId : addedEdgeIds) { | |
488 | + savedRuleChain = checkNotNull(ruleChainService.assignRuleChainToEdge(getCurrentUser().getTenantId(), ruleChainId, edgeId)); | |
489 | + ShortEdgeInfo edgeInfo = savedRuleChain.getAssignedEdgeInfo(edgeId); | |
490 | + logEntityAction(ruleChainId, savedRuleChain, | |
491 | + null, | |
492 | + ActionType.ASSIGNED_TO_EDGE, null, strRuleChainId, edgeId.toString(), edgeInfo.getTitle()); | |
493 | + } | |
494 | + for (EdgeId edgeId : removedEdgeIds) { | |
495 | + ShortEdgeInfo edgeInfo = ruleChain.getAssignedEdgeInfo(edgeId); | |
496 | + savedRuleChain = checkNotNull(ruleChainService.unassignRuleChainFromEdge(getCurrentUser().getTenantId(), ruleChainId, edgeId)); | |
497 | + logEntityAction(ruleChainId, ruleChain, | |
498 | + null, | |
499 | + ActionType.UNASSIGNED_FROM_EDGE, null, strRuleChainId, edgeId.toString(), edgeInfo.getTitle()); | |
500 | + | |
501 | + } | |
502 | + return savedRuleChain; | |
503 | + } | |
504 | + } catch (Exception e) { | |
505 | + | |
506 | + logEntityAction(emptyId(EntityType.RULE_CHAIN), null, | |
507 | + null, | |
508 | + ActionType.ASSIGNED_TO_EDGE, e, strRuleChainId); | |
509 | + | |
510 | + throw handleException(e); | |
511 | + } | |
512 | + } | |
513 | + | |
514 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | |
515 | + @RequestMapping(value = "/ruleChain/{ruleChainId}/edges/add", method = RequestMethod.POST) | |
516 | + @ResponseBody | |
517 | + public RuleChain addRuleChainEdges(@PathVariable(RULE_CHAIN_ID) String strRuleChainId, | |
518 | + @RequestBody String[] strEdgeIds) throws ThingsboardException { | |
519 | + checkParameter(RULE_CHAIN_ID, strRuleChainId); | |
520 | + try { | |
521 | + RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); | |
522 | + RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.ASSIGN_TO_EDGE); | |
523 | + | |
524 | + Set<EdgeId> edgeIds = new HashSet<>(); | |
525 | + if (strEdgeIds != null) { | |
526 | + for (String strEdgeId : strEdgeIds) { | |
527 | + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); | |
528 | + if (!ruleChain.isAssignedToEdge(edgeId)) { | |
529 | + edgeIds.add(edgeId); | |
530 | + } | |
531 | + } | |
532 | + } | |
533 | + | |
534 | + if (edgeIds.isEmpty()) { | |
535 | + return ruleChain; | |
536 | + } else { | |
537 | + RuleChain savedRuleChain = null; | |
538 | + for (EdgeId edgeId : edgeIds) { | |
539 | + savedRuleChain = checkNotNull(ruleChainService.assignRuleChainToEdge(getCurrentUser().getTenantId(), ruleChainId, edgeId)); | |
540 | + ShortEdgeInfo edgeInfo = savedRuleChain.getAssignedEdgeInfo(edgeId); | |
541 | + logEntityAction(ruleChainId, savedRuleChain, | |
542 | + null, | |
543 | + ActionType.ASSIGNED_TO_EDGE, null, strRuleChainId, edgeId.toString(), edgeInfo.getTitle()); | |
544 | + } | |
545 | + return savedRuleChain; | |
546 | + } | |
547 | + } catch (Exception e) { | |
548 | + | |
549 | + logEntityAction(emptyId(EntityType.RULE_CHAIN), null, | |
550 | + null, | |
551 | + ActionType.ASSIGNED_TO_EDGE, e, strRuleChainId); | |
552 | + | |
553 | + throw handleException(e); | |
554 | + } | |
555 | + } | |
556 | + | |
557 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | |
558 | + @RequestMapping(value = "/ruleChain/{ruleChainId}/edges/remove", method = RequestMethod.POST) | |
559 | + @ResponseBody | |
560 | + public RuleChain removeRuleChainEdges(@PathVariable(RULE_CHAIN_ID) String strRuleChainId, | |
561 | + @RequestBody String[] strEdgeIds) throws ThingsboardException { | |
562 | + checkParameter(RULE_CHAIN_ID, strRuleChainId); | |
563 | + try { | |
564 | + RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); | |
565 | + RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.UNASSIGN_FROM_EDGE); | |
566 | + | |
567 | + Set<EdgeId> edgeIds = new HashSet<>(); | |
568 | + if (strEdgeIds != null) { | |
569 | + for (String strEdgeId : strEdgeIds) { | |
570 | + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); | |
571 | + if (ruleChain.isAssignedToEdge(edgeId)) { | |
572 | + edgeIds.add(edgeId); | |
573 | + } | |
574 | + } | |
575 | + } | |
576 | + | |
577 | + if (edgeIds.isEmpty()) { | |
578 | + return ruleChain; | |
579 | + } else { | |
580 | + RuleChain savedRuleChain = null; | |
581 | + for (EdgeId edgeId : edgeIds) { | |
582 | + ShortEdgeInfo edgeInfo = ruleChain.getAssignedEdgeInfo(edgeId); | |
583 | + savedRuleChain = checkNotNull(ruleChainService.unassignRuleChainFromEdge(getCurrentUser().getTenantId(), ruleChainId, edgeId)); | |
584 | + logEntityAction(ruleChainId, ruleChain, | |
585 | + null, | |
586 | + ActionType.UNASSIGNED_FROM_EDGE, null, strRuleChainId, edgeId.toString(), edgeInfo.getTitle()); | |
587 | + | |
588 | + } | |
589 | + return savedRuleChain; | |
590 | + } | |
591 | + } catch (Exception e) { | |
592 | + | |
593 | + logEntityAction(emptyId(EntityType.RULE_CHAIN), null, | |
594 | + null, | |
595 | + ActionType.UNASSIGNED_FROM_EDGE, e, strRuleChainId); | |
596 | + | |
597 | + throw handleException(e); | |
598 | + } | |
599 | + } | |
600 | + | |
601 | + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") | |
602 | + @RequestMapping(value = "/edge/{edgeId}/ruleChains", params = {"pageSize", "page"}, method = RequestMethod.GET) | |
603 | + @ResponseBody | |
604 | + public PageData<RuleChain> getEdgeRuleChains( | |
605 | + @PathVariable("edgeId") String strEdgeId, | |
606 | + @RequestParam int pageSize, | |
607 | + @RequestParam int page, | |
608 | + @RequestParam(required = false) String textSearch, | |
609 | + @RequestParam(required = false) String sortProperty, | |
610 | + @RequestParam(required = false) String sortOrder, | |
611 | + @RequestParam(required = false) Long startTime, | |
612 | + @RequestParam(required = false) Long endTime) throws ThingsboardException { | |
613 | + checkParameter("edgeId", strEdgeId); | |
614 | + try { | |
615 | + TenantId tenantId = getCurrentUser().getTenantId(); | |
616 | + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); | |
617 | + checkEdgeId(edgeId, Operation.READ); | |
618 | + TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); | |
619 | + return checkNotNull(ruleChainService.findRuleChainsByTenantIdAndEdgeId(tenantId, edgeId, pageLink)); | |
620 | + } catch (Exception e) { | |
621 | + throw handleException(e); | |
622 | + } | |
623 | + } | |
624 | + | |
375 | 625 | } | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2020 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.service.edge; | |
17 | + | |
18 | +import lombok.Data; | |
19 | +import org.springframework.beans.factory.annotation.Autowired; | |
20 | +import org.springframework.context.annotation.Lazy; | |
21 | +import org.springframework.stereotype.Component; | |
22 | +import org.thingsboard.server.actors.service.ActorService; | |
23 | +import org.thingsboard.server.dao.alarm.AlarmService; | |
24 | +import org.thingsboard.server.dao.asset.AssetService; | |
25 | +import org.thingsboard.server.dao.attributes.AttributesService; | |
26 | +import org.thingsboard.server.dao.customer.CustomerService; | |
27 | +import org.thingsboard.server.dao.dashboard.DashboardService; | |
28 | +import org.thingsboard.server.dao.device.DeviceService; | |
29 | +import org.thingsboard.server.dao.edge.EdgeService; | |
30 | +import org.thingsboard.server.dao.entityview.EntityViewService; | |
31 | +import org.thingsboard.server.dao.relation.RelationService; | |
32 | + | |
33 | +@Component | |
34 | +@Data | |
35 | +public class EdgeContextComponent { | |
36 | + | |
37 | + @Lazy | |
38 | + @Autowired | |
39 | + private EdgeService edgeService; | |
40 | + | |
41 | + @Lazy | |
42 | + @Autowired | |
43 | + private AssetService assetService; | |
44 | + | |
45 | + @Lazy | |
46 | + @Autowired | |
47 | + private DeviceService deviceService; | |
48 | + | |
49 | + @Lazy | |
50 | + @Autowired | |
51 | + private EntityViewService entityViewService; | |
52 | + | |
53 | + @Lazy | |
54 | + @Autowired | |
55 | + private AttributesService attributesService; | |
56 | + | |
57 | + @Lazy | |
58 | + @Autowired | |
59 | + private CustomerService customerService; | |
60 | + | |
61 | + @Lazy | |
62 | + @Autowired | |
63 | + private RelationService relationService; | |
64 | + | |
65 | + @Lazy | |
66 | + @Autowired | |
67 | + private AlarmService alarmService; | |
68 | + | |
69 | + @Lazy | |
70 | + @Autowired | |
71 | + private DashboardService dashboardService; | |
72 | + | |
73 | + @Lazy | |
74 | + @Autowired | |
75 | + private ActorService actorService; | |
76 | +} | |
\ No newline at end of file | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2020 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.service.edge.rpc; | |
17 | + | |
18 | +import com.fasterxml.jackson.databind.ObjectMapper; | |
19 | +import com.google.common.io.Resources; | |
20 | +import io.grpc.Server; | |
21 | +import io.grpc.ServerBuilder; | |
22 | +import io.grpc.stub.StreamObserver; | |
23 | +import lombok.extern.slf4j.Slf4j; | |
24 | +import org.springframework.beans.factory.annotation.Autowired; | |
25 | +import org.springframework.beans.factory.annotation.Value; | |
26 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | |
27 | +import org.springframework.stereotype.Service; | |
28 | +import org.thingsboard.server.common.data.id.EdgeId; | |
29 | +import org.thingsboard.server.common.data.id.RuleChainId; | |
30 | +import org.thingsboard.server.common.data.id.TenantId; | |
31 | +import org.thingsboard.server.gen.edge.EdgeRpcServiceGrpc; | |
32 | +import org.thingsboard.server.gen.edge.RequestMsg; | |
33 | +import org.thingsboard.server.gen.edge.ResponseMsg; | |
34 | +import org.thingsboard.server.service.edge.EdgeContextComponent; | |
35 | + | |
36 | +import javax.annotation.PostConstruct; | |
37 | +import javax.annotation.PreDestroy; | |
38 | +import java.io.File; | |
39 | +import java.io.IOException; | |
40 | +import java.util.Map; | |
41 | +import java.util.concurrent.ConcurrentHashMap; | |
42 | +import java.util.concurrent.ExecutorService; | |
43 | +import java.util.concurrent.Executors; | |
44 | + | |
45 | +@Service | |
46 | +@Slf4j | |
47 | +@ConditionalOnProperty(prefix = "edges.rpc", value = "enabled", havingValue = "true") | |
48 | +public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase { | |
49 | + | |
50 | + private final Map<EdgeId, EdgeGrpcSession> sessions = new ConcurrentHashMap<>(); | |
51 | + private static final ObjectMapper objectMapper = new ObjectMapper(); | |
52 | + | |
53 | + @Value("${edges.rpc.port}") | |
54 | + private int rpcPort; | |
55 | + @Value("${edges.rpc.ssl.enabled}") | |
56 | + private boolean sslEnabled; | |
57 | + @Value("${edges.rpc.ssl.cert}") | |
58 | + private String certFileResource; | |
59 | + @Value("${edges.rpc.ssl.privateKey}") | |
60 | + private String privateKeyResource; | |
61 | + | |
62 | + @Autowired | |
63 | + private EdgeContextComponent ctx; | |
64 | + | |
65 | + private Server server; | |
66 | + | |
67 | + private ExecutorService executor; | |
68 | + | |
69 | + @PostConstruct | |
70 | + public void init() { | |
71 | + log.info("Initializing Edge RPC service!"); | |
72 | + ServerBuilder builder = ServerBuilder.forPort(rpcPort).addService(this); | |
73 | + if (sslEnabled) { | |
74 | + try { | |
75 | + File certFile = new File(Resources.getResource(certFileResource).toURI()); | |
76 | + File privateKeyFile = new File(Resources.getResource(privateKeyResource).toURI()); | |
77 | + builder.useTransportSecurity(certFile, privateKeyFile); | |
78 | + } catch (Exception e) { | |
79 | + log.error("Unable to set up SSL context. Reason: " + e.getMessage(), e); | |
80 | + throw new RuntimeException("Unable to set up SSL context!", e); | |
81 | + } | |
82 | + } | |
83 | + server = builder.build(); | |
84 | + log.info("Going to start Edge RPC server using port: {}", rpcPort); | |
85 | + try { | |
86 | + server.start(); | |
87 | + } catch (IOException e) { | |
88 | + log.error("Failed to start Edge RPC server!", e); | |
89 | + throw new RuntimeException("Failed to start Edge RPC server!"); | |
90 | + } | |
91 | + log.info("Edge RPC service initialized!"); | |
92 | + executor = Executors.newSingleThreadExecutor(); | |
93 | + processHandleMessages(); | |
94 | + } | |
95 | + | |
96 | + @PreDestroy | |
97 | + public void destroy() { | |
98 | + if (server != null) { | |
99 | + server.shutdownNow(); | |
100 | + } | |
101 | + } | |
102 | + | |
103 | + @Override | |
104 | + public StreamObserver<RequestMsg> handleMsgs(StreamObserver<ResponseMsg> outputStream) { | |
105 | + return new EdgeGrpcSession(ctx, outputStream, this::onEdgeConnect, this::onEdgeDisconnect, objectMapper).getInputStream(); | |
106 | + } | |
107 | + | |
108 | + private void onEdgeConnect(EdgeId edgeId, EdgeGrpcSession edgeGrpcSession) { | |
109 | + sessions.put(edgeId, edgeGrpcSession); | |
110 | + } | |
111 | + | |
112 | + private void processHandleMessages() { | |
113 | + executor.submit(() -> { | |
114 | + while (!Thread.interrupted()) { | |
115 | + try { | |
116 | + for (EdgeGrpcSession session : sessions.values()) { | |
117 | + session.processHandleMessages(); | |
118 | + } | |
119 | + } catch (Exception e) { | |
120 | + log.warn("Failed to process messages handling!", e); | |
121 | + } | |
122 | + } | |
123 | + }); | |
124 | + } | |
125 | + | |
126 | + private void onEdgeDisconnect(EdgeId edgeId) { | |
127 | + sessions.remove(edgeId); | |
128 | + } | |
129 | + | |
130 | +} | |
\ No newline at end of file | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2020 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.service.edge.rpc; | |
17 | + | |
18 | +import com.datastax.driver.core.utils.UUIDs; | |
19 | +import com.fasterxml.jackson.core.JsonProcessingException; | |
20 | +import com.fasterxml.jackson.databind.ObjectMapper; | |
21 | +import com.fasterxml.jackson.databind.node.ObjectNode; | |
22 | +import com.google.common.util.concurrent.Futures; | |
23 | +import com.google.common.util.concurrent.ListenableFuture; | |
24 | +import com.google.protobuf.ByteString; | |
25 | +import io.grpc.stub.StreamObserver; | |
26 | +import lombok.Data; | |
27 | +import lombok.extern.slf4j.Slf4j; | |
28 | +import org.thingsboard.server.common.data.Customer; | |
29 | +import org.thingsboard.server.common.data.Dashboard; | |
30 | +import org.thingsboard.server.common.data.DataConstants; | |
31 | +import org.thingsboard.server.common.data.Device; | |
32 | +import org.thingsboard.server.common.data.EntityType; | |
33 | +import org.thingsboard.server.common.data.EntityView; | |
34 | +import org.thingsboard.server.common.data.Event; | |
35 | +import org.thingsboard.server.common.data.User; | |
36 | +import org.thingsboard.server.common.data.alarm.Alarm; | |
37 | +import org.thingsboard.server.common.data.alarm.AlarmSeverity; | |
38 | +import org.thingsboard.server.common.data.alarm.AlarmStatus; | |
39 | +import org.thingsboard.server.common.data.asset.Asset; | |
40 | +import org.thingsboard.server.common.data.edge.Edge; | |
41 | +import org.thingsboard.server.common.data.edge.EdgeQueueEntry; | |
42 | +import org.thingsboard.server.common.data.id.AssetId; | |
43 | +import org.thingsboard.server.common.data.id.CustomerId; | |
44 | +import org.thingsboard.server.common.data.id.DeviceId; | |
45 | +import org.thingsboard.server.common.data.id.EdgeId; | |
46 | +import org.thingsboard.server.common.data.id.EntityId; | |
47 | +import org.thingsboard.server.common.data.id.EntityViewId; | |
48 | +import org.thingsboard.server.common.data.id.TenantId; | |
49 | +import org.thingsboard.server.common.data.kv.AttributeKvEntry; | |
50 | +import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; | |
51 | +import org.thingsboard.server.common.data.kv.DataType; | |
52 | +import org.thingsboard.server.common.data.kv.LongDataEntry; | |
53 | +import org.thingsboard.server.common.data.page.PageData; | |
54 | +import org.thingsboard.server.common.data.page.TimePageLink; | |
55 | +import org.thingsboard.server.common.data.relation.EntityRelation; | |
56 | +import org.thingsboard.server.common.data.relation.RelationTypeGroup; | |
57 | +import org.thingsboard.server.common.data.rule.NodeConnectionInfo; | |
58 | +import org.thingsboard.server.common.data.rule.RuleChain; | |
59 | +import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo; | |
60 | +import org.thingsboard.server.common.data.rule.RuleChainMetaData; | |
61 | +import org.thingsboard.server.common.data.rule.RuleNode; | |
62 | +import org.thingsboard.server.common.msg.TbMsg; | |
63 | +import org.thingsboard.server.common.msg.TbMsgDataType; | |
64 | +import org.thingsboard.server.common.msg.TbMsgMetaData; | |
65 | +import org.thingsboard.server.common.msg.cluster.SendToClusterMsg; | |
66 | +import org.thingsboard.server.common.msg.session.SessionMsgType; | |
67 | +import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; | |
68 | +import org.thingsboard.server.dao.util.mapping.JacksonUtil; | |
69 | +import org.thingsboard.server.gen.edge.AlarmUpdateMsg; | |
70 | +import org.thingsboard.server.gen.edge.AssetUpdateMsg; | |
71 | +import org.thingsboard.server.gen.edge.ConnectRequestMsg; | |
72 | +import org.thingsboard.server.gen.edge.ConnectResponseCode; | |
73 | +import org.thingsboard.server.gen.edge.ConnectResponseMsg; | |
74 | +import org.thingsboard.server.gen.edge.CustomerUpdateMsg; | |
75 | +import org.thingsboard.server.gen.edge.DashboardUpdateMsg; | |
76 | +import org.thingsboard.server.gen.edge.DeviceUpdateMsg; | |
77 | +import org.thingsboard.server.gen.edge.DownlinkMsg; | |
78 | +import org.thingsboard.server.gen.edge.EdgeConfiguration; | |
79 | +import org.thingsboard.server.gen.edge.EntityDataProto; | |
80 | +import org.thingsboard.server.gen.edge.EntityUpdateMsg; | |
81 | +import org.thingsboard.server.gen.edge.EntityViewUpdateMsg; | |
82 | +import org.thingsboard.server.gen.edge.NodeConnectionInfoProto; | |
83 | +import org.thingsboard.server.gen.edge.RequestMsg; | |
84 | +import org.thingsboard.server.gen.edge.RequestMsgType; | |
85 | +import org.thingsboard.server.gen.edge.ResponseMsg; | |
86 | +import org.thingsboard.server.gen.edge.RuleChainConnectionInfoProto; | |
87 | +import org.thingsboard.server.gen.edge.RuleChainMetadataUpdateMsg; | |
88 | +import org.thingsboard.server.gen.edge.RuleChainUpdateMsg; | |
89 | +import org.thingsboard.server.gen.edge.RuleNodeProto; | |
90 | +import org.thingsboard.server.gen.edge.UpdateMsgType; | |
91 | +import org.thingsboard.server.gen.edge.UplinkMsg; | |
92 | +import org.thingsboard.server.gen.edge.UplinkResponseMsg; | |
93 | +import org.thingsboard.server.gen.edge.UserUpdateMsg; | |
94 | +import org.thingsboard.server.service.edge.EdgeContextComponent; | |
95 | + | |
96 | +import java.io.IOException; | |
97 | +import java.util.ArrayList; | |
98 | +import java.util.Collections; | |
99 | +import java.util.List; | |
100 | +import java.util.Optional; | |
101 | +import java.util.UUID; | |
102 | +import java.util.concurrent.ExecutionException; | |
103 | +import java.util.concurrent.locks.ReentrantLock; | |
104 | +import java.util.function.BiConsumer; | |
105 | +import java.util.function.Consumer; | |
106 | + | |
107 | +import static org.thingsboard.server.gen.edge.UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE; | |
108 | + | |
109 | +@Slf4j | |
110 | +@Data | |
111 | +public final class EdgeGrpcSession implements Cloneable { | |
112 | + | |
113 | + private static final ReentrantLock entityCreationLock = new ReentrantLock(); | |
114 | + | |
115 | + private final UUID sessionId; | |
116 | + private final BiConsumer<EdgeId, EdgeGrpcSession> sessionOpenListener; | |
117 | + private final Consumer<EdgeId> sessionCloseListener; | |
118 | + private final ObjectMapper objectMapper; | |
119 | + | |
120 | + private EdgeContextComponent ctx; | |
121 | + private Edge edge; | |
122 | + private StreamObserver<RequestMsg> inputStream; | |
123 | + private StreamObserver<ResponseMsg> outputStream; | |
124 | + private boolean connected; | |
125 | + | |
126 | + EdgeGrpcSession(EdgeContextComponent ctx, StreamObserver<ResponseMsg> outputStream, BiConsumer<EdgeId, EdgeGrpcSession> sessionOpenListener, | |
127 | + Consumer<EdgeId> sessionCloseListener, ObjectMapper objectMapper) { | |
128 | + this.sessionId = UUID.randomUUID(); | |
129 | + this.ctx = ctx; | |
130 | + this.outputStream = outputStream; | |
131 | + this.sessionOpenListener = sessionOpenListener; | |
132 | + this.sessionCloseListener = sessionCloseListener; | |
133 | + this.objectMapper = objectMapper; | |
134 | + initInputStream(); | |
135 | + } | |
136 | + | |
137 | + private void initInputStream() { | |
138 | + this.inputStream = new StreamObserver<RequestMsg>() { | |
139 | + @Override | |
140 | + public void onNext(RequestMsg requestMsg) { | |
141 | + if (!connected && requestMsg.getMsgType().equals(RequestMsgType.CONNECT_RPC_MESSAGE)) { | |
142 | + ConnectResponseMsg responseMsg = processConnect(requestMsg.getConnectRequestMsg()); | |
143 | + outputStream.onNext(ResponseMsg.newBuilder() | |
144 | + .setConnectResponseMsg(responseMsg) | |
145 | + .build()); | |
146 | + if (ConnectResponseCode.ACCEPTED != responseMsg.getResponseCode()) { | |
147 | + outputStream.onError(new RuntimeException(responseMsg.getErrorMsg())); | |
148 | + } | |
149 | + } | |
150 | + if (connected) { | |
151 | + if (requestMsg.getMsgType().equals(RequestMsgType.UPLINK_RPC_MESSAGE) && requestMsg.hasUplinkMsg()) { | |
152 | + outputStream.onNext(ResponseMsg.newBuilder() | |
153 | + .setUplinkResponseMsg(processUplinkMsg(requestMsg.getUplinkMsg())) | |
154 | + .build()); | |
155 | + } | |
156 | + } | |
157 | + } | |
158 | + | |
159 | + @Override | |
160 | + public void onError(Throwable t) { | |
161 | + log.error("Failed to deliver message from client!", t); | |
162 | + } | |
163 | + | |
164 | + @Override | |
165 | + public void onCompleted() { | |
166 | + sessionCloseListener.accept(edge.getId()); | |
167 | + outputStream.onCompleted(); | |
168 | + } | |
169 | + }; | |
170 | + } | |
171 | + | |
172 | + | |
173 | + void processHandleMessages() throws ExecutionException, InterruptedException { | |
174 | + Long queueStartTs = getQueueStartTs().get(); | |
175 | + // TODO: this 100 value must be changed properly | |
176 | + TimePageLink pageLink = new TimePageLink(30, 0, "", null, queueStartTs + 1000, null); | |
177 | + PageData<Event> pageData; | |
178 | + UUID ifOffset = null; | |
179 | + do { | |
180 | + pageData = ctx.getEdgeService().findQueueEvents(edge.getTenantId(), edge.getId(), pageLink); | |
181 | + if (!pageData.getData().isEmpty()) { | |
182 | + log.trace("[{}] [{}] event(s) are going to be processed.", this.sessionId, pageData.getData().size()); | |
183 | + for (Event event : pageData.getData()) { | |
184 | + log.trace("[{}] Processing event [{}]", this.sessionId, event); | |
185 | + EdgeQueueEntry entry; | |
186 | + try { | |
187 | + entry = objectMapper.treeToValue(event.getBody(), EdgeQueueEntry.class); | |
188 | + | |
189 | + UpdateMsgType msgType = getResponseMsgType(entry.getType()); | |
190 | + switch (msgType) { | |
191 | + case ENTITY_DELETED_RPC_MESSAGE: | |
192 | + case ENTITY_UPDATED_RPC_MESSAGE: | |
193 | + case ENTITY_CREATED_RPC_MESSAGE: | |
194 | + case ALARM_ACK_RPC_MESSAGE: | |
195 | + case ALARM_CLEAR_RPC_MESSAGE: | |
196 | + processEntityCRUDMessage(entry, msgType); | |
197 | + break; | |
198 | + case RULE_CHAIN_CUSTOM_MESSAGE: | |
199 | + processCustomDownlinkMessage(entry); | |
200 | + break; | |
201 | + } | |
202 | + if (ENTITY_CREATED_RPC_MESSAGE.equals(msgType)) { | |
203 | + pushEntityAttributesToEdge(entry); | |
204 | + } | |
205 | + } catch (Exception e) { | |
206 | + log.error("Exception during processing records from queue", e); | |
207 | + } | |
208 | + ifOffset = event.getUuidId(); | |
209 | + } | |
210 | + } | |
211 | + if (pageData.hasNext()) { | |
212 | + pageLink = pageLink.nextPageLink(); | |
213 | + } | |
214 | + } while (pageData.hasNext()); | |
215 | + | |
216 | + if (ifOffset != null) { | |
217 | + Long newStartTs = UUIDs.unixTimestamp(ifOffset); | |
218 | + updateQueueStartTs(newStartTs); | |
219 | + } | |
220 | + try { | |
221 | + Thread.sleep(1000); | |
222 | + } catch (InterruptedException e) { | |
223 | + log.error("Error during sleep", e); | |
224 | + } | |
225 | + } | |
226 | + | |
227 | + private void pushEntityAttributesToEdge(EdgeQueueEntry entry) throws IOException { | |
228 | + EntityId entityId = null; | |
229 | + String entityName = null; | |
230 | + switch (entry.getEntityType()) { | |
231 | + case EDGE: | |
232 | + entityId = objectMapper.readValue(entry.getData(), Edge.class).getId(); | |
233 | + break; | |
234 | + case DEVICE: | |
235 | + entityId = objectMapper.readValue(entry.getData(), Device.class).getId(); | |
236 | + break; | |
237 | + case ASSET: | |
238 | + entityId = objectMapper.readValue(entry.getData(), Asset.class).getId(); | |
239 | + break; | |
240 | + case ENTITY_VIEW: | |
241 | + entityId = objectMapper.readValue(entry.getData(), EntityView.class).getId(); | |
242 | + break; | |
243 | + case DASHBOARD: | |
244 | + entityId = objectMapper.readValue(entry.getData(), Dashboard.class).getId(); | |
245 | + break; | |
246 | + } | |
247 | + if (entityId != null) { | |
248 | + ListenableFuture<List<AttributeKvEntry>> ssAttrFuture = ctx.getAttributesService().findAll(edge.getTenantId(), entityId, DataConstants.SERVER_SCOPE); | |
249 | + EntityId finalEntityId = entityId; | |
250 | + Futures.transform(ssAttrFuture, ssAttributes -> { | |
251 | + if (ssAttributes != null && !ssAttributes.isEmpty()) { | |
252 | + try { | |
253 | + TbMsgMetaData metaData = new TbMsgMetaData(); | |
254 | + ObjectNode entityNode = objectMapper.createObjectNode(); | |
255 | + metaData.putValue("scope", DataConstants.SERVER_SCOPE); | |
256 | + for (AttributeKvEntry attr : ssAttributes) { | |
257 | + if (attr.getDataType() == DataType.BOOLEAN) { | |
258 | + entityNode.put(attr.getKey(), attr.getBooleanValue().get()); | |
259 | + } else if (attr.getDataType() == DataType.DOUBLE) { | |
260 | + entityNode.put(attr.getKey(), attr.getDoubleValue().get()); | |
261 | + } else if (attr.getDataType() == DataType.LONG) { | |
262 | + entityNode.put(attr.getKey(), attr.getLongValue().get()); | |
263 | + } else { | |
264 | + entityNode.put(attr.getKey(), attr.getValueAsString()); | |
265 | + } | |
266 | + } | |
267 | + TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), DataConstants.ATTRIBUTES_UPDATED, finalEntityId, metaData, TbMsgDataType.JSON | |
268 | + , objectMapper.writeValueAsString(entityNode) | |
269 | + , null, null, 0L); | |
270 | + log.debug("Sending donwlink entity data msg, entityName [{}], tbMsg [{}]", entityName, tbMsg); | |
271 | + outputStream.onNext(ResponseMsg.newBuilder() | |
272 | + .setDownlinkMsg(constructDownlinkEntityDataMsg(entityName, tbMsg)) | |
273 | + .build()); | |
274 | + } catch (Exception e) { | |
275 | + log.error("[{}] Failed to send attribute updates to the edge", edge.getName(), e); | |
276 | + } | |
277 | + } | |
278 | + return null; | |
279 | + }); | |
280 | + ListenableFuture<List<AttributeKvEntry>> shAttrFuture = ctx.getAttributesService().findAll(edge.getTenantId(), entityId, DataConstants.SHARED_SCOPE); | |
281 | + ListenableFuture<List<AttributeKvEntry>> clAttrFuture = ctx.getAttributesService().findAll(edge.getTenantId(), entityId, DataConstants.CLIENT_SCOPE); | |
282 | + } | |
283 | + } | |
284 | + | |
285 | + private void processCustomDownlinkMessage(EdgeQueueEntry entry) throws IOException { | |
286 | + log.trace("Executing processCustomDownlinkMessage, entry [{}]", entry); | |
287 | + TbMsg tbMsg = objectMapper.readValue(entry.getData(), TbMsg.class); | |
288 | + String entityName = null; | |
289 | + switch (entry.getEntityType()) { | |
290 | + case DEVICE: | |
291 | + Device device = ctx.getDeviceService().findDeviceById(edge.getTenantId(), new DeviceId(tbMsg.getOriginator().getId())); | |
292 | + entityName = device.getName(); | |
293 | + break; | |
294 | + case ASSET: | |
295 | + Asset asset = ctx.getAssetService().findAssetById(edge.getTenantId(), new AssetId(tbMsg.getOriginator().getId())); | |
296 | + entityName = asset.getName(); | |
297 | + break; | |
298 | + case ENTITY_VIEW: | |
299 | + EntityView entityView = ctx.getEntityViewService().findEntityViewById(edge.getTenantId(), new EntityViewId(tbMsg.getOriginator().getId())); | |
300 | + entityName = entityView.getName(); | |
301 | + break; | |
302 | + | |
303 | + } | |
304 | + if (entityName != null) { | |
305 | + log.debug("Sending donwlink entity data msg, entityName [{}], tbMsg [{}]", entityName, tbMsg); | |
306 | + outputStream.onNext(ResponseMsg.newBuilder() | |
307 | + .setDownlinkMsg(constructDownlinkEntityDataMsg(entityName, tbMsg)) | |
308 | + .build()); | |
309 | + } | |
310 | + } | |
311 | + | |
312 | + private void processEntityCRUDMessage(EdgeQueueEntry entry, UpdateMsgType msgType) throws java.io.IOException { | |
313 | + log.trace("Executing processEntityCRUDMessage, entry [{}], msgType [{}]", entry, msgType); | |
314 | + switch (entry.getEntityType()) { | |
315 | + case EDGE: | |
316 | + Edge edge = objectMapper.readValue(entry.getData(), Edge.class); | |
317 | + onEdgeUpdated(msgType, edge); | |
318 | + break; | |
319 | + case DEVICE: | |
320 | + Device device = objectMapper.readValue(entry.getData(), Device.class); | |
321 | + onDeviceUpdated(msgType, device); | |
322 | + break; | |
323 | + case ASSET: | |
324 | + Asset asset = objectMapper.readValue(entry.getData(), Asset.class); | |
325 | + onAssetUpdated(msgType, asset); | |
326 | + break; | |
327 | + case ENTITY_VIEW: | |
328 | + EntityView entityView = objectMapper.readValue(entry.getData(), EntityView.class); | |
329 | + onEntityViewUpdated(msgType, entityView); | |
330 | + break; | |
331 | + case DASHBOARD: | |
332 | + Dashboard dashboard = objectMapper.readValue(entry.getData(), Dashboard.class); | |
333 | + onDashboardUpdated(msgType, dashboard); | |
334 | + break; | |
335 | + case RULE_CHAIN: | |
336 | + RuleChain ruleChain = objectMapper.readValue(entry.getData(), RuleChain.class); | |
337 | + onRuleChainUpdated(msgType, ruleChain); | |
338 | + break; | |
339 | + case RULE_CHAIN_METADATA: | |
340 | + RuleChainMetaData ruleChainMetaData = objectMapper.readValue(entry.getData(), RuleChainMetaData.class); | |
341 | + onRuleChainMetadataUpdated(msgType, ruleChainMetaData); | |
342 | + break; | |
343 | + case ALARM: | |
344 | + Alarm alarm = objectMapper.readValue(entry.getData(), Alarm.class); | |
345 | + onAlarmUpdated(msgType, alarm); | |
346 | + break; | |
347 | + } | |
348 | + } | |
349 | + | |
350 | + private void updateQueueStartTs(Long newStartTs) { | |
351 | + List<AttributeKvEntry> attributes = Collections.singletonList(new BaseAttributeKvEntry(new LongDataEntry("queueStartTs", newStartTs), System.currentTimeMillis())); | |
352 | + ctx.getAttributesService().save(edge.getTenantId(), edge.getId(), DataConstants.SERVER_SCOPE, attributes); | |
353 | + } | |
354 | + | |
355 | + private ListenableFuture<Long> getQueueStartTs() { | |
356 | + ListenableFuture<Optional<AttributeKvEntry>> future = | |
357 | + ctx.getAttributesService().find(edge.getTenantId(), edge.getId(), DataConstants.SERVER_SCOPE, "queueStartTs"); | |
358 | + return Futures.transform(future, attributeKvEntryOpt -> { | |
359 | + if (attributeKvEntryOpt != null && attributeKvEntryOpt.isPresent()) { | |
360 | + AttributeKvEntry attributeKvEntry = attributeKvEntryOpt.get(); | |
361 | + return attributeKvEntry.getLongValue().isPresent() ? attributeKvEntry.getLongValue().get() : 0L; | |
362 | + } else { | |
363 | + return 0L; | |
364 | + } | |
365 | + }); | |
366 | + } | |
367 | + | |
368 | + private void onEdgeUpdated(UpdateMsgType msgType, Edge edge) { | |
369 | + // TODO: voba add configuration update to edge | |
370 | + this.edge = edge; | |
371 | + } | |
372 | + | |
373 | + private void onDeviceUpdated(UpdateMsgType msgType, Device device) { | |
374 | + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() | |
375 | + .setDeviceUpdateMsg(constructDeviceUpdatedMsg(msgType, device)) | |
376 | + .build(); | |
377 | + outputStream.onNext(ResponseMsg.newBuilder() | |
378 | + .setEntityUpdateMsg(entityUpdateMsg) | |
379 | + .build()); | |
380 | + } | |
381 | + | |
382 | + private void onAssetUpdated(UpdateMsgType msgType, Asset asset) { | |
383 | + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() | |
384 | + .setAssetUpdateMsg(constructAssetUpdatedMsg(msgType, asset)) | |
385 | + .build(); | |
386 | + outputStream.onNext(ResponseMsg.newBuilder() | |
387 | + .setEntityUpdateMsg(entityUpdateMsg) | |
388 | + .build()); | |
389 | + } | |
390 | + | |
391 | + private void onEntityViewUpdated(UpdateMsgType msgType, EntityView entityView) { | |
392 | + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() | |
393 | + .setEntityViewUpdateMsg(constructEntityViewUpdatedMsg(msgType, entityView)) | |
394 | + .build(); | |
395 | + outputStream.onNext(ResponseMsg.newBuilder() | |
396 | + .setEntityUpdateMsg(entityUpdateMsg) | |
397 | + .build()); | |
398 | + } | |
399 | + | |
400 | + private void onRuleChainUpdated(UpdateMsgType msgType, RuleChain ruleChain) { | |
401 | + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() | |
402 | + .setRuleChainUpdateMsg(constructRuleChainUpdatedMsg(msgType, ruleChain)) | |
403 | + .build(); | |
404 | + outputStream.onNext(ResponseMsg.newBuilder() | |
405 | + .setEntityUpdateMsg(entityUpdateMsg) | |
406 | + .build()); | |
407 | + } | |
408 | + | |
409 | + private void onRuleChainMetadataUpdated(UpdateMsgType msgType, RuleChainMetaData ruleChainMetaData) { | |
410 | + RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = constructRuleChainMetadataUpdatedMsg(msgType, ruleChainMetaData); | |
411 | + if (ruleChainMetadataUpdateMsg != null) { | |
412 | + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() | |
413 | + .setRuleChainMetadataUpdateMsg(ruleChainMetadataUpdateMsg) | |
414 | + .build(); | |
415 | + outputStream.onNext(ResponseMsg.newBuilder() | |
416 | + .setEntityUpdateMsg(entityUpdateMsg) | |
417 | + .build()); | |
418 | + } | |
419 | + } | |
420 | + | |
421 | + private void onDashboardUpdated(UpdateMsgType msgType, Dashboard dashboard) { | |
422 | + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() | |
423 | + .setDashboardUpdateMsg(constructDashboardUpdatedMsg(msgType, dashboard)) | |
424 | + .build(); | |
425 | + outputStream.onNext(ResponseMsg.newBuilder() | |
426 | + .setEntityUpdateMsg(entityUpdateMsg) | |
427 | + .build()); | |
428 | + } | |
429 | + | |
430 | + private void onAlarmUpdated(UpdateMsgType msgType, Alarm alarm) { | |
431 | + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() | |
432 | + .setAlarmUpdateMsg(constructAlarmUpdatedMsg(msgType, alarm)) | |
433 | + .build(); | |
434 | + outputStream.onNext(ResponseMsg.newBuilder() | |
435 | + .setEntityUpdateMsg(entityUpdateMsg) | |
436 | + .build()); | |
437 | + } | |
438 | + | |
439 | + private AlarmUpdateMsg constructAlarmUpdatedMsg(UpdateMsgType msgType, Alarm alarm) { | |
440 | + String entityName = null; | |
441 | + switch (alarm.getOriginator().getEntityType()) { | |
442 | + case DEVICE: | |
443 | + entityName = ctx.getDeviceService().findDeviceById(edge.getTenantId(), new DeviceId(alarm.getOriginator().getId())).getName(); | |
444 | + break; | |
445 | + case ASSET: | |
446 | + entityName = ctx.getAssetService().findAssetById(edge.getTenantId(), new AssetId(alarm.getOriginator().getId())).getName(); | |
447 | + break; | |
448 | + case ENTITY_VIEW: | |
449 | + entityName = ctx.getEntityViewService().findEntityViewById(edge.getTenantId(), new EntityViewId(alarm.getOriginator().getId())).getName(); | |
450 | + break; | |
451 | + } | |
452 | + AlarmUpdateMsg.Builder builder = AlarmUpdateMsg.newBuilder() | |
453 | + .setMsgType(msgType) | |
454 | + .setName(alarm.getName()) | |
455 | + .setType(alarm.getType()) | |
456 | + .setOriginatorName(entityName) | |
457 | + .setOriginatorType(alarm.getOriginator().getEntityType().name()) | |
458 | + .setSeverity(alarm.getSeverity().name()) | |
459 | + .setStatus(alarm.getStatus().name()) | |
460 | + .setStartTs(alarm.getStartTs()) | |
461 | + .setEndTs(alarm.getEndTs()) | |
462 | + .setAckTs(alarm.getAckTs()) | |
463 | + .setClearTs(alarm.getClearTs()) | |
464 | + .setDetails(JacksonUtil.toString(alarm.getDetails())) | |
465 | + .setPropagate(alarm.isPropagate()); | |
466 | + return builder.build(); | |
467 | + } | |
468 | + | |
469 | + private UpdateMsgType getResponseMsgType(String msgType) { | |
470 | + if (msgType.equals(SessionMsgType.POST_TELEMETRY_REQUEST.name()) || | |
471 | + msgType.equals(SessionMsgType.POST_ATTRIBUTES_REQUEST.name()) || | |
472 | + msgType.equals(DataConstants.ATTRIBUTES_UPDATED) || | |
473 | + msgType.equals(DataConstants.ATTRIBUTES_DELETED)) { | |
474 | + return UpdateMsgType.RULE_CHAIN_CUSTOM_MESSAGE; | |
475 | + } else { | |
476 | + switch (msgType) { | |
477 | + case DataConstants.ENTITY_UPDATED: | |
478 | + return UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE; | |
479 | + case DataConstants.ENTITY_CREATED: | |
480 | + case DataConstants.ENTITY_ASSIGNED_TO_EDGE: | |
481 | + return ENTITY_CREATED_RPC_MESSAGE; | |
482 | + case DataConstants.ENTITY_DELETED: | |
483 | + case DataConstants.ENTITY_UNASSIGNED_FROM_EDGE: | |
484 | + return UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE; | |
485 | + case DataConstants.ALARM_ACK: | |
486 | + return UpdateMsgType.ALARM_ACK_RPC_MESSAGE; | |
487 | + case DataConstants.ALARM_CLEAR: | |
488 | + return UpdateMsgType.ALARM_CLEAR_RPC_MESSAGE; | |
489 | + default: | |
490 | + throw new RuntimeException("Unsupported msgType [" + msgType + "]"); | |
491 | + } | |
492 | + } | |
493 | + } | |
494 | + | |
495 | + private RuleChainUpdateMsg constructRuleChainUpdatedMsg(UpdateMsgType msgType, RuleChain ruleChain) { | |
496 | + RuleChainUpdateMsg.Builder builder = RuleChainUpdateMsg.newBuilder() | |
497 | + .setMsgType(msgType) | |
498 | + .setIdMSB(ruleChain.getId().getId().getMostSignificantBits()) | |
499 | + .setIdLSB(ruleChain.getId().getId().getLeastSignificantBits()) | |
500 | + .setName(ruleChain.getName()) | |
501 | + .setRoot(ruleChain.getId().equals(edge.getRootRuleChainId())) | |
502 | + .setDebugMode(ruleChain.isDebugMode()) | |
503 | + .setConfiguration(JacksonUtil.toString(ruleChain.getConfiguration())); | |
504 | + if (ruleChain.getFirstRuleNodeId() != null) { | |
505 | + builder.setFirstRuleNodeIdMSB(ruleChain.getFirstRuleNodeId().getId().getMostSignificantBits()) | |
506 | + .setFirstRuleNodeIdLSB(ruleChain.getFirstRuleNodeId().getId().getLeastSignificantBits()); | |
507 | + } | |
508 | + return builder.build(); | |
509 | + } | |
510 | + | |
511 | + private DownlinkMsg constructDownlinkEntityDataMsg(String entityName, TbMsg tbMsg) { | |
512 | + EntityDataProto entityData = EntityDataProto.newBuilder() | |
513 | + .setEntityName(entityName) | |
514 | + .setTbMsg(ByteString.copyFrom(TbMsg.toBytes(tbMsg))).build(); | |
515 | + | |
516 | + DownlinkMsg.Builder builder = DownlinkMsg.newBuilder() | |
517 | + .addAllEntityData(Collections.singletonList(entityData)); | |
518 | + | |
519 | + return builder.build(); | |
520 | + } | |
521 | + | |
522 | + private RuleChainMetadataUpdateMsg constructRuleChainMetadataUpdatedMsg(UpdateMsgType msgType, RuleChainMetaData ruleChainMetaData) { | |
523 | + try { | |
524 | + RuleChainMetadataUpdateMsg.Builder builder = RuleChainMetadataUpdateMsg.newBuilder() | |
525 | + .setRuleChainIdMSB(ruleChainMetaData.getRuleChainId().getId().getMostSignificantBits()) | |
526 | + .setRuleChainIdLSB(ruleChainMetaData.getRuleChainId().getId().getLeastSignificantBits()) | |
527 | + .addAllNodes(constructNodes(ruleChainMetaData.getNodes())) | |
528 | + .addAllConnections(constructConnections(ruleChainMetaData.getConnections())) | |
529 | + .addAllRuleChainConnections(constructRuleChainConnections(ruleChainMetaData.getRuleChainConnections())); | |
530 | + if (ruleChainMetaData.getFirstNodeIndex() != null) { | |
531 | + builder.setFirstNodeIndex(ruleChainMetaData.getFirstNodeIndex()); | |
532 | + } | |
533 | + builder.setMsgType(msgType); | |
534 | + return builder.build(); | |
535 | + } catch (JsonProcessingException ex) { | |
536 | + log.error("Can't construct RuleChainMetadataUpdateMsg", ex); | |
537 | + } | |
538 | + return null; | |
539 | + } | |
540 | + | |
541 | + private List<RuleChainConnectionInfoProto> constructRuleChainConnections(List<RuleChainConnectionInfo> ruleChainConnections) throws JsonProcessingException { | |
542 | + List<RuleChainConnectionInfoProto> result = new ArrayList<>(); | |
543 | + if (ruleChainConnections != null && !ruleChainConnections.isEmpty()) { | |
544 | + for (RuleChainConnectionInfo ruleChainConnectionInfo : ruleChainConnections) { | |
545 | + result.add(constructRuleChainConnection(ruleChainConnectionInfo)); | |
546 | + } | |
547 | + } | |
548 | + return result; | |
549 | + } | |
550 | + | |
551 | + private RuleChainConnectionInfoProto constructRuleChainConnection(RuleChainConnectionInfo ruleChainConnectionInfo) throws JsonProcessingException { | |
552 | + return RuleChainConnectionInfoProto.newBuilder() | |
553 | + .setFromIndex(ruleChainConnectionInfo.getFromIndex()) | |
554 | + .setTargetRuleChainIdMSB(ruleChainConnectionInfo.getTargetRuleChainId().getId().getMostSignificantBits()) | |
555 | + .setTargetRuleChainIdLSB(ruleChainConnectionInfo.getTargetRuleChainId().getId().getLeastSignificantBits()) | |
556 | + .setType(ruleChainConnectionInfo.getType()) | |
557 | + .setAdditionalInfo(objectMapper.writeValueAsString(ruleChainConnectionInfo.getAdditionalInfo())) | |
558 | + .build(); | |
559 | + } | |
560 | + | |
561 | + private List<NodeConnectionInfoProto> constructConnections(List<NodeConnectionInfo> connections) { | |
562 | + List<NodeConnectionInfoProto> result = new ArrayList<>(); | |
563 | + if (connections != null && !connections.isEmpty()) { | |
564 | + for (NodeConnectionInfo connection : connections) { | |
565 | + result.add(constructConnection(connection)); | |
566 | + } | |
567 | + } | |
568 | + return result; | |
569 | + } | |
570 | + | |
571 | + private NodeConnectionInfoProto constructConnection(NodeConnectionInfo connection) { | |
572 | + return NodeConnectionInfoProto.newBuilder() | |
573 | + .setFromIndex(connection.getFromIndex()) | |
574 | + .setToIndex(connection.getToIndex()) | |
575 | + .setType(connection.getType()) | |
576 | + .build(); | |
577 | + } | |
578 | + | |
579 | + private List<RuleNodeProto> constructNodes(List<RuleNode> nodes) throws JsonProcessingException { | |
580 | + List<RuleNodeProto> result = new ArrayList<>(); | |
581 | + if (nodes != null && !nodes.isEmpty()) { | |
582 | + for (RuleNode node : nodes) { | |
583 | + result.add(constructNode(node)); | |
584 | + } | |
585 | + } | |
586 | + return result; | |
587 | + } | |
588 | + | |
589 | + private RuleNodeProto constructNode(RuleNode node) throws JsonProcessingException { | |
590 | + return RuleNodeProto.newBuilder() | |
591 | + .setIdMSB(node.getId().getId().getMostSignificantBits()) | |
592 | + .setIdLSB(node.getId().getId().getLeastSignificantBits()) | |
593 | + .setType(node.getType()) | |
594 | + .setName(node.getName()) | |
595 | + .setDebugMode(node.isDebugMode()) | |
596 | + .setConfiguration(objectMapper.writeValueAsString(node.getConfiguration())) | |
597 | + .setAdditionalInfo(objectMapper.writeValueAsString(node.getAdditionalInfo())) | |
598 | + .build(); | |
599 | + } | |
600 | + | |
601 | + private DashboardUpdateMsg constructDashboardUpdatedMsg(UpdateMsgType msgType, Dashboard dashboard) { | |
602 | + dashboard = ctx.getDashboardService().findDashboardById(edge.getTenantId(), dashboard.getId()); | |
603 | + DashboardUpdateMsg.Builder builder = DashboardUpdateMsg.newBuilder() | |
604 | + .setMsgType(msgType) | |
605 | + .setIdMSB(dashboard.getId().getId().getMostSignificantBits()) | |
606 | + .setIdLSB(dashboard.getId().getId().getLeastSignificantBits()) | |
607 | + .setTitle(dashboard.getTitle()) | |
608 | + .setConfiguration(JacksonUtil.toString(dashboard.getConfiguration())); | |
609 | + return builder.build(); | |
610 | + } | |
611 | + | |
612 | + private CustomerUpdateMsg constructCustomerUpdatedMsg(UpdateMsgType msgType, Customer customer) { | |
613 | + CustomerUpdateMsg.Builder builder = CustomerUpdateMsg.newBuilder() | |
614 | + .setMsgType(msgType); | |
615 | + return builder.build(); | |
616 | + } | |
617 | + | |
618 | + private UserUpdateMsg constructUserUpdatedMsg(UpdateMsgType msgType, User user) { | |
619 | + UserUpdateMsg.Builder builder = UserUpdateMsg.newBuilder() | |
620 | + .setMsgType(msgType); | |
621 | + return builder.build(); | |
622 | + } | |
623 | + | |
624 | + private DeviceUpdateMsg constructDeviceUpdatedMsg(UpdateMsgType msgType, Device device) { | |
625 | + DeviceUpdateMsg.Builder builder = DeviceUpdateMsg.newBuilder() | |
626 | + .setMsgType(msgType) | |
627 | + .setName(device.getName()) | |
628 | + .setType(device.getType()); | |
629 | + return builder.build(); | |
630 | + } | |
631 | + | |
632 | + private AssetUpdateMsg constructAssetUpdatedMsg(UpdateMsgType msgType, Asset asset) { | |
633 | + AssetUpdateMsg.Builder builder = AssetUpdateMsg.newBuilder() | |
634 | + .setMsgType(msgType) | |
635 | + .setName(asset.getName()) | |
636 | + .setType(asset.getType()); | |
637 | + return builder.build(); | |
638 | + } | |
639 | + | |
640 | + private EntityViewUpdateMsg constructEntityViewUpdatedMsg(UpdateMsgType msgType, EntityView entityView) { | |
641 | + String relatedName; | |
642 | + String relatedType; | |
643 | + org.thingsboard.server.gen.edge.EntityType relatedEntityType; | |
644 | + if (entityView.getEntityId().getEntityType().equals(EntityType.DEVICE)) { | |
645 | + Device device = ctx.getDeviceService().findDeviceById(entityView.getTenantId(), new DeviceId(entityView.getEntityId().getId())); | |
646 | + relatedName = device.getName(); | |
647 | + relatedType = device.getType(); | |
648 | + relatedEntityType = org.thingsboard.server.gen.edge.EntityType.DEVICE; | |
649 | + } else { | |
650 | + Asset asset = ctx.getAssetService().findAssetById(entityView.getTenantId(), new AssetId(entityView.getEntityId().getId())); | |
651 | + relatedName = asset.getName(); | |
652 | + relatedType = asset.getType(); | |
653 | + relatedEntityType = org.thingsboard.server.gen.edge.EntityType.ASSET; | |
654 | + } | |
655 | + EntityViewUpdateMsg.Builder builder = EntityViewUpdateMsg.newBuilder() | |
656 | + .setMsgType(msgType) | |
657 | + .setName(entityView.getName()) | |
658 | + .setType(entityView.getType()) | |
659 | + .setRelatedName(relatedName) | |
660 | + .setRelatedType(relatedType) | |
661 | + .setRelatedEntityType(relatedEntityType); | |
662 | + return builder.build(); | |
663 | + } | |
664 | + | |
665 | + private UplinkResponseMsg processUplinkMsg(UplinkMsg uplinkMsg) { | |
666 | + try { | |
667 | + if (uplinkMsg.getEntityDataList() != null && !uplinkMsg.getEntityDataList().isEmpty()) { | |
668 | + for (EntityDataProto entityData : uplinkMsg.getEntityDataList()) { | |
669 | + TbMsg tbMsg = null; | |
670 | + TbMsg originalTbMsg = TbMsg.fromBytes(entityData.getTbMsg().toByteArray()); | |
671 | + switch (originalTbMsg.getOriginator().getEntityType()) { | |
672 | + case DEVICE: | |
673 | + String deviceName = entityData.getEntityName(); | |
674 | + String deviceType = entityData.getEntityType(); | |
675 | + Device device = getOrCreateDevice(deviceName, deviceType); | |
676 | + if (device != null) { | |
677 | + tbMsg = new TbMsg(UUIDs.timeBased(), originalTbMsg.getType(), device.getId(), originalTbMsg.getMetaData().copy(), | |
678 | + originalTbMsg.getDataType(), originalTbMsg.getData(), null, null, 0L); | |
679 | + } | |
680 | + break; | |
681 | + case ASSET: | |
682 | + String assetName = entityData.getEntityName(); | |
683 | + Asset asset = ctx.getAssetService().findAssetByTenantIdAndName(edge.getTenantId(), assetName); | |
684 | + if (asset != null) { | |
685 | + tbMsg = new TbMsg(UUIDs.timeBased(), originalTbMsg.getType(), asset.getId(), originalTbMsg.getMetaData().copy(), | |
686 | + originalTbMsg.getDataType(), originalTbMsg.getData(), null, null, 0L); | |
687 | + } | |
688 | + break; | |
689 | + case ENTITY_VIEW: | |
690 | + String entityViewName = entityData.getEntityName(); | |
691 | + EntityView entityView = ctx.getEntityViewService().findEntityViewByTenantIdAndName(edge.getTenantId(), entityViewName); | |
692 | + if (entityView != null) { | |
693 | + tbMsg = new TbMsg(UUIDs.timeBased(), originalTbMsg.getType(), entityView.getId(), originalTbMsg.getMetaData().copy(), | |
694 | + originalTbMsg.getDataType(), originalTbMsg.getData(), null, null, 0L); | |
695 | + } | |
696 | + break; | |
697 | + } | |
698 | + if (tbMsg != null) { | |
699 | + ctx.getActorService().onMsg(new SendToClusterMsg(tbMsg.getOriginator(), new ServiceToRuleEngineMsg(edge.getTenantId(), tbMsg))); | |
700 | + } | |
701 | + } | |
702 | + } | |
703 | + if (uplinkMsg.getDeviceUpdateMsgList() != null && !uplinkMsg.getDeviceUpdateMsgList().isEmpty()) { | |
704 | + for (DeviceUpdateMsg deviceUpdateMsg : uplinkMsg.getDeviceUpdateMsgList()) { | |
705 | + String deviceName = deviceUpdateMsg.getName(); | |
706 | + String deviceType = deviceUpdateMsg.getType(); | |
707 | + switch (deviceUpdateMsg.getMsgType()) { | |
708 | + case ENTITY_CREATED_RPC_MESSAGE: | |
709 | + getOrCreateDevice(deviceName, deviceType); | |
710 | + break; | |
711 | + } | |
712 | + } | |
713 | + } | |
714 | + if (uplinkMsg.getAlarmUpdatemsgList() != null && !uplinkMsg.getAlarmUpdatemsgList().isEmpty()) { | |
715 | + for (AlarmUpdateMsg alarmUpdateMsg : uplinkMsg.getAlarmUpdatemsgList()) { | |
716 | + onAlarmUpdate(alarmUpdateMsg); | |
717 | + } | |
718 | + } | |
719 | + } catch (Exception e) { | |
720 | + return UplinkResponseMsg.newBuilder().setSuccess(false).setErrorMsg(e.getMessage()).build(); | |
721 | + } | |
722 | + | |
723 | + return UplinkResponseMsg.newBuilder().setSuccess(true).build(); | |
724 | + } | |
725 | + | |
726 | + private EntityId getAlarmOriginator(String entityName, org.thingsboard.server.common.data.EntityType entityType) { | |
727 | + switch (entityType) { | |
728 | + case DEVICE: | |
729 | + return ctx.getDeviceService().findDeviceByTenantIdAndName(edge.getTenantId(), entityName).getId(); | |
730 | + case ASSET: | |
731 | + return ctx.getAssetService().findAssetByTenantIdAndName(edge.getTenantId(), entityName).getId(); | |
732 | + case ENTITY_VIEW: | |
733 | + return ctx.getEntityViewService().findEntityViewByTenantIdAndName(edge.getTenantId(), entityName).getId(); | |
734 | + default: | |
735 | + return null; | |
736 | + } | |
737 | + } | |
738 | + | |
739 | + private void onAlarmUpdate(AlarmUpdateMsg alarmUpdateMsg) { | |
740 | + EntityId originatorId = getAlarmOriginator(alarmUpdateMsg.getOriginatorName(), org.thingsboard.server.common.data.EntityType.valueOf(alarmUpdateMsg.getOriginatorType())); | |
741 | + if (originatorId != null) { | |
742 | + try { | |
743 | + Alarm existentAlarm = ctx.getAlarmService().findLatestByOriginatorAndType(edge.getTenantId(), originatorId, alarmUpdateMsg.getType()).get(); | |
744 | + switch (alarmUpdateMsg.getMsgType()) { | |
745 | + case ENTITY_CREATED_RPC_MESSAGE: | |
746 | + case ENTITY_UPDATED_RPC_MESSAGE: | |
747 | + if (existentAlarm == null) { | |
748 | + existentAlarm = new Alarm(); | |
749 | + existentAlarm.setTenantId(edge.getTenantId()); | |
750 | + existentAlarm.setType(alarmUpdateMsg.getName()); | |
751 | + existentAlarm.setOriginator(originatorId); | |
752 | + existentAlarm.setSeverity(AlarmSeverity.valueOf(alarmUpdateMsg.getSeverity())); | |
753 | + existentAlarm.setStatus(AlarmStatus.valueOf(alarmUpdateMsg.getStatus())); | |
754 | + existentAlarm.setStartTs(alarmUpdateMsg.getStartTs()); | |
755 | + existentAlarm.setAckTs(alarmUpdateMsg.getAckTs()); | |
756 | + existentAlarm.setClearTs(alarmUpdateMsg.getClearTs()); | |
757 | + existentAlarm.setPropagate(alarmUpdateMsg.getPropagate()); | |
758 | + } | |
759 | + existentAlarm.setEndTs(alarmUpdateMsg.getEndTs()); | |
760 | + existentAlarm.setDetails(objectMapper.readTree(alarmUpdateMsg.getDetails())); | |
761 | + ctx.getAlarmService().createOrUpdateAlarm(existentAlarm); | |
762 | + break; | |
763 | + case ALARM_ACK_RPC_MESSAGE: | |
764 | + if (existentAlarm != null) { | |
765 | + ctx.getAlarmService().ackAlarm(edge.getTenantId(), existentAlarm.getId(), alarmUpdateMsg.getAckTs()); | |
766 | + } | |
767 | + break; | |
768 | + case ALARM_CLEAR_RPC_MESSAGE: | |
769 | + if (existentAlarm != null) { | |
770 | + ctx.getAlarmService().clearAlarm(edge.getTenantId(), existentAlarm.getId(), objectMapper.readTree(alarmUpdateMsg.getDetails()), alarmUpdateMsg.getAckTs()); | |
771 | + } | |
772 | + break; | |
773 | + case ENTITY_DELETED_RPC_MESSAGE: | |
774 | + if (existentAlarm != null) { | |
775 | + ctx.getAlarmService().deleteAlarm(edge.getTenantId(), existentAlarm.getId()); | |
776 | + } | |
777 | + break; | |
778 | + } | |
779 | + } catch (Exception e) { | |
780 | + log.error("Error during finding existent alarm", e); | |
781 | + } | |
782 | + } | |
783 | + } | |
784 | + | |
785 | + private Device getOrCreateDevice(String deviceName, String deviceType) { | |
786 | + Device device = ctx.getDeviceService().findDeviceByTenantIdAndName(edge.getTenantId(), deviceName); | |
787 | + if (device == null) { | |
788 | + entityCreationLock.lock(); | |
789 | + try { | |
790 | + return processGetOrCreateDevice(deviceName, deviceType); | |
791 | + } finally { | |
792 | + entityCreationLock.unlock(); | |
793 | + } | |
794 | + } | |
795 | + return device; | |
796 | + } | |
797 | + | |
798 | + private Device processGetOrCreateDevice(String deviceName, String deviceType) { | |
799 | + Device device = ctx.getDeviceService().findDeviceByTenantIdAndName(edge.getTenantId(), deviceName); | |
800 | + if (device == null) { | |
801 | + device = new Device(); | |
802 | + device.setName(deviceName); | |
803 | + device.setType(deviceType); | |
804 | + device.setTenantId(edge.getTenantId()); | |
805 | + device.setCustomerId(edge.getCustomerId()); | |
806 | + device = ctx.getDeviceService().saveDevice(device); | |
807 | + device = ctx.getDeviceService().assignDeviceToEdge(edge.getTenantId(), device.getId(), edge.getId()); | |
808 | + createRelationFromEdge(device.getId()); | |
809 | + ctx.getActorService().onDeviceAdded(device); | |
810 | + pushDeviceCreatedEventToRuleEngine(device); | |
811 | + } | |
812 | + return device; | |
813 | + } | |
814 | + | |
815 | + private void pushDeviceCreatedEventToRuleEngine(Device device) { | |
816 | + try { | |
817 | + ObjectNode entityNode = objectMapper.valueToTree(device); | |
818 | + TbMsg msg = new TbMsg(UUIDs.timeBased(), DataConstants.ENTITY_CREATED, device.getId(), deviceActionTbMsgMetaData(device), objectMapper.writeValueAsString(entityNode), null, null, 0L); | |
819 | + ctx.getActorService().onMsg(new SendToClusterMsg(device.getId(), new ServiceToRuleEngineMsg(edge.getTenantId(), msg))); | |
820 | + } catch (JsonProcessingException | IllegalArgumentException e) { | |
821 | + log.warn("[{}] Failed to push device action to rule engine: {}", device.getId(), DataConstants.ENTITY_CREATED, e); | |
822 | + } | |
823 | + } | |
824 | + | |
825 | + private TbMsgMetaData deviceActionTbMsgMetaData(Device device) { | |
826 | + TbMsgMetaData metaData = getTbMsgMetaData(); | |
827 | + CustomerId customerId = device.getCustomerId(); | |
828 | + if (customerId != null && !customerId.isNullUid()) { | |
829 | + metaData.putValue("customerId", customerId.toString()); | |
830 | + } | |
831 | + return metaData; | |
832 | + } | |
833 | + | |
834 | + private TbMsgMetaData getTbMsgMetaData() { | |
835 | + TbMsgMetaData metaData = new TbMsgMetaData(); | |
836 | + metaData.putValue("edgeId", edge.getId().toString()); | |
837 | + metaData.putValue("edgeName", edge.getName()); | |
838 | + return metaData; | |
839 | + } | |
840 | + | |
841 | + private ConnectResponseMsg processConnect(ConnectRequestMsg request) { | |
842 | + Optional<Edge> optional = ctx.getEdgeService().findEdgeByRoutingKey(TenantId.SYS_TENANT_ID, request.getEdgeRoutingKey()); | |
843 | + if (optional.isPresent()) { | |
844 | + edge = optional.get(); | |
845 | + try { | |
846 | + if (edge.getSecret().equals(request.getEdgeSecret())) { | |
847 | + connected = true; | |
848 | + sessionOpenListener.accept(edge.getId(), this); | |
849 | + return ConnectResponseMsg.newBuilder() | |
850 | + .setResponseCode(ConnectResponseCode.ACCEPTED) | |
851 | + .setErrorMsg("") | |
852 | + .setConfiguration(constructEdgeConfigProto(edge)).build(); | |
853 | + } | |
854 | + return ConnectResponseMsg.newBuilder() | |
855 | + .setResponseCode(ConnectResponseCode.BAD_CREDENTIALS) | |
856 | + .setErrorMsg("Failed to validate the edge!") | |
857 | + .setConfiguration(EdgeConfiguration.getDefaultInstance()).build(); | |
858 | + } catch (Exception e) { | |
859 | + log.error("[{}] Failed to process edge connection!", request.getEdgeRoutingKey(), e); | |
860 | + return ConnectResponseMsg.newBuilder() | |
861 | + .setResponseCode(ConnectResponseCode.SERVER_UNAVAILABLE) | |
862 | + .setErrorMsg("Failed to process edge connection!") | |
863 | + .setConfiguration(EdgeConfiguration.getDefaultInstance()).build(); | |
864 | + } | |
865 | + } | |
866 | + return ConnectResponseMsg.newBuilder() | |
867 | + .setResponseCode(ConnectResponseCode.BAD_CREDENTIALS) | |
868 | + .setErrorMsg("Failed to find the edge! Routing key: " + request.getEdgeRoutingKey()) | |
869 | + .setConfiguration(EdgeConfiguration.getDefaultInstance()).build(); | |
870 | + } | |
871 | + | |
872 | + private void createRelationFromEdge(EntityId entityId) { | |
873 | + EntityRelation relation = new EntityRelation(); | |
874 | + relation.setFrom(edge.getId()); | |
875 | + relation.setTo(entityId); | |
876 | + relation.setTypeGroup(RelationTypeGroup.COMMON); | |
877 | + relation.setType(EntityRelation.EDGE_TYPE); | |
878 | + ctx.getRelationService().saveRelation(edge.getTenantId(), relation); | |
879 | + } | |
880 | + | |
881 | + private EdgeConfiguration constructEdgeConfigProto(Edge edge) throws JsonProcessingException { | |
882 | + return EdgeConfiguration.newBuilder() | |
883 | + .setTenantIdMSB(edge.getTenantId().getId().getMostSignificantBits()) | |
884 | + .setTenantIdLSB(edge.getTenantId().getId().getLeastSignificantBits()) | |
885 | + .setName(edge.getName()) | |
886 | + .setRoutingKey(edge.getRoutingKey()) | |
887 | + .setType(edge.getType().toString()) | |
888 | + .build(); | |
889 | + } | |
890 | +} | |
\ No newline at end of file | ... | ... |
... | ... | @@ -211,6 +211,8 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService |
211 | 211 | case "2.4.3": |
212 | 212 | try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { |
213 | 213 | log.info("Updating schema ..."); |
214 | + schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.5.0", SCHEMA_UPDATE_SQL); | |
215 | + loadSql(schemaUpdateFile, conn); | |
214 | 216 | try { |
215 | 217 | conn.createStatement().execute("ALTER TABLE attribute_kv ADD COLUMN json_v json;"); |
216 | 218 | } catch (Exception e) { |
... | ... | @@ -221,6 +223,24 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService |
221 | 223 | } |
222 | 224 | } |
223 | 225 | } |
226 | + try { | |
227 | + conn.createStatement().execute("ALTER TABLE asset ADD edge_id varchar(31)"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script | |
228 | + } catch (Exception e) {} | |
229 | + try { | |
230 | + conn.createStatement().execute("ALTER TABLE device ADD edge_id varchar(31)"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script | |
231 | + } catch (Exception e) {} | |
232 | + try { | |
233 | + conn.createStatement().execute("ALTER TABLE entity_view ADD edge_id varchar(31)"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script | |
234 | + } catch (Exception e) {} | |
235 | + try { | |
236 | + conn.createStatement().execute("ALTER TABLE dashboard ADD assigned_edges varchar(1000000)"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script | |
237 | + } catch (Exception e) {} | |
238 | + try { | |
239 | + conn.createStatement().execute("ALTER TABLE rule_chain ADD assigned_edges varchar(1000000)"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script | |
240 | + } catch (Exception e) {} | |
241 | + try { | |
242 | + conn.createStatement().execute("ALTER TABLE rule_chain ADD type varchar(255) DEFAULT 'SYSTEM'"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script | |
243 | + } catch (Exception e) {} | |
224 | 244 | log.info("Schema updated."); |
225 | 245 | } |
226 | 246 | break; | ... | ... |
... | ... | @@ -30,10 +30,12 @@ import org.thingsboard.server.common.data.Device; |
30 | 30 | import org.thingsboard.server.common.data.EntityView; |
31 | 31 | import org.thingsboard.server.common.data.Tenant; |
32 | 32 | import org.thingsboard.server.common.data.asset.Asset; |
33 | +import org.thingsboard.server.common.data.edge.Edge; | |
33 | 34 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
34 | 35 | import org.thingsboard.server.common.data.id.AssetId; |
35 | 36 | import org.thingsboard.server.common.data.id.CustomerId; |
36 | 37 | import org.thingsboard.server.common.data.id.DeviceId; |
38 | +import org.thingsboard.server.common.data.id.EdgeId; | |
37 | 39 | import org.thingsboard.server.common.data.id.EntityId; |
38 | 40 | import org.thingsboard.server.common.data.id.EntityIdFactory; |
39 | 41 | import org.thingsboard.server.common.data.id.EntityViewId; |
... | ... | @@ -47,6 +49,7 @@ import org.thingsboard.server.dao.alarm.AlarmService; |
47 | 49 | import org.thingsboard.server.dao.asset.AssetService; |
48 | 50 | import org.thingsboard.server.dao.customer.CustomerService; |
49 | 51 | import org.thingsboard.server.dao.device.DeviceService; |
52 | +import org.thingsboard.server.dao.edge.EdgeService; | |
50 | 53 | import org.thingsboard.server.dao.entityview.EntityViewService; |
51 | 54 | import org.thingsboard.server.dao.rule.RuleChainService; |
52 | 55 | import org.thingsboard.server.dao.tenant.TenantService; |
... | ... | @@ -74,6 +77,7 @@ public class AccessValidator { |
74 | 77 | public static final String SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION = "System administrator is not allowed to perform this operation!"; |
75 | 78 | public static final String DEVICE_WITH_REQUESTED_ID_NOT_FOUND = "Device with requested id wasn't found!"; |
76 | 79 | public static final String ENTITY_VIEW_WITH_REQUESTED_ID_NOT_FOUND = "Entity-view with requested id wasn't found!"; |
80 | + public static final String EDGE_WITH_REQUESTED_ID_NOT_FOUND = "Edge with requested id wasn't found!"; | |
77 | 81 | |
78 | 82 | @Autowired |
79 | 83 | protected TenantService tenantService; |
... | ... | @@ -100,6 +104,9 @@ public class AccessValidator { |
100 | 104 | protected EntityViewService entityViewService; |
101 | 105 | |
102 | 106 | @Autowired |
107 | + protected EdgeService edgeService; | |
108 | + | |
109 | + @Autowired | |
103 | 110 | protected AccessControlService accessControlService; |
104 | 111 | |
105 | 112 | private ExecutorService executor; |
... | ... | @@ -175,6 +182,9 @@ public class AccessValidator { |
175 | 182 | case ENTITY_VIEW: |
176 | 183 | validateEntityView(currentUser, operation, entityId, callback); |
177 | 184 | return; |
185 | + case EDGE: | |
186 | + validateEdge(currentUser, operation, entityId, callback); | |
187 | + return; | |
178 | 188 | default: |
179 | 189 | //TODO: add support of other entities |
180 | 190 | throw new IllegalStateException("Not Implemented!"); |
... | ... | @@ -328,6 +338,26 @@ public class AccessValidator { |
328 | 338 | } |
329 | 339 | } |
330 | 340 | |
341 | + private void validateEdge(final SecurityUser currentUser, Operation operation, EntityId entityId, FutureCallback<ValidationResult> callback) { | |
342 | + if (currentUser.isSystemAdmin()) { | |
343 | + callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); | |
344 | + } else { | |
345 | + ListenableFuture<Edge> edgeFuture = edgeService.findEdgeByIdAsync(currentUser.getTenantId(), new EdgeId(entityId.getId())); | |
346 | + Futures.addCallback(edgeFuture, getCallback(callback, edge -> { | |
347 | + if (edge == null) { | |
348 | + return ValidationResult.entityNotFound(EDGE_WITH_REQUESTED_ID_NOT_FOUND); | |
349 | + } else { | |
350 | + try { | |
351 | + accessControlService.checkPermission(currentUser, Resource.EDGE, operation, entityId, edge); | |
352 | + } catch (ThingsboardException e) { | |
353 | + return ValidationResult.accessDenied(e.getMessage()); | |
354 | + } | |
355 | + return ValidationResult.ok(edge); | |
356 | + } | |
357 | + }), executor); | |
358 | + } | |
359 | + } | |
360 | + | |
331 | 361 | private <T, V> FutureCallback<T> getCallback(FutureCallback<ValidationResult> callback, Function<T, ValidationResult<V>> transformer) { |
332 | 362 | return new FutureCallback<T>() { |
333 | 363 | @Override | ... | ... |
... | ... | @@ -37,6 +37,7 @@ public class CustomerUserPermissions extends AbstractPermissions { |
37 | 37 | put(Resource.CUSTOMER, customerPermissionChecker); |
38 | 38 | put(Resource.DASHBOARD, customerDashboardPermissionChecker); |
39 | 39 | put(Resource.ENTITY_VIEW, customerEntityPermissionChecker); |
40 | + put(Resource.EDGE, customerEntityPermissionChecker); | |
40 | 41 | put(Resource.USER, userPermissionChecker); |
41 | 42 | put(Resource.WIDGETS_BUNDLE, widgetsPermissionChecker); |
42 | 43 | put(Resource.WIDGET_TYPE, widgetsPermissionChecker); | ... | ... |
... | ... | @@ -18,6 +18,7 @@ package org.thingsboard.server.service.security.permission; |
18 | 18 | public enum Operation { |
19 | 19 | |
20 | 20 | ALL, CREATE, READ, WRITE, DELETE, ASSIGN_TO_CUSTOMER, UNASSIGN_FROM_CUSTOMER, RPC_CALL, |
21 | - READ_CREDENTIALS, WRITE_CREDENTIALS, READ_ATTRIBUTES, WRITE_ATTRIBUTES, READ_TELEMETRY, WRITE_TELEMETRY, CLAIM_DEVICES | |
21 | + READ_CREDENTIALS, WRITE_CREDENTIALS, READ_ATTRIBUTES, WRITE_ATTRIBUTES, READ_TELEMETRY, WRITE_TELEMETRY, CLAIM_DEVICES, | |
22 | + ASSIGN_TO_EDGE, UNASSIGN_FROM_EDGE | |
22 | 23 | |
23 | 24 | } | ... | ... |
... | ... | @@ -27,6 +27,7 @@ public enum Resource { |
27 | 27 | CUSTOMER(EntityType.CUSTOMER), |
28 | 28 | DASHBOARD(EntityType.DASHBOARD), |
29 | 29 | ENTITY_VIEW(EntityType.ENTITY_VIEW), |
30 | + EDGE(EntityType.EDGE), | |
30 | 31 | TENANT(EntityType.TENANT), |
31 | 32 | RULE_CHAIN(EntityType.RULE_CHAIN), |
32 | 33 | USER(EntityType.USER), | ... | ... |
... | ... | @@ -37,6 +37,7 @@ public class TenantAdminPermissions extends AbstractPermissions { |
37 | 37 | put(Resource.CUSTOMER, tenantEntityPermissionChecker); |
38 | 38 | put(Resource.DASHBOARD, tenantEntityPermissionChecker); |
39 | 39 | put(Resource.ENTITY_VIEW, tenantEntityPermissionChecker); |
40 | + put(Resource.EDGE, tenantEntityPermissionChecker); | |
40 | 41 | put(Resource.TENANT, tenantPermissionChecker); |
41 | 42 | put(Resource.RULE_CHAIN, tenantEntityPermissionChecker); |
42 | 43 | put(Resource.USER, userPermissionChecker); | ... | ... |
... | ... | @@ -294,6 +294,9 @@ caffeine: |
294 | 294 | securitySettings: |
295 | 295 | timeToLiveInMinutes: 1440 |
296 | 296 | maxSize: 1 |
297 | + edges: | |
298 | + timeToLiveInMinutes: 1440 | |
299 | + maxSize: 100000 | |
297 | 300 | |
298 | 301 | redis: |
299 | 302 | # standalone or cluster |
... | ... | @@ -558,6 +561,17 @@ transport: |
558 | 561 | bind_port: "${COAP_BIND_PORT:5683}" |
559 | 562 | timeout: "${COAP_TIMEOUT:10000}" |
560 | 563 | |
564 | +# Edges parameters | |
565 | +edges: | |
566 | + rpc: | |
567 | + enabled: "${EDGES_RPC_ENABLED:true}" | |
568 | + port: "${EDGES_RPC_PORT:60061}" | |
569 | + ssl: | |
570 | + # Enable/disable SSL support | |
571 | + enabled: "${EDGES_RPC_SSL_ENABLED:false}" | |
572 | + cert: "${EDGES_RPC_SSL_CERT:certChainFile.pem}" | |
573 | + privateKey: "${EDGES_RPC_SSL_PRIVATE_KEY:privateKeyFile.pem}" | |
574 | + | |
561 | 575 | swagger: |
562 | 576 | api_path_regex: "${SWAGGER_API_PATH_REGEX:/api.*}" |
563 | 577 | security_path_regex: "${SWAGGER_SECURITY_PATH_REGEX:/api.*}" | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2020 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.controller; | |
17 | + | |
18 | +import com.datastax.driver.core.utils.UUIDs; | |
19 | +import com.fasterxml.jackson.core.type.TypeReference; | |
20 | +import org.apache.commons.lang3.RandomStringUtils; | |
21 | +import org.junit.After; | |
22 | +import org.junit.Assert; | |
23 | +import org.junit.Before; | |
24 | +import org.junit.Test; | |
25 | +import org.thingsboard.server.common.data.Customer; | |
26 | +import org.thingsboard.server.common.data.EntitySubtype; | |
27 | +import org.thingsboard.server.common.data.Tenant; | |
28 | +import org.thingsboard.server.common.data.User; | |
29 | +import org.thingsboard.server.common.data.edge.Edge; | |
30 | +import org.thingsboard.server.common.data.id.CustomerId; | |
31 | +import org.thingsboard.server.common.data.page.PageData; | |
32 | +import org.thingsboard.server.common.data.page.PageLink; | |
33 | +import org.thingsboard.server.common.data.page.PageData; | |
34 | +import org.thingsboard.server.common.data.page.PageLink; | |
35 | +import org.thingsboard.server.common.data.security.Authority; | |
36 | +import org.thingsboard.server.dao.model.ModelConstants; | |
37 | + | |
38 | +import java.util.ArrayList; | |
39 | +import java.util.Collections; | |
40 | +import java.util.List; | |
41 | + | |
42 | +import static org.hamcrest.Matchers.containsString; | |
43 | +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; | |
44 | +import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; | |
45 | + | |
46 | +public abstract class BaseEdgeControllerTest extends AbstractControllerTest { | |
47 | + | |
48 | + private IdComparator<Edge> idComparator = new IdComparator<>(); | |
49 | + | |
50 | + private Tenant savedTenant; | |
51 | + private User tenantAdmin; | |
52 | + | |
53 | + @Before | |
54 | + public void beforeTest() throws Exception { | |
55 | + loginSysAdmin(); | |
56 | + | |
57 | + Tenant tenant = new Tenant(); | |
58 | + tenant.setTitle("My tenant"); | |
59 | + savedTenant = doPost("/api/tenant", tenant, Tenant.class); | |
60 | + Assert.assertNotNull(savedTenant); | |
61 | + | |
62 | + tenantAdmin = new User(); | |
63 | + tenantAdmin.setAuthority(Authority.TENANT_ADMIN); | |
64 | + tenantAdmin.setTenantId(savedTenant.getId()); | |
65 | + tenantAdmin.setEmail("tenant2@thingsboard.org"); | |
66 | + tenantAdmin.setFirstName("Joe"); | |
67 | + tenantAdmin.setLastName("Downs"); | |
68 | + | |
69 | + tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1"); | |
70 | + } | |
71 | + | |
72 | + @After | |
73 | + public void afterTest() throws Exception { | |
74 | + loginSysAdmin(); | |
75 | + | |
76 | + doDelete("/api/tenant/" + savedTenant.getId().getId().toString()) | |
77 | + .andExpect(status().isOk()); | |
78 | + } | |
79 | + | |
80 | + @Test | |
81 | + public void testSaveEdge() throws Exception { | |
82 | + Edge edge = new Edge(); | |
83 | + edge.setName("My edge"); | |
84 | + edge.setType("default"); | |
85 | + Edge savedEdge = doPost("/api/edge", edge, Edge.class); | |
86 | + | |
87 | + Assert.assertNotNull(savedEdge); | |
88 | + Assert.assertNotNull(savedEdge.getId()); | |
89 | + Assert.assertTrue(savedEdge.getCreatedTime() > 0); | |
90 | + Assert.assertEquals(savedTenant.getId(), savedEdge.getTenantId()); | |
91 | + Assert.assertNotNull(savedEdge.getCustomerId()); | |
92 | + Assert.assertEquals(NULL_UUID, savedEdge.getCustomerId().getId()); | |
93 | + Assert.assertEquals(edge.getName(), savedEdge.getName()); | |
94 | + | |
95 | + savedEdge.setName("My new edge"); | |
96 | + doPost("/api/edge", savedEdge, Edge.class); | |
97 | + | |
98 | + Edge foundEdge = doGet("/api/edge/" + savedEdge.getId().getId().toString(), Edge.class); | |
99 | + Assert.assertEquals(foundEdge.getName(), savedEdge.getName()); | |
100 | + } | |
101 | + | |
102 | + @Test | |
103 | + public void testFindEdgeById() throws Exception { | |
104 | + Edge edge = new Edge(); | |
105 | + edge.setName("My edge"); | |
106 | + edge.setType("default"); | |
107 | + Edge savedEdge = doPost("/api/edge", edge, Edge.class); | |
108 | + Edge foundEdge = doGet("/api/edge/" + savedEdge.getId().getId().toString(), Edge.class); | |
109 | + Assert.assertNotNull(foundEdge); | |
110 | + Assert.assertEquals(savedEdge, foundEdge); | |
111 | + } | |
112 | + | |
113 | + @Test | |
114 | + public void testFindEdgeTypesByTenantId() throws Exception { | |
115 | + List<Edge> edges = new ArrayList<>(); | |
116 | + for (int i = 0; i < 3; i++) { | |
117 | + Edge edge = new Edge(); | |
118 | + edge.setName("My edge B" + i); | |
119 | + edge.setType("typeB"); | |
120 | + edges.add(doPost("/api/edge", edge, Edge.class)); | |
121 | + } | |
122 | + for (int i = 0; i < 7; i++) { | |
123 | + Edge edge = new Edge(); | |
124 | + edge.setName("My edge C" + i); | |
125 | + edge.setType("typeC"); | |
126 | + edges.add(doPost("/api/edge", edge, Edge.class)); | |
127 | + } | |
128 | + for (int i = 0; i < 9; i++) { | |
129 | + Edge edge = new Edge(); | |
130 | + edge.setName("My edge A" + i); | |
131 | + edge.setType("typeA"); | |
132 | + edges.add(doPost("/api/edge", edge, Edge.class)); | |
133 | + } | |
134 | + List<EntitySubtype> edgeTypes = doGetTyped("/api/edge/types", | |
135 | + new TypeReference<List<EntitySubtype>>() { | |
136 | + }); | |
137 | + | |
138 | + Assert.assertNotNull(edgeTypes); | |
139 | + Assert.assertEquals(3, edgeTypes.size()); | |
140 | + Assert.assertEquals("typeA", edgeTypes.get(0).getType()); | |
141 | + Assert.assertEquals("typeB", edgeTypes.get(1).getType()); | |
142 | + Assert.assertEquals("typeC", edgeTypes.get(2).getType()); | |
143 | + } | |
144 | + | |
145 | + @Test | |
146 | + public void testDeleteEdge() throws Exception { | |
147 | + Edge edge = new Edge(); | |
148 | + edge.setName("My edge"); | |
149 | + edge.setType("default"); | |
150 | + Edge savedEdge = doPost("/api/edge", edge, Edge.class); | |
151 | + | |
152 | + doDelete("/api/edge/" + savedEdge.getId().getId().toString()) | |
153 | + .andExpect(status().isOk()); | |
154 | + | |
155 | + doGet("/api/edge/" + savedEdge.getId().getId().toString()) | |
156 | + .andExpect(status().isNotFound()); | |
157 | + } | |
158 | + | |
159 | + @Test | |
160 | + public void testSaveEdgeWithEmptyType() throws Exception { | |
161 | + Edge edge = new Edge(); | |
162 | + edge.setName("My edge"); | |
163 | + doPost("/api/edge", edge) | |
164 | + .andExpect(status().isBadRequest()) | |
165 | + .andExpect(statusReason(containsString("Edge type should be specified"))); | |
166 | + } | |
167 | + | |
168 | + @Test | |
169 | + public void testSaveEdgeWithEmptyName() throws Exception { | |
170 | + Edge edge = new Edge(); | |
171 | + edge.setType("default"); | |
172 | + doPost("/api/edge", edge) | |
173 | + .andExpect(status().isBadRequest()) | |
174 | + .andExpect(statusReason(containsString("Edge name should be specified"))); | |
175 | + } | |
176 | + | |
177 | + @Test | |
178 | + public void testAssignUnassignEdgeToCustomer() throws Exception { | |
179 | + Edge edge = new Edge(); | |
180 | + edge.setName("My edge"); | |
181 | + edge.setType("default"); | |
182 | + Edge savedEdge = doPost("/api/edge", edge, Edge.class); | |
183 | + | |
184 | + Customer customer = new Customer(); | |
185 | + customer.setTitle("My customer"); | |
186 | + Customer savedCustomer = doPost("/api/customer", customer, Customer.class); | |
187 | + | |
188 | + Edge assignedEdge = doPost("/api/customer/" + savedCustomer.getId().getId().toString() | |
189 | + + "/edge/" + savedEdge.getId().getId().toString(), Edge.class); | |
190 | + Assert.assertEquals(savedCustomer.getId(), assignedEdge.getCustomerId()); | |
191 | + | |
192 | + Edge foundEdge = doGet("/api/edge/" + savedEdge.getId().getId().toString(), Edge.class); | |
193 | + Assert.assertEquals(savedCustomer.getId(), foundEdge.getCustomerId()); | |
194 | + | |
195 | + Edge unassignedEdge = | |
196 | + doDelete("/api/customer/edge/" + savedEdge.getId().getId().toString(), Edge.class); | |
197 | + Assert.assertEquals(ModelConstants.NULL_UUID, unassignedEdge.getCustomerId().getId()); | |
198 | + | |
199 | + foundEdge = doGet("/api/edge/" + savedEdge.getId().getId().toString(), Edge.class); | |
200 | + Assert.assertEquals(ModelConstants.NULL_UUID, foundEdge.getCustomerId().getId()); | |
201 | + } | |
202 | + | |
203 | + @Test | |
204 | + public void testAssignEdgeToNonExistentCustomer() throws Exception { | |
205 | + Edge edge = new Edge(); | |
206 | + edge.setName("My edge"); | |
207 | + edge.setType("default"); | |
208 | + Edge savedEdge = doPost("/api/edge", edge, Edge.class); | |
209 | + | |
210 | + doPost("/api/customer/" + UUIDs.timeBased().toString() | |
211 | + + "/edge/" + savedEdge.getId().getId().toString()) | |
212 | + .andExpect(status().isNotFound()); | |
213 | + } | |
214 | + | |
215 | + @Test | |
216 | + public void testAssignEdgeToCustomerFromDifferentTenant() throws Exception { | |
217 | + loginSysAdmin(); | |
218 | + | |
219 | + Tenant tenant2 = new Tenant(); | |
220 | + tenant2.setTitle("Different tenant"); | |
221 | + Tenant savedTenant2 = doPost("/api/tenant", tenant2, Tenant.class); | |
222 | + Assert.assertNotNull(savedTenant2); | |
223 | + | |
224 | + User tenantAdmin2 = new User(); | |
225 | + tenantAdmin2.setAuthority(Authority.TENANT_ADMIN); | |
226 | + tenantAdmin2.setTenantId(savedTenant2.getId()); | |
227 | + tenantAdmin2.setEmail("tenant3@thingsboard.org"); | |
228 | + tenantAdmin2.setFirstName("Joe"); | |
229 | + tenantAdmin2.setLastName("Downs"); | |
230 | + | |
231 | + tenantAdmin2 = createUserAndLogin(tenantAdmin2, "testPassword1"); | |
232 | + | |
233 | + Customer customer = new Customer(); | |
234 | + customer.setTitle("Different customer"); | |
235 | + Customer savedCustomer = doPost("/api/customer", customer, Customer.class); | |
236 | + | |
237 | + login(tenantAdmin.getEmail(), "testPassword1"); | |
238 | + | |
239 | + Edge edge = new Edge(); | |
240 | + edge.setName("My edge"); | |
241 | + edge.setType("default"); | |
242 | + Edge savedEdge = doPost("/api/edge", edge, Edge.class); | |
243 | + | |
244 | + doPost("/api/customer/" + savedCustomer.getId().getId().toString() | |
245 | + + "/edge/" + savedEdge.getId().getId().toString()) | |
246 | + .andExpect(status().isForbidden()); | |
247 | + | |
248 | + loginSysAdmin(); | |
249 | + | |
250 | + doDelete("/api/tenant/" + savedTenant2.getId().getId().toString()) | |
251 | + .andExpect(status().isOk()); | |
252 | + } | |
253 | + | |
254 | + @Test | |
255 | + public void testFindTenantEdges() throws Exception { | |
256 | + List<Edge> edges = new ArrayList<>(); | |
257 | + for (int i = 0; i < 178; i++) { | |
258 | + Edge edge = new Edge(); | |
259 | + edge.setName("Edge" + i); | |
260 | + edge.setType("default"); | |
261 | + edges.add(doPost("/api/edge", edge, Edge.class)); | |
262 | + } | |
263 | + List<Edge> loadedEdges = new ArrayList<>(); | |
264 | + PageLink pageLink = new PageLink(23); | |
265 | + PageData<Edge> pageData = null; | |
266 | + do { | |
267 | + pageData = doGetTypedWithPageLink("/api/tenant/edges?", | |
268 | + new TypeReference<PageData<Edge>>() { | |
269 | + }, pageLink); | |
270 | + loadedEdges.addAll(pageData.getData()); | |
271 | + if (pageData.hasNext()) { | |
272 | + pageLink = pageLink.nextPageLink(); | |
273 | + } | |
274 | + } while (pageData.hasNext()); | |
275 | + | |
276 | + Collections.sort(edges, idComparator); | |
277 | + Collections.sort(loadedEdges, idComparator); | |
278 | + | |
279 | + Assert.assertEquals(edges, loadedEdges); | |
280 | + } | |
281 | + | |
282 | + @Test | |
283 | + public void testFindTenantEdgesByName() throws Exception { | |
284 | + String title1 = "Edge title 1"; | |
285 | + List<Edge> edgesTitle1 = new ArrayList<>(); | |
286 | + for (int i = 0; i < 143; i++) { | |
287 | + Edge edge = new Edge(); | |
288 | + String suffix = RandomStringUtils.randomAlphanumeric(15); | |
289 | + String name = title1 + suffix; | |
290 | + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); | |
291 | + edge.setName(name); | |
292 | + edge.setType("default"); | |
293 | + edgesTitle1.add(doPost("/api/edge", edge, Edge.class)); | |
294 | + } | |
295 | + String title2 = "Edge title 2"; | |
296 | + List<Edge> edgesTitle2 = new ArrayList<>(); | |
297 | + for (int i = 0; i < 75; i++) { | |
298 | + Edge edge = new Edge(); | |
299 | + String suffix = RandomStringUtils.randomAlphanumeric(15); | |
300 | + String name = title2 + suffix; | |
301 | + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); | |
302 | + edge.setName(name); | |
303 | + edge.setType("default"); | |
304 | + edgesTitle2.add(doPost("/api/edge", edge, Edge.class)); | |
305 | + } | |
306 | + | |
307 | + List<Edge> loadedEdgesTitle1 = new ArrayList<>(); | |
308 | + PageLink pageLink = new PageLink(15, 0, title1); | |
309 | + PageData<Edge> pageData = null; | |
310 | + do { | |
311 | + pageData = doGetTypedWithPageLink("/api/tenant/edges?", | |
312 | + new TypeReference<PageData<Edge>>() { | |
313 | + }, pageLink); | |
314 | + loadedEdgesTitle1.addAll(pageData.getData()); | |
315 | + if (pageData.hasNext()) { | |
316 | + pageLink = pageLink.nextPageLink(); | |
317 | + } | |
318 | + } while (pageData.hasNext()); | |
319 | + | |
320 | + Collections.sort(edgesTitle1, idComparator); | |
321 | + Collections.sort(loadedEdgesTitle1, idComparator); | |
322 | + | |
323 | + Assert.assertEquals(edgesTitle1, loadedEdgesTitle1); | |
324 | + | |
325 | + List<Edge> loadedEdgesTitle2 = new ArrayList<>(); | |
326 | + pageLink = new PageLink(4, 0, title2); | |
327 | + do { | |
328 | + pageData = doGetTypedWithPageLink("/api/tenant/edges?", | |
329 | + new TypeReference<PageData<Edge>>() { | |
330 | + }, pageLink); | |
331 | + loadedEdgesTitle2.addAll(pageData.getData()); | |
332 | + if (pageData.hasNext()) { | |
333 | + pageLink = pageLink.nextPageLink(); | |
334 | + } | |
335 | + } while (pageData.hasNext()); | |
336 | + | |
337 | + Collections.sort(edgesTitle2, idComparator); | |
338 | + Collections.sort(loadedEdgesTitle2, idComparator); | |
339 | + | |
340 | + Assert.assertEquals(edgesTitle2, loadedEdgesTitle2); | |
341 | + | |
342 | + for (Edge edge : loadedEdgesTitle1) { | |
343 | + doDelete("/api/edge/" + edge.getId().getId().toString()) | |
344 | + .andExpect(status().isOk()); | |
345 | + } | |
346 | + | |
347 | + pageLink = new PageLink(4, 0, title1); | |
348 | + pageData = doGetTypedWithPageLink("/api/tenant/edges?", | |
349 | + new TypeReference<PageData<Edge>>() { | |
350 | + }, pageLink); | |
351 | + Assert.assertFalse(pageData.hasNext()); | |
352 | + Assert.assertEquals(0, pageData.getData().size()); | |
353 | + | |
354 | + for (Edge edge : loadedEdgesTitle2) { | |
355 | + doDelete("/api/edge/" + edge.getId().getId().toString()) | |
356 | + .andExpect(status().isOk()); | |
357 | + } | |
358 | + | |
359 | + pageLink = new PageLink(4, 0, title2); | |
360 | + pageData = doGetTypedWithPageLink("/api/tenant/edges?", | |
361 | + new TypeReference<PageData<Edge>>() { | |
362 | + }, pageLink); | |
363 | + Assert.assertFalse(pageData.hasNext()); | |
364 | + Assert.assertEquals(0, pageData.getData().size()); | |
365 | + } | |
366 | + | |
367 | + @Test | |
368 | + public void testFindTenantEdgesByType() throws Exception { | |
369 | + String title1 = "Edge title 1"; | |
370 | + String type1 = "typeA"; | |
371 | + List<Edge> edgesType1 = new ArrayList<>(); | |
372 | + for (int i = 0; i < 143; i++) { | |
373 | + Edge edge = new Edge(); | |
374 | + String suffix = RandomStringUtils.randomAlphanumeric(15); | |
375 | + String name = title1 + suffix; | |
376 | + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); | |
377 | + edge.setName(name); | |
378 | + edge.setType(type1); | |
379 | + edgesType1.add(doPost("/api/edge", edge, Edge.class)); | |
380 | + } | |
381 | + String title2 = "Edge title 2"; | |
382 | + String type2 = "typeB"; | |
383 | + List<Edge> edgesType2 = new ArrayList<>(); | |
384 | + for (int i = 0; i < 75; i++) { | |
385 | + Edge edge = new Edge(); | |
386 | + String suffix = RandomStringUtils.randomAlphanumeric(15); | |
387 | + String name = title2 + suffix; | |
388 | + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); | |
389 | + edge.setName(name); | |
390 | + edge.setType(type2); | |
391 | + edgesType2.add(doPost("/api/edge", edge, Edge.class)); | |
392 | + } | |
393 | + | |
394 | + List<Edge> loadedEdgesType1 = new ArrayList<>(); | |
395 | + PageLink pageLink = new PageLink(15); | |
396 | + PageData<Edge> pageData = null; | |
397 | + do { | |
398 | + pageData = doGetTypedWithPageLink("/api/tenant/edges?type={type}&", | |
399 | + new TypeReference<PageData<Edge>>() { | |
400 | + }, pageLink, type1); | |
401 | + loadedEdgesType1.addAll(pageData.getData()); | |
402 | + if (pageData.hasNext()) { | |
403 | + pageLink = pageLink.nextPageLink(); | |
404 | + } | |
405 | + } while (pageData.hasNext()); | |
406 | + | |
407 | + Collections.sort(edgesType1, idComparator); | |
408 | + Collections.sort(loadedEdgesType1, idComparator); | |
409 | + | |
410 | + Assert.assertEquals(edgesType1, loadedEdgesType1); | |
411 | + | |
412 | + List<Edge> loadedEdgesType2 = new ArrayList<>(); | |
413 | + pageLink = new PageLink(4); | |
414 | + do { | |
415 | + pageData = doGetTypedWithPageLink("/api/tenant/edges?type={type}&", | |
416 | + new TypeReference<PageData<Edge>>() { | |
417 | + }, pageLink, type2); | |
418 | + loadedEdgesType2.addAll(pageData.getData()); | |
419 | + if (pageData.hasNext()) { | |
420 | + pageLink = pageLink.nextPageLink(); | |
421 | + } | |
422 | + } while (pageData.hasNext()); | |
423 | + | |
424 | + Collections.sort(edgesType2, idComparator); | |
425 | + Collections.sort(loadedEdgesType2, idComparator); | |
426 | + | |
427 | + Assert.assertEquals(edgesType2, loadedEdgesType2); | |
428 | + | |
429 | + for (Edge edge : loadedEdgesType1) { | |
430 | + doDelete("/api/edge/" + edge.getId().getId().toString()) | |
431 | + .andExpect(status().isOk()); | |
432 | + } | |
433 | + | |
434 | + pageLink = new PageLink(4); | |
435 | + pageData = doGetTypedWithPageLink("/api/tenant/edges?type={type}&", | |
436 | + new TypeReference<PageData<Edge>>() { | |
437 | + }, pageLink, type1); | |
438 | + Assert.assertFalse(pageData.hasNext()); | |
439 | + Assert.assertEquals(0, pageData.getData().size()); | |
440 | + | |
441 | + for (Edge edge : loadedEdgesType2) { | |
442 | + doDelete("/api/edge/" + edge.getId().getId().toString()) | |
443 | + .andExpect(status().isOk()); | |
444 | + } | |
445 | + | |
446 | + pageLink = new PageLink(4); | |
447 | + pageData = doGetTypedWithPageLink("/api/tenant/edges?type={type}&", | |
448 | + new TypeReference<PageData<Edge>>() { | |
449 | + }, pageLink, type2); | |
450 | + Assert.assertFalse(pageData.hasNext()); | |
451 | + Assert.assertEquals(0, pageData.getData().size()); | |
452 | + } | |
453 | + | |
454 | + @Test | |
455 | + public void testFindCustomerEdges() throws Exception { | |
456 | + Customer customer = new Customer(); | |
457 | + customer.setTitle("Test customer"); | |
458 | + customer = doPost("/api/customer", customer, Customer.class); | |
459 | + CustomerId customerId = customer.getId(); | |
460 | + | |
461 | + List<Edge> edges = new ArrayList<>(); | |
462 | + for (int i = 0; i < 128; i++) { | |
463 | + Edge edge = new Edge(); | |
464 | + edge.setName("Edge" + i); | |
465 | + edge.setType("default"); | |
466 | + edge = doPost("/api/edge", edge, Edge.class); | |
467 | + edges.add(doPost("/api/customer/" + customerId.getId().toString() | |
468 | + + "/edge/" + edge.getId().getId().toString(), Edge.class)); | |
469 | + } | |
470 | + | |
471 | + List<Edge> loadedEdges = new ArrayList<>(); | |
472 | + PageLink pageLink = new PageLink(23); | |
473 | + PageData<Edge> pageData = null; | |
474 | + do { | |
475 | + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/edges?", | |
476 | + new TypeReference<PageData<Edge>>() { | |
477 | + }, pageLink); | |
478 | + loadedEdges.addAll(pageData.getData()); | |
479 | + if (pageData.hasNext()) { | |
480 | + pageLink = pageLink.nextPageLink(); | |
481 | + } | |
482 | + } while (pageData.hasNext()); | |
483 | + | |
484 | + Collections.sort(edges, idComparator); | |
485 | + Collections.sort(loadedEdges, idComparator); | |
486 | + | |
487 | + Assert.assertEquals(edges, loadedEdges); | |
488 | + } | |
489 | + | |
490 | + @Test | |
491 | + public void testFindCustomerEdgesByName() throws Exception { | |
492 | + Customer customer = new Customer(); | |
493 | + customer.setTitle("Test customer"); | |
494 | + customer = doPost("/api/customer", customer, Customer.class); | |
495 | + CustomerId customerId = customer.getId(); | |
496 | + | |
497 | + String title1 = "Edge title 1"; | |
498 | + List<Edge> edgesTitle1 = new ArrayList<>(); | |
499 | + for (int i = 0; i < 125; i++) { | |
500 | + Edge edge = new Edge(); | |
501 | + String suffix = RandomStringUtils.randomAlphanumeric(15); | |
502 | + String name = title1 + suffix; | |
503 | + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); | |
504 | + edge.setName(name); | |
505 | + edge.setType("default"); | |
506 | + edge = doPost("/api/edge", edge, Edge.class); | |
507 | + edgesTitle1.add(doPost("/api/customer/" + customerId.getId().toString() | |
508 | + + "/edge/" + edge.getId().getId().toString(), Edge.class)); | |
509 | + } | |
510 | + String title2 = "Edge title 2"; | |
511 | + List<Edge> edgesTitle2 = new ArrayList<>(); | |
512 | + for (int i = 0; i < 143; i++) { | |
513 | + Edge edge = new Edge(); | |
514 | + String suffix = RandomStringUtils.randomAlphanumeric(15); | |
515 | + String name = title2 + suffix; | |
516 | + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); | |
517 | + edge.setName(name); | |
518 | + edge.setType("default"); | |
519 | + edge = doPost("/api/edge", edge, Edge.class); | |
520 | + edgesTitle2.add(doPost("/api/customer/" + customerId.getId().toString() | |
521 | + + "/edge/" + edge.getId().getId().toString(), Edge.class)); | |
522 | + } | |
523 | + | |
524 | + List<Edge> loadedEdgesTitle1 = new ArrayList<>(); | |
525 | + PageLink pageLink = new PageLink(15,0, title1); | |
526 | + PageData<Edge> pageData = null; | |
527 | + do { | |
528 | + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/edges?", | |
529 | + new TypeReference<PageData<Edge>>() { | |
530 | + }, pageLink); | |
531 | + loadedEdgesTitle1.addAll(pageData.getData()); | |
532 | + if (pageData.hasNext()) { | |
533 | + pageLink = pageLink.nextPageLink(); | |
534 | + } | |
535 | + } while (pageData.hasNext()); | |
536 | + | |
537 | + Collections.sort(edgesTitle1, idComparator); | |
538 | + Collections.sort(loadedEdgesTitle1, idComparator); | |
539 | + | |
540 | + Assert.assertEquals(edgesTitle1, loadedEdgesTitle1); | |
541 | + | |
542 | + List<Edge> loadedEdgesTitle2 = new ArrayList<>(); | |
543 | + pageLink = new PageLink(4,0, title2); | |
544 | + do { | |
545 | + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/edges?", | |
546 | + new TypeReference<PageData<Edge>>() { | |
547 | + }, pageLink); | |
548 | + loadedEdgesTitle2.addAll(pageData.getData()); | |
549 | + if (pageData.hasNext()) { | |
550 | + pageLink = pageLink.nextPageLink(); | |
551 | + } | |
552 | + } while (pageData.hasNext()); | |
553 | + | |
554 | + Collections.sort(edgesTitle2, idComparator); | |
555 | + Collections.sort(loadedEdgesTitle2, idComparator); | |
556 | + | |
557 | + Assert.assertEquals(edgesTitle2, loadedEdgesTitle2); | |
558 | + | |
559 | + for (Edge edge : loadedEdgesTitle1) { | |
560 | + doDelete("/api/customer/edge/" + edge.getId().getId().toString()) | |
561 | + .andExpect(status().isOk()); | |
562 | + } | |
563 | + | |
564 | + pageLink = new PageLink(4, 0, title1); | |
565 | + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/edges?", | |
566 | + new TypeReference<PageData<Edge>>() { | |
567 | + }, pageLink); | |
568 | + Assert.assertFalse(pageData.hasNext()); | |
569 | + Assert.assertEquals(0, pageData.getData().size()); | |
570 | + | |
571 | + for (Edge edge : loadedEdgesTitle2) { | |
572 | + doDelete("/api/customer/edge/" + edge.getId().getId().toString()) | |
573 | + .andExpect(status().isOk()); | |
574 | + } | |
575 | + | |
576 | + pageLink = new PageLink(4, 0, title2); | |
577 | + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/edges?", | |
578 | + new TypeReference<PageData<Edge>>() { | |
579 | + }, pageLink); | |
580 | + Assert.assertFalse(pageData.hasNext()); | |
581 | + Assert.assertEquals(0, pageData.getData().size()); | |
582 | + } | |
583 | + | |
584 | + @Test | |
585 | + public void testFindCustomerEdgesByType() throws Exception { | |
586 | + Customer customer = new Customer(); | |
587 | + customer.setTitle("Test customer"); | |
588 | + customer = doPost("/api/customer", customer, Customer.class); | |
589 | + CustomerId customerId = customer.getId(); | |
590 | + | |
591 | + String title1 = "Edge title 1"; | |
592 | + String type1 = "typeC"; | |
593 | + List<Edge> edgesType1 = new ArrayList<>(); | |
594 | + for (int i = 0; i < 125; i++) { | |
595 | + Edge edge = new Edge(); | |
596 | + String suffix = RandomStringUtils.randomAlphanumeric(15); | |
597 | + String name = title1 + suffix; | |
598 | + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); | |
599 | + edge.setName(name); | |
600 | + edge.setType(type1); | |
601 | + edge = doPost("/api/edge", edge, Edge.class); | |
602 | + edgesType1.add(doPost("/api/customer/" + customerId.getId().toString() | |
603 | + + "/edge/" + edge.getId().getId().toString(), Edge.class)); | |
604 | + } | |
605 | + String title2 = "Edge title 2"; | |
606 | + String type2 = "typeD"; | |
607 | + List<Edge> edgesType2 = new ArrayList<>(); | |
608 | + for (int i = 0; i < 143; i++) { | |
609 | + Edge edge = new Edge(); | |
610 | + String suffix = RandomStringUtils.randomAlphanumeric(15); | |
611 | + String name = title2 + suffix; | |
612 | + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); | |
613 | + edge.setName(name); | |
614 | + edge.setType(type2); | |
615 | + edge = doPost("/api/edge", edge, Edge.class); | |
616 | + edgesType2.add(doPost("/api/customer/" + customerId.getId().toString() | |
617 | + + "/edge/" + edge.getId().getId().toString(), Edge.class)); | |
618 | + } | |
619 | + | |
620 | + List<Edge> loadedEdgesType1 = new ArrayList<>(); | |
621 | + PageLink pageLink = new PageLink(15, 0, title1); | |
622 | + PageData<Edge> pageData = null; | |
623 | + do { | |
624 | + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/edges?type={type}&", | |
625 | + new TypeReference<PageData<Edge>>() { | |
626 | + }, pageLink, type1); | |
627 | + loadedEdgesType1.addAll(pageData.getData()); | |
628 | + if (pageData.hasNext()) { | |
629 | + pageLink = pageLink.nextPageLink(); | |
630 | + } | |
631 | + } while (pageData.hasNext()); | |
632 | + | |
633 | + Collections.sort(edgesType1, idComparator); | |
634 | + Collections.sort(loadedEdgesType1, idComparator); | |
635 | + | |
636 | + Assert.assertEquals(edgesType1, loadedEdgesType1); | |
637 | + | |
638 | + List<Edge> loadedEdgesType2 = new ArrayList<>(); | |
639 | + pageLink = new PageLink(4, 0, title2); | |
640 | + do { | |
641 | + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/edges?type={type}&", | |
642 | + new TypeReference<PageData<Edge>>() { | |
643 | + }, pageLink, type2); | |
644 | + loadedEdgesType2.addAll(pageData.getData()); | |
645 | + if (pageData.hasNext()) { | |
646 | + pageLink = pageLink.nextPageLink(); | |
647 | + } | |
648 | + } while (pageData.hasNext()); | |
649 | + | |
650 | + Collections.sort(edgesType2, idComparator); | |
651 | + Collections.sort(loadedEdgesType2, idComparator); | |
652 | + | |
653 | + Assert.assertEquals(edgesType2, loadedEdgesType2); | |
654 | + | |
655 | + for (Edge edge : loadedEdgesType1) { | |
656 | + doDelete("/api/customer/edge/" + edge.getId().getId().toString()) | |
657 | + .andExpect(status().isOk()); | |
658 | + } | |
659 | + | |
660 | + pageLink = new PageLink(4, 0, title1); | |
661 | + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/edges?type={type}&", | |
662 | + new TypeReference<PageData<Edge>>() { | |
663 | + }, pageLink, type1); | |
664 | + Assert.assertFalse(pageData.hasNext()); | |
665 | + Assert.assertEquals(0, pageData.getData().size()); | |
666 | + | |
667 | + for (Edge edge : loadedEdgesType2) { | |
668 | + doDelete("/api/customer/edge/" + edge.getId().getId().toString()) | |
669 | + .andExpect(status().isOk()); | |
670 | + } | |
671 | + | |
672 | + pageLink = new PageLink(4, 0, title2); | |
673 | + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/edges?type={type}&", | |
674 | + new TypeReference<PageData<Edge>>() { | |
675 | + }, pageLink, type2); | |
676 | + Assert.assertFalse(pageData.hasNext()); | |
677 | + Assert.assertEquals(0, pageData.getData().size()); | |
678 | + } | |
679 | +} | |
\ No newline at end of file | ... | ... |
application/src/test/java/org/thingsboard/server/controller/sql/EdgeControllerSqlTest.java
0 → 100644
1 | +/** | |
2 | + * Copyright © 2016-2020 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.controller.sql; | |
17 | + | |
18 | +import org.thingsboard.server.controller.BaseEdgeControllerTest; | |
19 | +import org.thingsboard.server.dao.service.DaoSqlTest; | |
20 | + | |
21 | +@DaoSqlTest | |
22 | +public class EdgeControllerSqlTest extends BaseEdgeControllerTest { | |
23 | +} | |
\ No newline at end of file | ... | ... |
... | ... | @@ -22,6 +22,7 @@ import org.thingsboard.server.common.data.asset.AssetInfo; |
22 | 22 | import org.thingsboard.server.common.data.asset.AssetSearchQuery; |
23 | 23 | import org.thingsboard.server.common.data.id.AssetId; |
24 | 24 | import org.thingsboard.server.common.data.id.CustomerId; |
25 | +import org.thingsboard.server.common.data.id.EdgeId; | |
25 | 26 | import org.thingsboard.server.common.data.id.TenantId; |
26 | 27 | import org.thingsboard.server.common.data.page.PageData; |
27 | 28 | import org.thingsboard.server.common.data.page.PageLink; |
... | ... | @@ -74,4 +75,12 @@ public interface AssetService { |
74 | 75 | ListenableFuture<List<Asset>> findAssetsByQuery(TenantId tenantId, AssetSearchQuery query); |
75 | 76 | |
76 | 77 | ListenableFuture<List<EntitySubtype>> findAssetTypesByTenantId(TenantId tenantId); |
78 | + | |
79 | + Asset assignAssetToEdge(TenantId tenantId, AssetId assetId, EdgeId edgeId); | |
80 | + | |
81 | + Asset unassignAssetFromEdge(TenantId tenantId, AssetId assetId); | |
82 | + | |
83 | + PageData<Asset> findAssetsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink); | |
84 | + | |
85 | + PageData<Asset> findAssetsByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, PageLink pageLink); | |
77 | 86 | } | ... | ... |
... | ... | @@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.Dashboard; |
20 | 20 | import org.thingsboard.server.common.data.DashboardInfo; |
21 | 21 | import org.thingsboard.server.common.data.id.CustomerId; |
22 | 22 | import org.thingsboard.server.common.data.id.DashboardId; |
23 | +import org.thingsboard.server.common.data.id.EdgeId; | |
23 | 24 | import org.thingsboard.server.common.data.id.TenantId; |
24 | 25 | import org.thingsboard.server.common.data.page.PageData; |
25 | 26 | import org.thingsboard.server.common.data.page.PageLink; |
... | ... | @@ -53,4 +54,13 @@ public interface DashboardService { |
53 | 54 | |
54 | 55 | void updateCustomerDashboards(TenantId tenantId, CustomerId customerId); |
55 | 56 | |
57 | + Dashboard assignDashboardToEdge(TenantId tenantId, DashboardId dashboardId, EdgeId edgeId); | |
58 | + | |
59 | + Dashboard unassignDashboardFromEdge(TenantId tenantId, DashboardId dashboardId, EdgeId edgeId); | |
60 | + | |
61 | + void unassignEdgeDashboards(TenantId tenantId, EdgeId edgeId); | |
62 | + | |
63 | + void updateEdgeDashboards(TenantId tenantId, EdgeId edgeId); | |
64 | + | |
65 | + PageData<DashboardInfo> findDashboardsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink); | |
56 | 66 | } | ... | ... |
... | ... | @@ -22,6 +22,7 @@ import org.thingsboard.server.common.data.EntitySubtype; |
22 | 22 | import org.thingsboard.server.common.data.device.DeviceSearchQuery; |
23 | 23 | import org.thingsboard.server.common.data.id.CustomerId; |
24 | 24 | import org.thingsboard.server.common.data.id.DeviceId; |
25 | +import org.thingsboard.server.common.data.id.EdgeId; | |
25 | 26 | import org.thingsboard.server.common.data.id.TenantId; |
26 | 27 | import org.thingsboard.server.common.data.page.PageData; |
27 | 28 | import org.thingsboard.server.common.data.page.PageLink; |
... | ... | @@ -76,4 +77,11 @@ public interface DeviceService { |
76 | 77 | |
77 | 78 | ListenableFuture<List<EntitySubtype>> findDeviceTypesByTenantId(TenantId tenantId); |
78 | 79 | |
80 | + Device assignDeviceToEdge(TenantId tenantId, DeviceId deviceId, EdgeId edgeId); | |
81 | + | |
82 | + Device unassignDeviceFromEdge(TenantId tenantId, DeviceId deviceId); | |
83 | + | |
84 | + PageData<Device> findDevicesByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink); | |
85 | + | |
86 | + PageData<Device> findDevicesByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, PageLink pageLink); | |
79 | 87 | } | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2020 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.dao.edge; | |
17 | + | |
18 | +import com.google.common.util.concurrent.FutureCallback; | |
19 | +import com.google.common.util.concurrent.ListenableFuture; | |
20 | +import org.thingsboard.server.common.data.EntitySubtype; | |
21 | +import org.thingsboard.server.common.data.Event; | |
22 | +import org.thingsboard.server.common.data.edge.Edge; | |
23 | +import org.thingsboard.server.common.data.edge.EdgeSearchQuery; | |
24 | +import org.thingsboard.server.common.data.id.CustomerId; | |
25 | +import org.thingsboard.server.common.data.id.EdgeId; | |
26 | +import org.thingsboard.server.common.data.id.RuleChainId; | |
27 | +import org.thingsboard.server.common.data.id.TenantId; | |
28 | +import org.thingsboard.server.common.data.page.PageData; | |
29 | +import org.thingsboard.server.common.data.page.PageLink; | |
30 | +import org.thingsboard.server.common.data.page.TimePageLink; | |
31 | +import org.thingsboard.server.common.msg.TbMsg; | |
32 | + | |
33 | +import java.io.IOException; | |
34 | +import java.util.List; | |
35 | +import java.util.Optional; | |
36 | + | |
37 | +public interface EdgeService { | |
38 | + | |
39 | + Edge findEdgeById(TenantId tenantId, EdgeId edgeId); | |
40 | + | |
41 | + ListenableFuture<Edge> findEdgeByIdAsync(TenantId tenantId, EdgeId edgeId); | |
42 | + | |
43 | + Edge findEdgeByTenantIdAndName(TenantId tenantId, String name); | |
44 | + | |
45 | + Optional<Edge> findEdgeByRoutingKey(TenantId tenantId, String routingKey); | |
46 | + | |
47 | + Edge saveEdge(Edge edge); | |
48 | + | |
49 | + Edge assignEdgeToCustomer(TenantId tenantId, EdgeId edgeId, CustomerId customerId); | |
50 | + | |
51 | + Edge unassignEdgeFromCustomer(TenantId tenantId, EdgeId edgeId); | |
52 | + | |
53 | + void deleteEdge(TenantId tenantId, EdgeId edgeId); | |
54 | + | |
55 | + PageData<Edge> findEdgesByTenantId(TenantId tenantId, PageLink pageLink); | |
56 | + | |
57 | + PageData<Edge> findEdgesByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink); | |
58 | + | |
59 | + ListenableFuture<List<Edge>> findEdgesByTenantIdAndIdsAsync(TenantId tenantId, List<EdgeId> edgeIds); | |
60 | + | |
61 | + void deleteEdgesByTenantId(TenantId tenantId); | |
62 | + | |
63 | + PageData<Edge> findEdgesByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, PageLink pageLink); | |
64 | + | |
65 | + PageData<Edge> findEdgesByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, String type, PageLink pageLink); | |
66 | + | |
67 | + ListenableFuture<List<Edge>> findEdgesByTenantIdCustomerIdAndIdsAsync(TenantId tenantId, CustomerId customerId, List<EdgeId> edgeIds); | |
68 | + | |
69 | + void unassignCustomerEdges(TenantId tenantId, CustomerId customerId); | |
70 | + | |
71 | + ListenableFuture<List<Edge>> findEdgesByQuery(TenantId tenantId, EdgeSearchQuery query); | |
72 | + | |
73 | + ListenableFuture<List<EntitySubtype>> findEdgeTypesByTenantId(TenantId tenantId); | |
74 | + | |
75 | + void pushEventToEdge(TenantId tenantId, TbMsg tbMsg, FutureCallback<Void> callback); | |
76 | + | |
77 | + PageData<Event> findQueueEvents(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink); | |
78 | + | |
79 | + Edge setRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) throws IOException; | |
80 | +} | ... | ... |
... | ... | @@ -19,9 +19,9 @@ import com.google.common.util.concurrent.ListenableFuture; |
19 | 19 | import org.thingsboard.server.common.data.EntitySubtype; |
20 | 20 | import org.thingsboard.server.common.data.EntityView; |
21 | 21 | import org.thingsboard.server.common.data.EntityViewInfo; |
22 | -import org.thingsboard.server.common.data.Tenant; | |
23 | 22 | import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery; |
24 | 23 | import org.thingsboard.server.common.data.id.CustomerId; |
24 | +import org.thingsboard.server.common.data.id.EdgeId; | |
25 | 25 | import org.thingsboard.server.common.data.id.EntityId; |
26 | 26 | import org.thingsboard.server.common.data.id.EntityViewId; |
27 | 27 | import org.thingsboard.server.common.data.id.TenantId; |
... | ... | @@ -76,4 +76,12 @@ public interface EntityViewService { |
76 | 76 | void deleteEntityViewsByTenantId(TenantId tenantId); |
77 | 77 | |
78 | 78 | ListenableFuture<List<EntitySubtype>> findEntityViewTypesByTenantId(TenantId tenantId); |
79 | + | |
80 | + EntityView assignEntityViewToEdge(TenantId tenantId, EntityViewId entityViewId, EdgeId edgeId); | |
81 | + | |
82 | + EntityView unassignEntityViewFromEdge(TenantId tenantId, EntityViewId entityViewId); | |
83 | + | |
84 | + PageData<EntityView> findEntityViewsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink); | |
85 | + | |
86 | + PageData<EntityView> findEntityViewsByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, PageLink pageLink); | |
79 | 87 | } | ... | ... |
... | ... | @@ -16,14 +16,17 @@ |
16 | 16 | package org.thingsboard.server.dao.rule; |
17 | 17 | |
18 | 18 | import com.google.common.util.concurrent.ListenableFuture; |
19 | +import org.thingsboard.server.common.data.id.EdgeId; | |
19 | 20 | import org.thingsboard.server.common.data.id.RuleChainId; |
20 | 21 | import org.thingsboard.server.common.data.id.RuleNodeId; |
21 | 22 | import org.thingsboard.server.common.data.id.TenantId; |
22 | 23 | import org.thingsboard.server.common.data.page.PageData; |
23 | 24 | import org.thingsboard.server.common.data.page.PageLink; |
25 | +import org.thingsboard.server.common.data.page.TimePageLink; | |
24 | 26 | import org.thingsboard.server.common.data.relation.EntityRelation; |
25 | 27 | import org.thingsboard.server.common.data.rule.RuleChain; |
26 | 28 | import org.thingsboard.server.common.data.rule.RuleChainMetaData; |
29 | +import org.thingsboard.server.common.data.rule.RuleChainType; | |
27 | 30 | import org.thingsboard.server.common.data.rule.RuleNode; |
28 | 31 | |
29 | 32 | import java.util.List; |
... | ... | @@ -59,8 +62,19 @@ public interface RuleChainService { |
59 | 62 | |
60 | 63 | PageData<RuleChain> findTenantRuleChains(TenantId tenantId, PageLink pageLink); |
61 | 64 | |
65 | + PageData<RuleChain> findTenantRuleChainsByType(TenantId tenantId, RuleChainType type, PageLink pageLink); | |
66 | + | |
62 | 67 | void deleteRuleChainById(TenantId tenantId, RuleChainId ruleChainId); |
63 | 68 | |
64 | 69 | void deleteRuleChainsByTenantId(TenantId tenantId); |
65 | 70 | |
71 | + RuleChain assignRuleChainToEdge(TenantId tenantId, RuleChainId ruleChainId, EdgeId edgeId); | |
72 | + | |
73 | + RuleChain unassignRuleChainFromEdge(TenantId tenantId, RuleChainId ruleChainId, EdgeId edgeId); | |
74 | + | |
75 | + void unassignEdgeRuleChains(TenantId tenantId, EdgeId edgeId); | |
76 | + | |
77 | + void updateEdgeRuleChains(TenantId tenantId, EdgeId edgeId); | |
78 | + | |
79 | + PageData<RuleChain> findRuleChainsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink); | |
66 | 80 | } | ... | ... |
... | ... | @@ -22,6 +22,7 @@ public class CacheConstants { |
22 | 22 | public static final String SESSIONS_CACHE = "sessions"; |
23 | 23 | public static final String ASSET_CACHE = "assets"; |
24 | 24 | public static final String ENTITY_VIEW_CACHE = "entityViews"; |
25 | + public static final String EDGE_CACHE = "edges"; | |
25 | 26 | public static final String CLAIM_DEVICES_CACHE = "claimDevices"; |
26 | 27 | public static final String SECURITY_SETTINGS_CACHE = "securitySettings"; |
27 | 28 | } | ... | ... |
... | ... | @@ -16,8 +16,12 @@ |
16 | 16 | package org.thingsboard.server.common.data; |
17 | 17 | |
18 | 18 | import com.fasterxml.jackson.annotation.JsonProperty; |
19 | +import lombok.Getter; | |
20 | +import lombok.Setter; | |
21 | +import org.thingsboard.server.common.data.edge.Edge; | |
19 | 22 | import org.thingsboard.server.common.data.id.CustomerId; |
20 | 23 | import org.thingsboard.server.common.data.id.DashboardId; |
24 | +import org.thingsboard.server.common.data.id.EdgeId; | |
21 | 25 | import org.thingsboard.server.common.data.id.TenantId; |
22 | 26 | |
23 | 27 | import java.util.*; |
... | ... | @@ -28,6 +32,9 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa |
28 | 32 | private String title; |
29 | 33 | private Set<ShortCustomerInfo> assignedCustomers; |
30 | 34 | |
35 | + @Getter @Setter | |
36 | + private Set<ShortEdgeInfo> assignedEdges; | |
37 | + | |
31 | 38 | public DashboardInfo() { |
32 | 39 | super(); |
33 | 40 | } |
... | ... | @@ -116,6 +123,55 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa |
116 | 123 | } |
117 | 124 | } |
118 | 125 | |
126 | + public boolean isAssignedToEdge(EdgeId edgeId) { | |
127 | + return this.assignedEdges != null && this.assignedEdges.contains(new ShortEdgeInfo(edgeId, null, null)); | |
128 | + } | |
129 | + | |
130 | + public ShortEdgeInfo getAssignedEdgeInfo(EdgeId edgeId) { | |
131 | + if (this.assignedEdges != null) { | |
132 | + for (ShortEdgeInfo edgeInfo : this.assignedEdges) { | |
133 | + if (edgeInfo.getEdgeId().equals(edgeId)) { | |
134 | + return edgeInfo; | |
135 | + } | |
136 | + } | |
137 | + } | |
138 | + return null; | |
139 | + } | |
140 | + | |
141 | + public boolean addAssignedEdge(Edge edge) { | |
142 | + ShortEdgeInfo edgeInfo = edge.toShortEdgeInfo(); | |
143 | + if (this.assignedEdges != null && this.assignedEdges.contains(edgeInfo)) { | |
144 | + return false; | |
145 | + } else { | |
146 | + if (this.assignedEdges == null) { | |
147 | + this.assignedEdges = new HashSet<>(); | |
148 | + } | |
149 | + this.assignedEdges.add(edgeInfo); | |
150 | + return true; | |
151 | + } | |
152 | + } | |
153 | + | |
154 | + public boolean updateAssignedEdge(Edge edge) { | |
155 | + ShortEdgeInfo edgeInfo = edge.toShortEdgeInfo(); | |
156 | + if (this.assignedEdges != null && this.assignedEdges.contains(edgeInfo)) { | |
157 | + this.assignedEdges.remove(edgeInfo); | |
158 | + this.assignedEdges.add(edgeInfo); | |
159 | + return true; | |
160 | + } else { | |
161 | + return false; | |
162 | + } | |
163 | + } | |
164 | + | |
165 | + public boolean removeAssignedEdge(Edge edge) { | |
166 | + ShortEdgeInfo edgeInfo = edge.toShortEdgeInfo(); | |
167 | + if (this.assignedEdges != null && this.assignedEdges.contains(edgeInfo)) { | |
168 | + this.assignedEdges.remove(edgeInfo); | |
169 | + return true; | |
170 | + } else { | |
171 | + return false; | |
172 | + } | |
173 | + } | |
174 | + | |
119 | 175 | @Override |
120 | 176 | @JsonProperty(access = JsonProperty.Access.READ_ONLY) |
121 | 177 | public String getName() { | ... | ... |
... | ... | @@ -57,6 +57,8 @@ public class DataConstants { |
57 | 57 | public static final String ATTRIBUTES_DELETED = "ATTRIBUTES_DELETED"; |
58 | 58 | public static final String ALARM_ACK = "ALARM_ACK"; |
59 | 59 | public static final String ALARM_CLEAR = "ALARM_CLEAR"; |
60 | + public static final String ENTITY_ASSIGNED_TO_EDGE = "ENTITY_ASSIGNED_TO_EDGE"; | |
61 | + public static final String ENTITY_UNASSIGNED_FROM_EDGE = "ENTITY_UNASSIGNED_FROM_EDGE"; | |
60 | 62 | |
61 | 63 | public static final String RPC_CALL_FROM_SERVER_TO_DEVICE = "RPC_CALL_FROM_SERVER_TO_DEVICE"; |
62 | 64 | |
... | ... | @@ -64,4 +66,5 @@ public class DataConstants { |
64 | 66 | public static final String SECRET_KEY_FIELD_NAME = "secretKey"; |
65 | 67 | public static final String DURATION_MS_FIELD_NAME = "durationMs"; |
66 | 68 | |
69 | + public static final String EDGE_QUEUE_EVENT_TYPE = "EDGE_QUEUE"; | |
67 | 70 | } | ... | ... |
... | ... | @@ -18,6 +18,7 @@ package org.thingsboard.server.common.data; |
18 | 18 | import lombok.EqualsAndHashCode; |
19 | 19 | import org.thingsboard.server.common.data.id.CustomerId; |
20 | 20 | import org.thingsboard.server.common.data.id.DeviceId; |
21 | +import org.thingsboard.server.common.data.id.EdgeId; | |
21 | 22 | import org.thingsboard.server.common.data.id.TenantId; |
22 | 23 | |
23 | 24 | @EqualsAndHashCode(callSuper = true) |
... | ... | @@ -27,6 +28,7 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen |
27 | 28 | |
28 | 29 | private TenantId tenantId; |
29 | 30 | private CustomerId customerId; |
31 | + private EdgeId edgeId; | |
30 | 32 | private String name; |
31 | 33 | private String type; |
32 | 34 | private String label; |
... | ... | @@ -46,6 +48,7 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen |
46 | 48 | this.name = device.getName(); |
47 | 49 | this.type = device.getType(); |
48 | 50 | this.label = device.getLabel(); |
51 | + this.edgeId = device.getEdgeId(); | |
49 | 52 | } |
50 | 53 | |
51 | 54 | public TenantId getTenantId() { |
... | ... | @@ -64,6 +67,14 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen |
64 | 67 | this.customerId = customerId; |
65 | 68 | } |
66 | 69 | |
70 | + public EdgeId getEdgeId() { | |
71 | + return edgeId; | |
72 | + } | |
73 | + | |
74 | + public void setEdgeId(EdgeId edgeId) { | |
75 | + this.edgeId = edgeId; | |
76 | + } | |
77 | + | |
67 | 78 | @Override |
68 | 79 | public String getName() { |
69 | 80 | return name; |
... | ... | @@ -101,6 +112,8 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen |
101 | 112 | builder.append(tenantId); |
102 | 113 | builder.append(", customerId="); |
103 | 114 | builder.append(customerId); |
115 | + builder.append(", edgeId="); | |
116 | + builder.append(edgeId); | |
104 | 117 | builder.append(", name="); |
105 | 118 | builder.append(name); |
106 | 119 | builder.append(", type="); | ... | ... |
... | ... | @@ -19,5 +19,5 @@ package org.thingsboard.server.common.data; |
19 | 19 | * @author Andrew Shvayka |
20 | 20 | */ |
21 | 21 | public enum EntityType { |
22 | - TENANT, CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, ALARM, RULE_CHAIN, RULE_NODE, ENTITY_VIEW, WIDGETS_BUNDLE, WIDGET_TYPE | |
22 | + TENANT, CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, ALARM, RULE_CHAIN, RULE_NODE, ENTITY_VIEW, WIDGETS_BUNDLE, WIDGET_TYPE, EDGE | |
23 | 23 | } | ... | ... |
... | ... | @@ -19,6 +19,7 @@ import lombok.AllArgsConstructor; |
19 | 19 | import lombok.Data; |
20 | 20 | import lombok.EqualsAndHashCode; |
21 | 21 | import org.thingsboard.server.common.data.id.CustomerId; |
22 | +import org.thingsboard.server.common.data.id.EdgeId; | |
22 | 23 | import org.thingsboard.server.common.data.id.EntityId; |
23 | 24 | import org.thingsboard.server.common.data.id.EntityViewId; |
24 | 25 | import org.thingsboard.server.common.data.id.TenantId; |
... | ... | @@ -39,6 +40,7 @@ public class EntityView extends SearchTextBasedWithAdditionalInfo<EntityViewId> |
39 | 40 | private EntityId entityId; |
40 | 41 | private TenantId tenantId; |
41 | 42 | private CustomerId customerId; |
43 | + private EdgeId edgeId; | |
42 | 44 | private String name; |
43 | 45 | private String type; |
44 | 46 | private TelemetryEntityView keys; | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2020 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.common.data; | |
17 | + | |
18 | +import lombok.AllArgsConstructor; | |
19 | +import lombok.Getter; | |
20 | +import lombok.Setter; | |
21 | +import org.thingsboard.server.common.data.id.EdgeId; | |
22 | +import org.thingsboard.server.common.data.id.RuleChainId; | |
23 | + | |
24 | +@AllArgsConstructor | |
25 | +public class ShortEdgeInfo { | |
26 | + | |
27 | + @Getter @Setter | |
28 | + private EdgeId edgeId; | |
29 | + | |
30 | + @Getter @Setter | |
31 | + private String title; | |
32 | + | |
33 | + @Getter @Setter | |
34 | + private RuleChainId rootRuleChainId; | |
35 | + | |
36 | + @Override | |
37 | + public boolean equals(Object o) { | |
38 | + if (this == o) return true; | |
39 | + if (o == null || getClass() != o.getClass()) return false; | |
40 | + | |
41 | + ShortEdgeInfo that = (ShortEdgeInfo) o; | |
42 | + | |
43 | + return edgeId.equals(that.edgeId); | |
44 | + } | |
45 | + | |
46 | + @Override | |
47 | + public int hashCode() { | |
48 | + return edgeId.hashCode(); | |
49 | + } | |
50 | +} | |
\ No newline at end of file | ... | ... |
... | ... | @@ -20,6 +20,7 @@ import lombok.EqualsAndHashCode; |
20 | 20 | import org.thingsboard.server.common.data.*; |
21 | 21 | import org.thingsboard.server.common.data.id.AssetId; |
22 | 22 | import org.thingsboard.server.common.data.id.CustomerId; |
23 | +import org.thingsboard.server.common.data.id.EdgeId; | |
23 | 24 | import org.thingsboard.server.common.data.id.TenantId; |
24 | 25 | |
25 | 26 | @EqualsAndHashCode(callSuper = true) |
... | ... | @@ -29,6 +30,7 @@ public class Asset extends SearchTextBasedWithAdditionalInfo<AssetId> implements |
29 | 30 | |
30 | 31 | private TenantId tenantId; |
31 | 32 | private CustomerId customerId; |
33 | + private EdgeId edgeId; | |
32 | 34 | private String name; |
33 | 35 | private String type; |
34 | 36 | private String label; |
... | ... | @@ -45,6 +47,7 @@ public class Asset extends SearchTextBasedWithAdditionalInfo<AssetId> implements |
45 | 47 | super(asset); |
46 | 48 | this.tenantId = asset.getTenantId(); |
47 | 49 | this.customerId = asset.getCustomerId(); |
50 | + this.edgeId = asset.getEdgeId(); | |
48 | 51 | this.name = asset.getName(); |
49 | 52 | this.type = asset.getType(); |
50 | 53 | this.label = asset.getLabel(); |
... | ... | @@ -66,6 +69,14 @@ public class Asset extends SearchTextBasedWithAdditionalInfo<AssetId> implements |
66 | 69 | this.customerId = customerId; |
67 | 70 | } |
68 | 71 | |
72 | + public EdgeId getEdgeId() { | |
73 | + return edgeId; | |
74 | + } | |
75 | + | |
76 | + public void setEdgeId(EdgeId edgeId) { | |
77 | + this.edgeId = edgeId; | |
78 | + } | |
79 | + | |
69 | 80 | @Override |
70 | 81 | public String getName() { |
71 | 82 | return name; |
... | ... | @@ -103,6 +114,8 @@ public class Asset extends SearchTextBasedWithAdditionalInfo<AssetId> implements |
103 | 114 | builder.append(tenantId); |
104 | 115 | builder.append(", customerId="); |
105 | 116 | builder.append(customerId); |
117 | + builder.append(", edgeId="); | |
118 | + builder.append(edgeId); | |
106 | 119 | builder.append(", name="); |
107 | 120 | builder.append(name); |
108 | 121 | builder.append(", type="); | ... | ... |
... | ... | @@ -40,7 +40,9 @@ public enum ActionType { |
40 | 40 | ALARM_CLEAR(false), |
41 | 41 | LOGIN(false), |
42 | 42 | LOGOUT(false), |
43 | - LOCKOUT(false); | |
43 | + LOCKOUT(false), | |
44 | + ASSIGNED_TO_EDGE(false), // log edge name | |
45 | + UNASSIGNED_FROM_EDGE(false); // log edge name | |
44 | 46 | |
45 | 47 | private final boolean isRead; |
46 | 48 | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2020 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.common.data.edge; | |
17 | + | |
18 | +import com.fasterxml.jackson.annotation.JsonIgnore; | |
19 | +import com.fasterxml.jackson.databind.JsonNode; | |
20 | +import lombok.EqualsAndHashCode; | |
21 | +import lombok.Getter; | |
22 | +import lombok.Setter; | |
23 | +import lombok.ToString; | |
24 | +import org.thingsboard.server.common.data.HasCustomerId; | |
25 | +import org.thingsboard.server.common.data.HasName; | |
26 | +import org.thingsboard.server.common.data.HasTenantId; | |
27 | +import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; | |
28 | +import org.thingsboard.server.common.data.ShortCustomerInfo; | |
29 | +import org.thingsboard.server.common.data.ShortEdgeInfo; | |
30 | +import org.thingsboard.server.common.data.id.CustomerId; | |
31 | +import org.thingsboard.server.common.data.id.EdgeId; | |
32 | +import org.thingsboard.server.common.data.id.RuleChainId; | |
33 | +import org.thingsboard.server.common.data.id.TenantId; | |
34 | + | |
35 | +@EqualsAndHashCode(callSuper = true) | |
36 | +@ToString | |
37 | +@Getter | |
38 | +@Setter | |
39 | +public class Edge extends SearchTextBasedWithAdditionalInfo<EdgeId> implements HasName, HasTenantId, HasCustomerId { | |
40 | + | |
41 | + private static final long serialVersionUID = 4934987555236873728L; | |
42 | + | |
43 | + private TenantId tenantId; | |
44 | + private CustomerId customerId; | |
45 | + private RuleChainId rootRuleChainId; | |
46 | + private String name; | |
47 | + private String type; | |
48 | + private String label; | |
49 | + private String routingKey; | |
50 | + private String secret; | |
51 | + private transient JsonNode configuration; | |
52 | + | |
53 | + public Edge() { | |
54 | + super(); | |
55 | + } | |
56 | + | |
57 | + public Edge(EdgeId id) { | |
58 | + super(id); | |
59 | + } | |
60 | + | |
61 | + public Edge(Edge edge) { | |
62 | + super(edge); | |
63 | + this.tenantId = edge.getTenantId(); | |
64 | + this.customerId = edge.getCustomerId(); | |
65 | + this.rootRuleChainId = edge.getRootRuleChainId(); | |
66 | + this.type = edge.getType(); | |
67 | + this.name = edge.getName(); | |
68 | + this.routingKey = edge.getRoutingKey(); | |
69 | + this.secret = edge.getSecret(); | |
70 | + this.configuration = edge.getConfiguration(); | |
71 | + } | |
72 | + | |
73 | + @JsonIgnore | |
74 | + public ShortEdgeInfo toShortEdgeInfo() { | |
75 | + return new ShortEdgeInfo(id, name, rootRuleChainId); | |
76 | + } | |
77 | + | |
78 | + @Override | |
79 | + public String getSearchText() { | |
80 | + return getName(); | |
81 | + } | |
82 | +} | |
\ No newline at end of file | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeQueueEntityType.java
0 → 100644
1 | +/** | |
2 | + * Copyright © 2016-2020 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.common.data.edge; | |
17 | + | |
18 | +public enum EdgeQueueEntityType { | |
19 | + DASHBOARD, ASSET, DEVICE, ENTITY_VIEW, ALARM, RULE_CHAIN, RULE_CHAIN_METADATA, EDGE, USER, CUSTOMER | |
20 | +} | |
\ No newline at end of file | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2020 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.common.data.edge; | |
17 | + | |
18 | +import lombok.Data; | |
19 | + | |
20 | +@Data | |
21 | +public class EdgeQueueEntry { | |
22 | + private String type; | |
23 | + private EdgeQueueEntityType entityType; | |
24 | + private String data; | |
25 | +} | |
\ No newline at end of file | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2020 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.common.data.edge; | |
17 | + | |
18 | +import lombok.Data; | |
19 | +import org.thingsboard.server.common.data.EntityType; | |
20 | +import org.thingsboard.server.common.data.relation.EntityRelation; | |
21 | +import org.thingsboard.server.common.data.relation.EntityRelationsQuery; | |
22 | +import org.thingsboard.server.common.data.relation.EntityTypeFilter; | |
23 | +import org.thingsboard.server.common.data.relation.RelationsSearchParameters; | |
24 | + | |
25 | +import java.util.Collections; | |
26 | +import java.util.List; | |
27 | + | |
28 | +@Data | |
29 | +public class EdgeSearchQuery { | |
30 | + | |
31 | + private RelationsSearchParameters parameters; | |
32 | + private String relationType; | |
33 | + private List<String> edgeTypes; | |
34 | + | |
35 | + public EntityRelationsQuery toEntitySearchQuery() { | |
36 | + EntityRelationsQuery query = new EntityRelationsQuery(); | |
37 | + query.setParameters(parameters); | |
38 | + query.setFilters( | |
39 | + Collections.singletonList(new EntityTypeFilter(relationType == null ? EntityRelation.CONTAINS_TYPE : relationType, | |
40 | + Collections.singletonList(EntityType.EDGE)))); | |
41 | + return query; | |
42 | + } | |
43 | +} | |
\ No newline at end of file | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2020 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.common.data.id; | |
17 | + | |
18 | +import com.fasterxml.jackson.annotation.JsonCreator; | |
19 | +import com.fasterxml.jackson.annotation.JsonIgnore; | |
20 | +import com.fasterxml.jackson.annotation.JsonProperty; | |
21 | +import org.thingsboard.server.common.data.EntityType; | |
22 | + | |
23 | +import java.util.UUID; | |
24 | + | |
25 | +public class EdgeId extends UUIDBased implements EntityId { | |
26 | + | |
27 | + private static final long serialVersionUID = 1L; | |
28 | + | |
29 | + @JsonCreator | |
30 | + public EdgeId(@JsonProperty("id") UUID id) { | |
31 | + super(id); | |
32 | + } | |
33 | + | |
34 | + public static EdgeId fromString(String integrationId) { | |
35 | + return new EdgeId(UUID.fromString(integrationId)); | |
36 | + } | |
37 | + | |
38 | + @JsonIgnore | |
39 | + @Override | |
40 | + public EntityType getEntityType() { | |
41 | + return EntityType.EDGE; | |
42 | + } | |
43 | +} | |
\ No newline at end of file | ... | ... |
... | ... | @@ -63,6 +63,8 @@ public class EntityIdFactory { |
63 | 63 | return new WidgetsBundleId(uuid); |
64 | 64 | case WIDGET_TYPE: |
65 | 65 | return new WidgetTypeId(uuid); |
66 | + case EDGE: | |
67 | + return new EdgeId(uuid); | |
66 | 68 | } |
67 | 69 | throw new IllegalArgumentException("EntityType " + type + " is not supported!"); |
68 | 70 | } | ... | ... |
... | ... | @@ -32,6 +32,7 @@ public class EntityRelation implements Serializable { |
32 | 32 | |
33 | 33 | private static final long serialVersionUID = 2807343040519543363L; |
34 | 34 | |
35 | + public static final String EDGE_TYPE = "ManagedByEdge"; | |
35 | 36 | public static final String CONTAINS_TYPE = "Contains"; |
36 | 37 | public static final String MANAGES_TYPE = "Manages"; |
37 | 38 | ... | ... |
... | ... | @@ -23,10 +23,16 @@ import lombok.extern.slf4j.Slf4j; |
23 | 23 | import org.thingsboard.server.common.data.HasName; |
24 | 24 | import org.thingsboard.server.common.data.HasTenantId; |
25 | 25 | import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; |
26 | +import org.thingsboard.server.common.data.ShortEdgeInfo; | |
27 | +import org.thingsboard.server.common.data.edge.Edge; | |
28 | +import org.thingsboard.server.common.data.id.EdgeId; | |
26 | 29 | import org.thingsboard.server.common.data.id.RuleChainId; |
27 | 30 | import org.thingsboard.server.common.data.id.RuleNodeId; |
28 | 31 | import org.thingsboard.server.common.data.id.TenantId; |
29 | 32 | |
33 | +import java.util.HashSet; | |
34 | +import java.util.Set; | |
35 | + | |
30 | 36 | @Data |
31 | 37 | @EqualsAndHashCode(callSuper = true) |
32 | 38 | @Slf4j |
... | ... | @@ -36,13 +42,16 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo<RuleChainId> im |
36 | 42 | |
37 | 43 | private TenantId tenantId; |
38 | 44 | private String name; |
45 | + private RuleChainType type; | |
39 | 46 | private RuleNodeId firstRuleNodeId; |
40 | 47 | private boolean root; |
41 | 48 | private boolean debugMode; |
42 | 49 | private transient JsonNode configuration; |
50 | + private Set<ShortEdgeInfo> assignedEdges; | |
43 | 51 | @JsonIgnore |
44 | 52 | private byte[] configurationBytes; |
45 | 53 | |
54 | + | |
46 | 55 | public RuleChain() { |
47 | 56 | super(); |
48 | 57 | } |
... | ... | @@ -55,8 +64,10 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo<RuleChainId> im |
55 | 64 | super(ruleChain); |
56 | 65 | this.tenantId = ruleChain.getTenantId(); |
57 | 66 | this.name = ruleChain.getName(); |
67 | + this.type = ruleChain.getType(); | |
58 | 68 | this.firstRuleNodeId = ruleChain.getFirstRuleNodeId(); |
59 | 69 | this.root = ruleChain.isRoot(); |
70 | + this.assignedEdges = ruleChain.getAssignedEdges(); | |
60 | 71 | this.setConfiguration(ruleChain.getConfiguration()); |
61 | 72 | } |
62 | 73 | |
... | ... | @@ -78,4 +89,52 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo<RuleChainId> im |
78 | 89 | setJson(data, json -> this.configuration = json, bytes -> this.configurationBytes = bytes); |
79 | 90 | } |
80 | 91 | |
92 | + public boolean isAssignedToEdge(EdgeId edgeId) { | |
93 | + return this.assignedEdges != null && this.assignedEdges.contains(new ShortEdgeInfo(edgeId, null, null)); | |
94 | + } | |
95 | + | |
96 | + public ShortEdgeInfo getAssignedEdgeInfo(EdgeId edgeId) { | |
97 | + if (this.assignedEdges != null) { | |
98 | + for (ShortEdgeInfo edgeInfo : this.assignedEdges) { | |
99 | + if (edgeInfo.getEdgeId().equals(edgeId)) { | |
100 | + return edgeInfo; | |
101 | + } | |
102 | + } | |
103 | + } | |
104 | + return null; | |
105 | + } | |
106 | + | |
107 | + public boolean addAssignedEdge(Edge edge) { | |
108 | + ShortEdgeInfo edgeInfo = edge.toShortEdgeInfo(); | |
109 | + if (this.assignedEdges != null && this.assignedEdges.contains(edgeInfo)) { | |
110 | + return false; | |
111 | + } else { | |
112 | + if (this.assignedEdges == null) { | |
113 | + this.assignedEdges = new HashSet<>(); | |
114 | + } | |
115 | + this.assignedEdges.add(edgeInfo); | |
116 | + return true; | |
117 | + } | |
118 | + } | |
119 | + | |
120 | + public boolean updateAssignedEdge(Edge edge) { | |
121 | + ShortEdgeInfo edgeInfo = edge.toShortEdgeInfo(); | |
122 | + if (this.assignedEdges != null && this.assignedEdges.contains(edgeInfo)) { | |
123 | + this.assignedEdges.remove(edgeInfo); | |
124 | + this.assignedEdges.add(edgeInfo); | |
125 | + return true; | |
126 | + } else { | |
127 | + return false; | |
128 | + } | |
129 | + } | |
130 | + | |
131 | + public boolean removeAssignedEdge(Edge edge) { | |
132 | + ShortEdgeInfo edgeInfo = edge.toShortEdgeInfo(); | |
133 | + if (this.assignedEdges != null && this.assignedEdges.contains(edgeInfo)) { | |
134 | + this.assignedEdges.remove(edgeInfo); | |
135 | + return true; | |
136 | + } else { | |
137 | + return false; | |
138 | + } | |
139 | + } | |
81 | 140 | } | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2020 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.common.data.rule; | |
17 | + | |
18 | +public enum RuleChainType { | |
19 | + SYSTEM, EDGE | |
20 | +} | |
\ No newline at end of file | ... | ... |
common/edge-api/pom.xml
0 → 100644
1 | +<!-- | |
2 | + | |
3 | + Copyright © 2016-2020 The Thingsboard Authors | |
4 | + | |
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | + you may not use this file except in compliance with the License. | |
7 | + You may obtain a copy of the License at | |
8 | + | |
9 | + http://www.apache.org/licenses/LICENSE-2.0 | |
10 | + | |
11 | + Unless required by applicable law or agreed to in writing, software | |
12 | + distributed under the License is distributed on an "AS IS" BASIS, | |
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | + See the License for the specific language governing permissions and | |
15 | + limitations under the License. | |
16 | + | |
17 | +--> | |
18 | +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
19 | + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |
20 | + <modelVersion>4.0.0</modelVersion> | |
21 | + <parent> | |
22 | + <groupId>org.thingsboard</groupId> | |
23 | + <version>3.0.0-SNAPSHOT</version> | |
24 | + <artifactId>common</artifactId> | |
25 | + </parent> | |
26 | + <groupId>org.thingsboard.common</groupId> | |
27 | + <artifactId>edge-api</artifactId> | |
28 | + <packaging>jar</packaging> | |
29 | + | |
30 | + <name>Thingsboard Server Remote Edge wrapper</name> | |
31 | + <url>https://thingsboard.io</url> | |
32 | + | |
33 | + <properties> | |
34 | + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |
35 | + <main.dir>${basedir}/../..</main.dir> | |
36 | + </properties> | |
37 | + | |
38 | + <dependencies> | |
39 | + <dependency> | |
40 | + <groupId>org.thingsboard.common</groupId> | |
41 | + <artifactId>data</artifactId> | |
42 | + </dependency> | |
43 | + <dependency> | |
44 | + <groupId>org.thingsboard.common</groupId> | |
45 | + <artifactId>message</artifactId> | |
46 | + </dependency> | |
47 | + <dependency> | |
48 | + <groupId>com.google.code.gson</groupId> | |
49 | + <artifactId>gson</artifactId> | |
50 | + </dependency> | |
51 | + <dependency> | |
52 | + <groupId>org.slf4j</groupId> | |
53 | + <artifactId>slf4j-api</artifactId> | |
54 | + </dependency> | |
55 | + <dependency> | |
56 | + <groupId>org.slf4j</groupId> | |
57 | + <artifactId>log4j-over-slf4j</artifactId> | |
58 | + </dependency> | |
59 | + <dependency> | |
60 | + <groupId>ch.qos.logback</groupId> | |
61 | + <artifactId>logback-core</artifactId> | |
62 | + </dependency> | |
63 | + <dependency> | |
64 | + <groupId>ch.qos.logback</groupId> | |
65 | + <artifactId>logback-classic</artifactId> | |
66 | + </dependency> | |
67 | + <dependency> | |
68 | + <groupId>org.springframework</groupId> | |
69 | + <artifactId>spring-context</artifactId> | |
70 | + </dependency> | |
71 | + <dependency> | |
72 | + <groupId>org.springframework.boot</groupId> | |
73 | + <artifactId>spring-boot-starter-web</artifactId> | |
74 | + </dependency> | |
75 | + <dependency> | |
76 | + <groupId>io.netty</groupId> | |
77 | + <artifactId>netty-all</artifactId> | |
78 | + <scope>provided</scope> | |
79 | + </dependency> | |
80 | + <dependency> | |
81 | + <groupId>com.google.guava</groupId> | |
82 | + <artifactId>guava</artifactId> | |
83 | + </dependency> | |
84 | + <dependency> | |
85 | + <groupId>io.grpc</groupId> | |
86 | + <artifactId>grpc-netty</artifactId> | |
87 | + <exclusions> | |
88 | + <exclusion> | |
89 | + <artifactId>netty-transport</artifactId> | |
90 | + <groupId>io.netty</groupId> | |
91 | + </exclusion> | |
92 | + <exclusion> | |
93 | + <artifactId>netty-common</artifactId> | |
94 | + <groupId>io.netty</groupId> | |
95 | + </exclusion> | |
96 | + </exclusions> | |
97 | + </dependency> | |
98 | + <dependency> | |
99 | + <groupId>io.grpc</groupId> | |
100 | + <artifactId>grpc-protobuf</artifactId> | |
101 | + </dependency> | |
102 | + <dependency> | |
103 | + <groupId>io.grpc</groupId> | |
104 | + <artifactId>grpc-stub</artifactId> | |
105 | + </dependency> | |
106 | + <dependency> | |
107 | + <groupId>com.google.protobuf</groupId> | |
108 | + <artifactId>protobuf-java</artifactId> | |
109 | + </dependency> | |
110 | + </dependencies> | |
111 | + | |
112 | + <build> | |
113 | + <plugins> | |
114 | + <plugin> | |
115 | + <groupId>org.xolstice.maven.plugins</groupId> | |
116 | + <artifactId>protobuf-maven-plugin</artifactId> | |
117 | + </plugin> | |
118 | + </plugins> | |
119 | + </build> | |
120 | + | |
121 | + <distributionManagement> | |
122 | + <repository> | |
123 | + <id>thingsboard-repo-deploy</id> | |
124 | + <name>ThingsBoard Repo Deployment</name> | |
125 | + <url>https://repo.thingsboard.io/artifactory/libs-release-public</url> | |
126 | + </repository> | |
127 | + </distributionManagement> | |
128 | + | |
129 | +</project> | |
\ No newline at end of file | ... | ... |
common/edge-api/src/main/java/org/thingsboard/edge/exception/EdgeConnectionException.java
0 → 100644
1 | +/** | |
2 | + * Copyright © 2016-2020 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.edge.exception; | |
17 | + | |
18 | +public class EdgeConnectionException extends RuntimeException { | |
19 | + | |
20 | + private static final long serialVersionUID = -4372754681230555723L; | |
21 | + | |
22 | + public EdgeConnectionException(String message) { | |
23 | + super(message); | |
24 | + } | |
25 | + | |
26 | + public EdgeConnectionException(String message, Throwable cause) { | |
27 | + super(message, cause); | |
28 | + } | |
29 | +} | |
\ No newline at end of file | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2020 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.edge.rpc; | |
17 | + | |
18 | +import com.google.common.io.Resources; | |
19 | +import io.grpc.ManagedChannel; | |
20 | +import io.grpc.netty.GrpcSslContexts; | |
21 | +import io.grpc.netty.NettyChannelBuilder; | |
22 | +import io.grpc.stub.StreamObserver; | |
23 | +import lombok.extern.slf4j.Slf4j; | |
24 | +import org.springframework.beans.factory.annotation.Value; | |
25 | +import org.springframework.stereotype.Service; | |
26 | +import org.thingsboard.edge.exception.EdgeConnectionException; | |
27 | +import org.thingsboard.server.gen.edge.ConnectRequestMsg; | |
28 | +import org.thingsboard.server.gen.edge.ConnectResponseCode; | |
29 | +import org.thingsboard.server.gen.edge.ConnectResponseMsg; | |
30 | +import org.thingsboard.server.gen.edge.DownlinkMsg; | |
31 | +import org.thingsboard.server.gen.edge.EdgeConfiguration; | |
32 | +import org.thingsboard.server.gen.edge.EdgeRpcServiceGrpc; | |
33 | +import org.thingsboard.server.gen.edge.EntityUpdateMsg; | |
34 | +import org.thingsboard.server.gen.edge.RequestMsg; | |
35 | +import org.thingsboard.server.gen.edge.RequestMsgType; | |
36 | +import org.thingsboard.server.gen.edge.ResponseMsg; | |
37 | +import org.thingsboard.server.gen.edge.UplinkMsg; | |
38 | +import org.thingsboard.server.gen.edge.UplinkResponseMsg; | |
39 | + | |
40 | +import javax.net.ssl.SSLException; | |
41 | +import java.io.File; | |
42 | +import java.net.URISyntaxException; | |
43 | +import java.util.concurrent.TimeUnit; | |
44 | +import java.util.function.Consumer; | |
45 | + | |
46 | +@Service | |
47 | +@Slf4j | |
48 | +public class EdgeGrpcClient implements EdgeRpcClient { | |
49 | + | |
50 | + @Value("${cloud.rpc.host}") | |
51 | + private String rpcHost; | |
52 | + @Value("${cloud.rpc.port}") | |
53 | + private int rpcPort; | |
54 | + @Value("${cloud.rpc.timeout}") | |
55 | + private int timeoutSecs; | |
56 | + @Value("${cloud.rpc.ssl.enabled}") | |
57 | + private boolean sslEnabled; | |
58 | + @Value("${cloud.rpc.ssl.cert}") | |
59 | + private String certResource; | |
60 | + | |
61 | + private ManagedChannel channel; | |
62 | + | |
63 | + private StreamObserver<RequestMsg> inputStream; | |
64 | + | |
65 | + @Override | |
66 | + public void connect(String edgeKey, | |
67 | + String edgeSecret, | |
68 | + Consumer<UplinkResponseMsg> onUplinkResponse, | |
69 | + Consumer<EdgeConfiguration> onEdgeUpdate, | |
70 | + Consumer<EntityUpdateMsg> onEntityUpdate, | |
71 | + Consumer<DownlinkMsg> onDownlink, | |
72 | + Consumer<Exception> onError) { | |
73 | + NettyChannelBuilder builder = NettyChannelBuilder.forAddress(rpcHost, rpcPort).usePlaintext(); | |
74 | + if (sslEnabled) { | |
75 | + try { | |
76 | + builder.sslContext(GrpcSslContexts.forClient().trustManager(new File(Resources.getResource(certResource).toURI())).build()); | |
77 | + } catch (URISyntaxException | SSLException e) { | |
78 | + log.error("Failed to initialize channel!", e); | |
79 | + throw new RuntimeException(e); | |
80 | + } | |
81 | + } | |
82 | + channel = builder.build(); | |
83 | + EdgeRpcServiceGrpc.EdgeRpcServiceStub stub = EdgeRpcServiceGrpc.newStub(channel); | |
84 | + log.info("[{}] Sending a connect request to the TB!", edgeKey); | |
85 | + this.inputStream = stub.handleMsgs(initOutputStream(edgeKey, onUplinkResponse, onEdgeUpdate, onEntityUpdate, onDownlink, onError)); | |
86 | + this.inputStream.onNext(RequestMsg.newBuilder() | |
87 | + .setMsgType(RequestMsgType.CONNECT_RPC_MESSAGE) | |
88 | + .setConnectRequestMsg(ConnectRequestMsg.newBuilder().setEdgeRoutingKey(edgeKey).setEdgeSecret(edgeSecret).build()) | |
89 | + .build()); | |
90 | + } | |
91 | + | |
92 | + @Override | |
93 | + public void disconnect() throws InterruptedException { | |
94 | + inputStream.onCompleted(); | |
95 | + if (channel != null) { | |
96 | + channel.shutdown().awaitTermination(timeoutSecs, TimeUnit.SECONDS); | |
97 | + } | |
98 | + } | |
99 | + | |
100 | + @Override | |
101 | + public void sendUplinkMsg(UplinkMsg msg) { | |
102 | + this.inputStream.onNext(RequestMsg.newBuilder() | |
103 | + .setMsgType(RequestMsgType.UPLINK_RPC_MESSAGE) | |
104 | + .setUplinkMsg(msg) | |
105 | + .build()); | |
106 | + } | |
107 | + | |
108 | + private StreamObserver<ResponseMsg> initOutputStream(String edgeKey, | |
109 | + Consumer<UplinkResponseMsg> onUplinkResponse, | |
110 | + Consumer<EdgeConfiguration> onEdgeUpdate, | |
111 | + Consumer<EntityUpdateMsg> onEntityUpdate, | |
112 | + Consumer<DownlinkMsg> onDownlink, | |
113 | + Consumer<Exception> onError) { | |
114 | + return new StreamObserver<ResponseMsg>() { | |
115 | + @Override | |
116 | + public void onNext(ResponseMsg responseMsg) { | |
117 | + if (responseMsg.hasConnectResponseMsg()) { | |
118 | + ConnectResponseMsg connectResponseMsg = responseMsg.getConnectResponseMsg(); | |
119 | + if (connectResponseMsg.getResponseCode().equals(ConnectResponseCode.ACCEPTED)) { | |
120 | + log.info("[{}] Configuration received: {}", edgeKey, connectResponseMsg.getConfiguration()); | |
121 | + onEdgeUpdate.accept(connectResponseMsg.getConfiguration()); | |
122 | + } else { | |
123 | + log.error("[{}] Failed to establish the connection! Code: {}. Error message: {}.", edgeKey, connectResponseMsg.getResponseCode(), connectResponseMsg.getErrorMsg()); | |
124 | + onError.accept(new EdgeConnectionException("Failed to establish the connection! Response code: " + connectResponseMsg.getResponseCode().name())); | |
125 | + } | |
126 | + } else if (responseMsg.hasUplinkResponseMsg()) { | |
127 | + log.debug("[{}] Uplink response message received {}", edgeKey, responseMsg.getUplinkResponseMsg()); | |
128 | + onUplinkResponse.accept(responseMsg.getUplinkResponseMsg()); | |
129 | + } else if (responseMsg.hasEntityUpdateMsg()) { | |
130 | + log.debug("[{}] Entity update message received {}", edgeKey, responseMsg.getEntityUpdateMsg()); | |
131 | + onEntityUpdate.accept(responseMsg.getEntityUpdateMsg()); | |
132 | + } else if (responseMsg.hasDownlinkMsg()) { | |
133 | + log.debug("[{}] Downlink message received for rule chain {}", edgeKey, responseMsg.getDownlinkMsg()); | |
134 | + onDownlink.accept(responseMsg.getDownlinkMsg()); | |
135 | + } | |
136 | + } | |
137 | + | |
138 | + @Override | |
139 | + public void onError(Throwable t) { | |
140 | + log.debug("[{}] The rpc session received an error!", edgeKey, t); | |
141 | + onError.accept(new RuntimeException(t)); | |
142 | + } | |
143 | + | |
144 | + @Override | |
145 | + public void onCompleted() { | |
146 | + log.debug("[{}] The rpc session was closed!", edgeKey); | |
147 | + } | |
148 | + }; | |
149 | + } | |
150 | +} | |
\ No newline at end of file | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2020 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.edge.rpc; | |
17 | + | |
18 | +import org.thingsboard.server.gen.edge.DownlinkMsg; | |
19 | +import org.thingsboard.server.gen.edge.EdgeConfiguration; | |
20 | +import org.thingsboard.server.gen.edge.EntityUpdateMsg; | |
21 | +import org.thingsboard.server.gen.edge.UplinkMsg; | |
22 | +import org.thingsboard.server.gen.edge.UplinkResponseMsg; | |
23 | + | |
24 | +import java.util.function.Consumer; | |
25 | + | |
26 | +public interface EdgeRpcClient { | |
27 | + | |
28 | + void connect(String integrationKey, | |
29 | + String integrationSecret, | |
30 | + Consumer<UplinkResponseMsg> onUplinkResponse, | |
31 | + Consumer<EdgeConfiguration> onEdgeUpdate, | |
32 | + Consumer<EntityUpdateMsg> onEntityUpdate, | |
33 | + Consumer<DownlinkMsg> onDownlink, | |
34 | + Consumer<Exception> onError); | |
35 | + | |
36 | + void disconnect() throws InterruptedException; | |
37 | + | |
38 | + void sendUplinkMsg(UplinkMsg uplinkMsg) throws InterruptedException; | |
39 | +} | |
\ No newline at end of file | ... | ... |