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,6 +89,10 @@ | ||
89 | <artifactId>queue</artifactId> | 89 | <artifactId>queue</artifactId> |
90 | </dependency> | 90 | </dependency> |
91 | <dependency> | 91 | <dependency> |
92 | + <groupId>org.thingsboard.common</groupId> | ||
93 | + <artifactId>edge-api</artifactId> | ||
94 | + </dependency> | ||
95 | + <dependency> | ||
92 | <groupId>org.thingsboard</groupId> | 96 | <groupId>org.thingsboard</groupId> |
93 | <artifactId>dao</artifactId> | 97 | <artifactId>dao</artifactId> |
94 | <type>test-jar</type> | 98 | <type>test-jar</type> |
@@ -2,6 +2,7 @@ | @@ -2,6 +2,7 @@ | ||
2 | "ruleChain": { | 2 | "ruleChain": { |
3 | "additionalInfo": null, | 3 | "additionalInfo": null, |
4 | "name": "Root Rule Chain", | 4 | "name": "Root Rule Chain", |
5 | + "type": "SYSTEM", | ||
5 | "firstRuleNodeId": null, | 6 | "firstRuleNodeId": null, |
6 | "root": true, | 7 | "root": true, |
7 | "debugMode": false, | 8 | "debugMode": false, |
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 | +); |
@@ -56,6 +56,7 @@ import org.thingsboard.server.dao.cassandra.CassandraCluster; | @@ -56,6 +56,7 @@ import org.thingsboard.server.dao.cassandra.CassandraCluster; | ||
56 | import org.thingsboard.server.dao.customer.CustomerService; | 56 | import org.thingsboard.server.dao.customer.CustomerService; |
57 | import org.thingsboard.server.dao.dashboard.DashboardService; | 57 | import org.thingsboard.server.dao.dashboard.DashboardService; |
58 | import org.thingsboard.server.dao.device.DeviceService; | 58 | import org.thingsboard.server.dao.device.DeviceService; |
59 | +import org.thingsboard.server.dao.edge.EdgeService; | ||
59 | import org.thingsboard.server.dao.entityview.EntityViewService; | 60 | import org.thingsboard.server.dao.entityview.EntityViewService; |
60 | import org.thingsboard.server.dao.event.EventService; | 61 | import org.thingsboard.server.dao.event.EventService; |
61 | import org.thingsboard.server.dao.nosql.CassandraBufferedRateExecutor; | 62 | import org.thingsboard.server.dao.nosql.CassandraBufferedRateExecutor; |
@@ -248,6 +249,11 @@ public class ActorSystemContext { | @@ -248,6 +249,11 @@ public class ActorSystemContext { | ||
248 | @Getter | 249 | @Getter |
249 | private RuleChainTransactionService ruleChainTransactionService; | 250 | private RuleChainTransactionService ruleChainTransactionService; |
250 | 251 | ||
252 | + @Lazy | ||
253 | + @Autowired | ||
254 | + @Getter | ||
255 | + private EdgeService edgeService; | ||
256 | + | ||
251 | @Value("${cluster.partition_id}") | 257 | @Value("${cluster.partition_id}") |
252 | @Getter | 258 | @Getter |
253 | private long queuePartitionId; | 259 | private long queuePartitionId; |
@@ -60,6 +60,7 @@ import org.thingsboard.server.dao.cassandra.CassandraCluster; | @@ -60,6 +60,7 @@ import org.thingsboard.server.dao.cassandra.CassandraCluster; | ||
60 | import org.thingsboard.server.dao.customer.CustomerService; | 60 | import org.thingsboard.server.dao.customer.CustomerService; |
61 | import org.thingsboard.server.dao.dashboard.DashboardService; | 61 | import org.thingsboard.server.dao.dashboard.DashboardService; |
62 | import org.thingsboard.server.dao.device.DeviceService; | 62 | import org.thingsboard.server.dao.device.DeviceService; |
63 | +import org.thingsboard.server.dao.edge.EdgeService; | ||
63 | import org.thingsboard.server.dao.entityview.EntityViewService; | 64 | import org.thingsboard.server.dao.entityview.EntityViewService; |
64 | import org.thingsboard.server.dao.nosql.CassandraStatementTask; | 65 | import org.thingsboard.server.dao.nosql.CassandraStatementTask; |
65 | import org.thingsboard.server.dao.relation.RelationService; | 66 | import org.thingsboard.server.dao.relation.RelationService; |
@@ -185,15 +186,26 @@ class DefaultTbContext implements TbContext { | @@ -185,15 +186,26 @@ class DefaultTbContext implements TbContext { | ||
185 | } | 186 | } |
186 | 187 | ||
187 | public TbMsg alarmCreatedMsg(Alarm alarm, RuleNodeId ruleNodeId) { | 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 | try { | 201 | try { |
189 | ObjectNode entityNode = mapper.valueToTree(alarm); | 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 | } catch (JsonProcessingException | IllegalArgumentException e) { | 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 | @Override | 209 | @Override |
198 | public RuleNodeId getSelfId() { | 210 | public RuleNodeId getSelfId() { |
199 | return nodeCtx.getSelf().getId(); | 211 | return nodeCtx.getSelf().getId(); |
@@ -326,6 +338,11 @@ class DefaultTbContext implements TbContext { | @@ -326,6 +338,11 @@ class DefaultTbContext implements TbContext { | ||
326 | } | 338 | } |
327 | 339 | ||
328 | @Override | 340 | @Override |
341 | + public EdgeService getEdgeService() { | ||
342 | + return mainCtx.getEdgeService(); | ||
343 | + } | ||
344 | + | ||
345 | + @Override | ||
329 | public EventLoopGroup getSharedEventLoop() { | 346 | public EventLoopGroup getSharedEventLoop() { |
330 | return mainCtx.getSharedEventLoopGroupService().getSharedEventLoopGroup(); | 347 | return mainCtx.getSharedEventLoopGroupService().getSharedEventLoopGroup(); |
331 | } | 348 | } |
@@ -24,10 +24,12 @@ import com.datastax.driver.core.utils.UUIDs; | @@ -24,10 +24,12 @@ import com.datastax.driver.core.utils.UUIDs; | ||
24 | import java.util.Optional; | 24 | import java.util.Optional; |
25 | 25 | ||
26 | import lombok.extern.slf4j.Slf4j; | 26 | import lombok.extern.slf4j.Slf4j; |
27 | +import com.google.common.util.concurrent.FutureCallback; | ||
27 | import org.thingsboard.server.actors.ActorSystemContext; | 28 | import org.thingsboard.server.actors.ActorSystemContext; |
28 | import org.thingsboard.server.actors.device.DeviceActorToRuleEngineMsg; | 29 | import org.thingsboard.server.actors.device.DeviceActorToRuleEngineMsg; |
29 | import org.thingsboard.server.actors.service.DefaultActorService; | 30 | import org.thingsboard.server.actors.service.DefaultActorService; |
30 | import org.thingsboard.server.actors.shared.ComponentMsgProcessor; | 31 | import org.thingsboard.server.actors.shared.ComponentMsgProcessor; |
32 | +import org.thingsboard.server.common.data.DataConstants; | ||
31 | import org.thingsboard.server.common.data.EntityType; | 33 | import org.thingsboard.server.common.data.EntityType; |
32 | import org.thingsboard.server.common.data.id.EntityId; | 34 | import org.thingsboard.server.common.data.id.EntityId; |
33 | import org.thingsboard.server.common.data.id.RuleChainId; | 35 | import org.thingsboard.server.common.data.id.RuleChainId; |
@@ -37,14 +39,17 @@ import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; | @@ -37,14 +39,17 @@ import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; | ||
37 | import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; | 39 | import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; |
38 | import org.thingsboard.server.common.data.relation.EntityRelation; | 40 | import org.thingsboard.server.common.data.relation.EntityRelation; |
39 | import org.thingsboard.server.common.data.rule.RuleChain; | 41 | import org.thingsboard.server.common.data.rule.RuleChain; |
42 | +import org.thingsboard.server.common.data.rule.RuleChainType; | ||
40 | import org.thingsboard.server.common.data.rule.RuleNode; | 43 | import org.thingsboard.server.common.data.rule.RuleNode; |
41 | import org.thingsboard.server.common.msg.TbMsg; | 44 | import org.thingsboard.server.common.msg.TbMsg; |
42 | import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; | 45 | import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; |
43 | import org.thingsboard.server.common.msg.cluster.ServerAddress; | 46 | import org.thingsboard.server.common.msg.cluster.ServerAddress; |
44 | import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; | 47 | import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; |
45 | import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; | 48 | import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; |
49 | +import org.thingsboard.server.dao.edge.EdgeService; | ||
46 | import org.thingsboard.server.dao.rule.RuleChainService; | 50 | import org.thingsboard.server.dao.rule.RuleChainService; |
47 | 51 | ||
52 | +import javax.annotation.Nullable; | ||
48 | import java.util.ArrayList; | 53 | import java.util.ArrayList; |
49 | import java.util.Collections; | 54 | import java.util.Collections; |
50 | import java.util.HashMap; | 55 | import java.util.HashMap; |
@@ -65,6 +70,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh | @@ -65,6 +70,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh | ||
65 | private final Map<RuleNodeId, RuleNodeCtx> nodeActors; | 70 | private final Map<RuleNodeId, RuleNodeCtx> nodeActors; |
66 | private final Map<RuleNodeId, List<RuleNodeRelation>> nodeRoutes; | 71 | private final Map<RuleNodeId, List<RuleNodeRelation>> nodeRoutes; |
67 | private final RuleChainService service; | 72 | private final RuleChainService service; |
73 | + private final EdgeService edgeService; | ||
68 | 74 | ||
69 | private RuleNodeId firstId; | 75 | private RuleNodeId firstId; |
70 | private RuleNodeCtx firstNode; | 76 | private RuleNodeCtx firstNode; |
@@ -79,6 +85,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh | @@ -79,6 +85,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh | ||
79 | this.nodeActors = new HashMap<>(); | 85 | this.nodeActors = new HashMap<>(); |
80 | this.nodeRoutes = new HashMap<>(); | 86 | this.nodeRoutes = new HashMap<>(); |
81 | this.service = systemContext.getRuleChainService(); | 87 | this.service = systemContext.getRuleChainService(); |
88 | + this.edgeService = systemContext.getEdgeService(); | ||
82 | this.ruleChainName = ruleChainId.toString(); | 89 | this.ruleChainName = ruleChainId.toString(); |
83 | } | 90 | } |
84 | 91 | ||
@@ -92,17 +99,19 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh | @@ -92,17 +99,19 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh | ||
92 | if (!started) { | 99 | if (!started) { |
93 | RuleChain ruleChain = service.findRuleChainById(tenantId, entityId); | 100 | RuleChain ruleChain = service.findRuleChainById(tenantId, entityId); |
94 | if (ruleChain != null) { | 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 | } else { | 116 | } else { |
108 | onUpdate(context); | 117 | onUpdate(context); |
@@ -113,31 +122,36 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh | @@ -113,31 +122,36 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh | ||
113 | public void onUpdate(ActorContext context) { | 122 | public void onUpdate(ActorContext context) { |
114 | RuleChain ruleChain = service.findRuleChainById(tenantId, entityId); | 123 | RuleChain ruleChain = service.findRuleChainById(tenantId, entityId); |
115 | if (ruleChain != null) { | 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,6 +340,30 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh | ||
326 | if (nodeCtx != null) { | 340 | if (nodeCtx != null) { |
327 | nodeCtx.getSelfActor().tell(new RuleChainToRuleNodeMsg(new DefaultTbContext(systemContext, nodeCtx), msg, fromRelationType), self); | 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 | private TbMsg enrichWithRuleChainId(TbMsg tbMsg) { | 369 | private TbMsg enrichWithRuleChainId(TbMsg tbMsg) { |
@@ -39,6 +39,7 @@ import org.thingsboard.server.common.data.id.RuleChainId; | @@ -39,6 +39,7 @@ import org.thingsboard.server.common.data.id.RuleChainId; | ||
39 | import org.thingsboard.server.common.data.id.TenantId; | 39 | import org.thingsboard.server.common.data.id.TenantId; |
40 | import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; | 40 | import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; |
41 | import org.thingsboard.server.common.data.rule.RuleChain; | 41 | import org.thingsboard.server.common.data.rule.RuleChain; |
42 | +import org.thingsboard.server.common.data.rule.RuleChainType; | ||
42 | import org.thingsboard.server.common.msg.TbActorMsg; | 43 | import org.thingsboard.server.common.msg.TbActorMsg; |
43 | import org.thingsboard.server.common.msg.aware.DeviceAwareMsg; | 44 | import org.thingsboard.server.common.msg.aware.DeviceAwareMsg; |
44 | import org.thingsboard.server.common.msg.aware.RuleChainAwareMsg; | 45 | import org.thingsboard.server.common.msg.aware.RuleChainAwareMsg; |
@@ -139,11 +140,18 @@ public class TenantActor extends RuleChainManagerActor { | @@ -139,11 +140,18 @@ public class TenantActor extends RuleChainManagerActor { | ||
139 | } | 140 | } |
140 | 141 | ||
141 | private void onComponentLifecycleMsg(ComponentLifecycleMsg msg) { | 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 | ActorRef target = getEntityActorRef(msg.getEntityId()); | 152 | ActorRef target = getEntityActorRef(msg.getEntityId()); |
143 | if (target != null) { | 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 | ruleChainManager.visit(ruleChain, target); | 155 | ruleChainManager.visit(ruleChain, target); |
148 | } | 156 | } |
149 | target.tell(msg, ActorRef.noSender()); | 157 | target.tell(msg, ActorRef.noSender()); |
@@ -33,10 +33,12 @@ import org.thingsboard.server.common.data.asset.Asset; | @@ -33,10 +33,12 @@ import org.thingsboard.server.common.data.asset.Asset; | ||
33 | import org.thingsboard.server.common.data.asset.AssetInfo; | 33 | import org.thingsboard.server.common.data.asset.AssetInfo; |
34 | import org.thingsboard.server.common.data.asset.AssetSearchQuery; | 34 | import org.thingsboard.server.common.data.asset.AssetSearchQuery; |
35 | import org.thingsboard.server.common.data.audit.ActionType; | 35 | import org.thingsboard.server.common.data.audit.ActionType; |
36 | +import org.thingsboard.server.common.data.edge.Edge; | ||
36 | import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; | 37 | import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; |
37 | import org.thingsboard.server.common.data.exception.ThingsboardException; | 38 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
38 | import org.thingsboard.server.common.data.id.AssetId; | 39 | import org.thingsboard.server.common.data.id.AssetId; |
39 | import org.thingsboard.server.common.data.id.CustomerId; | 40 | import org.thingsboard.server.common.data.id.CustomerId; |
41 | +import org.thingsboard.server.common.data.id.EdgeId; | ||
40 | import org.thingsboard.server.common.data.id.TenantId; | 42 | import org.thingsboard.server.common.data.id.TenantId; |
41 | import org.thingsboard.server.common.data.page.PageData; | 43 | import org.thingsboard.server.common.data.page.PageData; |
42 | import org.thingsboard.server.common.data.page.PageLink; | 44 | import org.thingsboard.server.common.data.page.PageLink; |
@@ -51,6 +53,8 @@ import java.util.ArrayList; | @@ -51,6 +53,8 @@ import java.util.ArrayList; | ||
51 | import java.util.List; | 53 | import java.util.List; |
52 | import java.util.stream.Collectors; | 54 | import java.util.stream.Collectors; |
53 | 55 | ||
56 | +import static org.thingsboard.server.controller.EdgeController.EDGE_ID; | ||
57 | + | ||
54 | @RestController | 58 | @RestController |
55 | @RequestMapping("/api") | 59 | @RequestMapping("/api") |
56 | public class AssetController extends BaseController { | 60 | public class AssetController extends BaseController { |
@@ -396,4 +400,93 @@ public class AssetController extends BaseController { | @@ -396,4 +400,93 @@ public class AssetController extends BaseController { | ||
396 | throw handleException(e); | 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,12 +35,14 @@ import org.thingsboard.server.common.data.alarm.AlarmInfo; | ||
35 | import org.thingsboard.server.common.data.asset.Asset; | 35 | import org.thingsboard.server.common.data.asset.Asset; |
36 | import org.thingsboard.server.common.data.asset.AssetInfo; | 36 | import org.thingsboard.server.common.data.asset.AssetInfo; |
37 | import org.thingsboard.server.common.data.audit.ActionType; | 37 | import org.thingsboard.server.common.data.audit.ActionType; |
38 | +import org.thingsboard.server.common.data.edge.Edge; | ||
38 | import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; | 39 | import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; |
39 | import org.thingsboard.server.common.data.exception.ThingsboardException; | 40 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
40 | import org.thingsboard.server.common.data.id.AssetId; | 41 | import org.thingsboard.server.common.data.id.AssetId; |
41 | import org.thingsboard.server.common.data.id.CustomerId; | 42 | import org.thingsboard.server.common.data.id.CustomerId; |
42 | import org.thingsboard.server.common.data.id.DashboardId; | 43 | import org.thingsboard.server.common.data.id.DashboardId; |
43 | import org.thingsboard.server.common.data.id.DeviceId; | 44 | import org.thingsboard.server.common.data.id.DeviceId; |
45 | +import org.thingsboard.server.common.data.id.EdgeId; | ||
44 | import org.thingsboard.server.common.data.id.EntityId; | 46 | import org.thingsboard.server.common.data.id.EntityId; |
45 | import org.thingsboard.server.common.data.id.EntityIdFactory; | 47 | import org.thingsboard.server.common.data.id.EntityIdFactory; |
46 | import org.thingsboard.server.common.data.id.EntityViewId; | 48 | import org.thingsboard.server.common.data.id.EntityViewId; |
@@ -75,6 +77,7 @@ import org.thingsboard.server.dao.dashboard.DashboardService; | @@ -75,6 +77,7 @@ import org.thingsboard.server.dao.dashboard.DashboardService; | ||
75 | import org.thingsboard.server.dao.device.ClaimDevicesService; | 77 | import org.thingsboard.server.dao.device.ClaimDevicesService; |
76 | import org.thingsboard.server.dao.device.DeviceCredentialsService; | 78 | import org.thingsboard.server.dao.device.DeviceCredentialsService; |
77 | import org.thingsboard.server.dao.device.DeviceService; | 79 | import org.thingsboard.server.dao.device.DeviceService; |
80 | +import org.thingsboard.server.dao.edge.EdgeService; | ||
78 | import org.thingsboard.server.dao.entityview.EntityViewService; | 81 | import org.thingsboard.server.dao.entityview.EntityViewService; |
79 | import org.thingsboard.server.dao.exception.DataValidationException; | 82 | import org.thingsboard.server.dao.exception.DataValidationException; |
80 | import org.thingsboard.server.dao.exception.IncorrectParameterException; | 83 | import org.thingsboard.server.dao.exception.IncorrectParameterException; |
@@ -178,6 +181,9 @@ public abstract class BaseController { | @@ -178,6 +181,9 @@ public abstract class BaseController { | ||
178 | @Autowired | 181 | @Autowired |
179 | protected ClaimDevicesService claimDevicesService; | 182 | protected ClaimDevicesService claimDevicesService; |
180 | 183 | ||
184 | + @Autowired | ||
185 | + protected EdgeService edgeService; | ||
186 | + | ||
181 | @Value("${server.log_controller_error_stack_trace}") | 187 | @Value("${server.log_controller_error_stack_trace}") |
182 | @Getter | 188 | @Getter |
183 | private boolean logControllerErrorStackTrace; | 189 | private boolean logControllerErrorStackTrace; |
@@ -353,6 +359,9 @@ public abstract class BaseController { | @@ -353,6 +359,9 @@ public abstract class BaseController { | ||
353 | case ENTITY_VIEW: | 359 | case ENTITY_VIEW: |
354 | checkEntityViewId(new EntityViewId(entityId.getId()), operation); | 360 | checkEntityViewId(new EntityViewId(entityId.getId()), operation); |
355 | return; | 361 | return; |
362 | + case EDGE: | ||
363 | + checkEdgeId(new EdgeId(entityId.getId()), operation); | ||
364 | + return; | ||
356 | default: | 365 | default: |
357 | throw new IllegalArgumentException("Unsupported entity type: " + entityId.getEntityType()); | 366 | throw new IllegalArgumentException("Unsupported entity type: " + entityId.getEntityType()); |
358 | } | 367 | } |
@@ -548,6 +557,17 @@ public abstract class BaseController { | @@ -548,6 +557,17 @@ public abstract class BaseController { | ||
548 | return ruleNode; | 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 | protected String constructBaseUrl(HttpServletRequest request) { | 572 | protected String constructBaseUrl(HttpServletRequest request) { |
553 | String scheme = request.getScheme(); | 573 | String scheme = request.getScheme(); |
@@ -637,6 +657,12 @@ public abstract class BaseController { | @@ -637,6 +657,12 @@ public abstract class BaseController { | ||
637 | case ALARM_CLEAR: | 657 | case ALARM_CLEAR: |
638 | msgType = DataConstants.ALARM_CLEAR; | 658 | msgType = DataConstants.ALARM_CLEAR; |
639 | break; | 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 | if (!StringUtils.isEmpty(msgType)) { | 667 | if (!StringUtils.isEmpty(msgType)) { |
642 | try { | 668 | try { |
@@ -656,6 +682,12 @@ public abstract class BaseController { | @@ -656,6 +682,12 @@ public abstract class BaseController { | ||
656 | String strCustomerName = extractParameter(String.class, 2, additionalInfo); | 682 | String strCustomerName = extractParameter(String.class, 2, additionalInfo); |
657 | metaData.putValue("unassignedCustomerId", strCustomerId); | 683 | metaData.putValue("unassignedCustomerId", strCustomerId); |
658 | metaData.putValue("unassignedCustomerName", strCustomerName); | 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 | ObjectNode entityNode; | 692 | ObjectNode entityNode; |
661 | if (entity != null) { | 693 | if (entity != null) { |
@@ -32,10 +32,13 @@ import org.thingsboard.server.common.data.Dashboard; | @@ -32,10 +32,13 @@ import org.thingsboard.server.common.data.Dashboard; | ||
32 | import org.thingsboard.server.common.data.DashboardInfo; | 32 | import org.thingsboard.server.common.data.DashboardInfo; |
33 | import org.thingsboard.server.common.data.EntityType; | 33 | import org.thingsboard.server.common.data.EntityType; |
34 | import org.thingsboard.server.common.data.ShortCustomerInfo; | 34 | import org.thingsboard.server.common.data.ShortCustomerInfo; |
35 | +import org.thingsboard.server.common.data.ShortEdgeInfo; | ||
35 | import org.thingsboard.server.common.data.audit.ActionType; | 36 | import org.thingsboard.server.common.data.audit.ActionType; |
37 | +import org.thingsboard.server.common.data.edge.Edge; | ||
36 | import org.thingsboard.server.common.data.exception.ThingsboardException; | 38 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
37 | import org.thingsboard.server.common.data.id.CustomerId; | 39 | import org.thingsboard.server.common.data.id.CustomerId; |
38 | import org.thingsboard.server.common.data.id.DashboardId; | 40 | import org.thingsboard.server.common.data.id.DashboardId; |
41 | +import org.thingsboard.server.common.data.id.EdgeId; | ||
39 | import org.thingsboard.server.common.data.id.TenantId; | 42 | import org.thingsboard.server.common.data.id.TenantId; |
40 | import org.thingsboard.server.common.data.page.PageData; | 43 | import org.thingsboard.server.common.data.page.PageData; |
41 | import org.thingsboard.server.common.data.page.PageLink; | 44 | import org.thingsboard.server.common.data.page.PageLink; |
@@ -474,4 +477,241 @@ public class DashboardController extends BaseController { | @@ -474,4 +477,241 @@ public class DashboardController extends BaseController { | ||
474 | throw handleException(e); | 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,9 +33,11 @@ import org.springframework.web.context.request.async.DeferredResult; | ||
33 | import org.thingsboard.server.common.data.*; | 33 | import org.thingsboard.server.common.data.*; |
34 | import org.thingsboard.server.common.data.audit.ActionType; | 34 | import org.thingsboard.server.common.data.audit.ActionType; |
35 | import org.thingsboard.server.common.data.device.DeviceSearchQuery; | 35 | import org.thingsboard.server.common.data.device.DeviceSearchQuery; |
36 | +import org.thingsboard.server.common.data.edge.Edge; | ||
36 | import org.thingsboard.server.common.data.exception.ThingsboardException; | 37 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
37 | import org.thingsboard.server.common.data.id.CustomerId; | 38 | import org.thingsboard.server.common.data.id.CustomerId; |
38 | import org.thingsboard.server.common.data.id.DeviceId; | 39 | import org.thingsboard.server.common.data.id.DeviceId; |
40 | +import org.thingsboard.server.common.data.id.EdgeId; | ||
39 | import org.thingsboard.server.common.data.id.TenantId; | 41 | import org.thingsboard.server.common.data.id.TenantId; |
40 | import org.thingsboard.server.common.data.page.PageData; | 42 | import org.thingsboard.server.common.data.page.PageData; |
41 | import org.thingsboard.server.common.data.page.PageLink; | 43 | import org.thingsboard.server.common.data.page.PageLink; |
@@ -55,6 +57,8 @@ import java.util.ArrayList; | @@ -55,6 +57,8 @@ import java.util.ArrayList; | ||
55 | import java.util.List; | 57 | import java.util.List; |
56 | import java.util.stream.Collectors; | 58 | import java.util.stream.Collectors; |
57 | 59 | ||
60 | +import static org.thingsboard.server.controller.EdgeController.EDGE_ID; | ||
61 | + | ||
58 | @RestController | 62 | @RestController |
59 | @RequestMapping("/api") | 63 | @RequestMapping("/api") |
60 | public class DeviceController extends BaseController { | 64 | public class DeviceController extends BaseController { |
@@ -541,4 +545,88 @@ public class DeviceController extends BaseController { | @@ -541,4 +545,88 @@ public class DeviceController extends BaseController { | ||
541 | } | 545 | } |
542 | return DataConstants.DEFAULT_SECRET_KEY; | 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 | +} |
@@ -31,9 +31,11 @@ import org.springframework.web.bind.annotation.ResponseStatus; | @@ -31,9 +31,11 @@ import org.springframework.web.bind.annotation.ResponseStatus; | ||
31 | import org.springframework.web.bind.annotation.RestController; | 31 | import org.springframework.web.bind.annotation.RestController; |
32 | import org.thingsboard.server.common.data.*; | 32 | import org.thingsboard.server.common.data.*; |
33 | import org.thingsboard.server.common.data.audit.ActionType; | 33 | import org.thingsboard.server.common.data.audit.ActionType; |
34 | +import org.thingsboard.server.common.data.edge.Edge; | ||
34 | import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery; | 35 | import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery; |
35 | import org.thingsboard.server.common.data.exception.ThingsboardException; | 36 | 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.CustomerId; |
38 | +import org.thingsboard.server.common.data.id.EdgeId; | ||
37 | import org.thingsboard.server.common.data.id.EntityId; | 39 | import org.thingsboard.server.common.data.id.EntityId; |
38 | import org.thingsboard.server.common.data.id.EntityViewId; | 40 | import org.thingsboard.server.common.data.id.EntityViewId; |
39 | import org.thingsboard.server.common.data.id.TenantId; | 41 | import org.thingsboard.server.common.data.id.TenantId; |
@@ -55,6 +57,7 @@ import java.util.concurrent.ExecutionException; | @@ -55,6 +57,7 @@ import java.util.concurrent.ExecutionException; | ||
55 | import java.util.stream.Collectors; | 57 | import java.util.stream.Collectors; |
56 | 58 | ||
57 | import static org.thingsboard.server.controller.CustomerController.CUSTOMER_ID; | 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 | * Created by Victor Basanets on 8/28/2017. | 63 | * Created by Victor Basanets on 8/28/2017. |
@@ -426,4 +429,84 @@ public class EntityViewController extends BaseController { | @@ -426,4 +429,84 @@ public class EntityViewController extends BaseController { | ||
426 | throw handleException(e); | 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,16 +40,21 @@ import org.thingsboard.server.actors.tenant.DebugTbRateLimits; | ||
40 | import org.thingsboard.server.common.data.DataConstants; | 40 | import org.thingsboard.server.common.data.DataConstants; |
41 | import org.thingsboard.server.common.data.EntityType; | 41 | import org.thingsboard.server.common.data.EntityType; |
42 | import org.thingsboard.server.common.data.Event; | 42 | import org.thingsboard.server.common.data.Event; |
43 | +import org.thingsboard.server.common.data.ShortEdgeInfo; | ||
43 | import org.thingsboard.server.common.data.audit.ActionType; | 44 | import org.thingsboard.server.common.data.audit.ActionType; |
45 | +import org.thingsboard.server.common.data.edge.Edge; | ||
44 | import org.thingsboard.server.common.data.exception.ThingsboardException; | 46 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
47 | +import org.thingsboard.server.common.data.id.EdgeId; | ||
45 | import org.thingsboard.server.common.data.id.RuleChainId; | 48 | import org.thingsboard.server.common.data.id.RuleChainId; |
46 | import org.thingsboard.server.common.data.id.RuleNodeId; | 49 | import org.thingsboard.server.common.data.id.RuleNodeId; |
47 | import org.thingsboard.server.common.data.id.TenantId; | 50 | import org.thingsboard.server.common.data.id.TenantId; |
48 | import org.thingsboard.server.common.data.page.PageData; | 51 | import org.thingsboard.server.common.data.page.PageData; |
49 | import org.thingsboard.server.common.data.page.PageLink; | 52 | import org.thingsboard.server.common.data.page.PageLink; |
53 | +import org.thingsboard.server.common.data.page.TimePageLink; | ||
50 | import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; | 54 | import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; |
51 | import org.thingsboard.server.common.data.rule.RuleChain; | 55 | import org.thingsboard.server.common.data.rule.RuleChain; |
52 | import org.thingsboard.server.common.data.rule.RuleChainMetaData; | 56 | import org.thingsboard.server.common.data.rule.RuleChainMetaData; |
57 | +import org.thingsboard.server.common.data.rule.RuleChainType; | ||
53 | import org.thingsboard.server.common.data.rule.RuleNode; | 58 | import org.thingsboard.server.common.data.rule.RuleNode; |
54 | import org.thingsboard.server.common.msg.TbMsg; | 59 | import org.thingsboard.server.common.msg.TbMsg; |
55 | import org.thingsboard.server.common.msg.TbMsgMetaData; | 60 | import org.thingsboard.server.common.msg.TbMsgMetaData; |
@@ -59,6 +64,7 @@ import org.thingsboard.server.service.script.RuleNodeJsScriptEngine; | @@ -59,6 +64,7 @@ import org.thingsboard.server.service.script.RuleNodeJsScriptEngine; | ||
59 | import org.thingsboard.server.service.security.permission.Operation; | 64 | import org.thingsboard.server.service.security.permission.Operation; |
60 | import org.thingsboard.server.service.security.permission.Resource; | 65 | import org.thingsboard.server.service.security.permission.Resource; |
61 | 66 | ||
67 | +import java.util.HashSet; | ||
62 | import java.util.List; | 68 | import java.util.List; |
63 | import java.util.Map; | 69 | import java.util.Map; |
64 | import java.util.Set; | 70 | import java.util.Set; |
@@ -225,13 +231,19 @@ public class RuleChainController extends BaseController { | @@ -225,13 +231,19 @@ public class RuleChainController extends BaseController { | ||
225 | public PageData<RuleChain> getRuleChains( | 231 | public PageData<RuleChain> getRuleChains( |
226 | @RequestParam int pageSize, | 232 | @RequestParam int pageSize, |
227 | @RequestParam int page, | 233 | @RequestParam int page, |
234 | + @RequestParam(value = "type", required = false) String typeStr, | ||
228 | @RequestParam(required = false) String textSearch, | 235 | @RequestParam(required = false) String textSearch, |
229 | @RequestParam(required = false) String sortProperty, | 236 | @RequestParam(required = false) String sortProperty, |
230 | @RequestParam(required = false) String sortOrder) throws ThingsboardException { | 237 | @RequestParam(required = false) String sortOrder) throws ThingsboardException { |
231 | try { | 238 | try { |
232 | TenantId tenantId = getCurrentUser().getTenantId(); | 239 | TenantId tenantId = getCurrentUser().getTenantId(); |
233 | PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); | 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 | } catch (Exception e) { | 247 | } catch (Exception e) { |
236 | throw handleException(e); | 248 | throw handleException(e); |
237 | } | 249 | } |
@@ -372,4 +384,242 @@ public class RuleChainController extends BaseController { | @@ -372,4 +384,242 @@ public class RuleChainController extends BaseController { | ||
372 | return objectMapper.writeValueAsString(msgData); | 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 | +} |
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 | +} |
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 | +} |
@@ -211,6 +211,8 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService | @@ -211,6 +211,8 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService | ||
211 | case "2.4.3": | 211 | case "2.4.3": |
212 | try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { | 212 | try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { |
213 | log.info("Updating schema ..."); | 213 | log.info("Updating schema ..."); |
214 | + schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.5.0", SCHEMA_UPDATE_SQL); | ||
215 | + loadSql(schemaUpdateFile, conn); | ||
214 | try { | 216 | try { |
215 | conn.createStatement().execute("ALTER TABLE attribute_kv ADD COLUMN json_v json;"); | 217 | conn.createStatement().execute("ALTER TABLE attribute_kv ADD COLUMN json_v json;"); |
216 | } catch (Exception e) { | 218 | } catch (Exception e) { |
@@ -221,6 +223,24 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService | @@ -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 | log.info("Schema updated."); | 244 | log.info("Schema updated."); |
225 | } | 245 | } |
226 | break; | 246 | break; |
@@ -30,10 +30,12 @@ import org.thingsboard.server.common.data.Device; | @@ -30,10 +30,12 @@ import org.thingsboard.server.common.data.Device; | ||
30 | import org.thingsboard.server.common.data.EntityView; | 30 | import org.thingsboard.server.common.data.EntityView; |
31 | import org.thingsboard.server.common.data.Tenant; | 31 | import org.thingsboard.server.common.data.Tenant; |
32 | import org.thingsboard.server.common.data.asset.Asset; | 32 | import org.thingsboard.server.common.data.asset.Asset; |
33 | +import org.thingsboard.server.common.data.edge.Edge; | ||
33 | import org.thingsboard.server.common.data.exception.ThingsboardException; | 34 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
34 | import org.thingsboard.server.common.data.id.AssetId; | 35 | import org.thingsboard.server.common.data.id.AssetId; |
35 | import org.thingsboard.server.common.data.id.CustomerId; | 36 | import org.thingsboard.server.common.data.id.CustomerId; |
36 | import org.thingsboard.server.common.data.id.DeviceId; | 37 | import org.thingsboard.server.common.data.id.DeviceId; |
38 | +import org.thingsboard.server.common.data.id.EdgeId; | ||
37 | import org.thingsboard.server.common.data.id.EntityId; | 39 | import org.thingsboard.server.common.data.id.EntityId; |
38 | import org.thingsboard.server.common.data.id.EntityIdFactory; | 40 | import org.thingsboard.server.common.data.id.EntityIdFactory; |
39 | import org.thingsboard.server.common.data.id.EntityViewId; | 41 | import org.thingsboard.server.common.data.id.EntityViewId; |
@@ -47,6 +49,7 @@ import org.thingsboard.server.dao.alarm.AlarmService; | @@ -47,6 +49,7 @@ import org.thingsboard.server.dao.alarm.AlarmService; | ||
47 | import org.thingsboard.server.dao.asset.AssetService; | 49 | import org.thingsboard.server.dao.asset.AssetService; |
48 | import org.thingsboard.server.dao.customer.CustomerService; | 50 | import org.thingsboard.server.dao.customer.CustomerService; |
49 | import org.thingsboard.server.dao.device.DeviceService; | 51 | import org.thingsboard.server.dao.device.DeviceService; |
52 | +import org.thingsboard.server.dao.edge.EdgeService; | ||
50 | import org.thingsboard.server.dao.entityview.EntityViewService; | 53 | import org.thingsboard.server.dao.entityview.EntityViewService; |
51 | import org.thingsboard.server.dao.rule.RuleChainService; | 54 | import org.thingsboard.server.dao.rule.RuleChainService; |
52 | import org.thingsboard.server.dao.tenant.TenantService; | 55 | import org.thingsboard.server.dao.tenant.TenantService; |
@@ -74,6 +77,7 @@ public class AccessValidator { | @@ -74,6 +77,7 @@ public class AccessValidator { | ||
74 | public static final String SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION = "System administrator is not allowed to perform this operation!"; | 77 | public static final String SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION = "System administrator is not allowed to perform this operation!"; |
75 | public static final String DEVICE_WITH_REQUESTED_ID_NOT_FOUND = "Device with requested id wasn't found!"; | 78 | public static final String DEVICE_WITH_REQUESTED_ID_NOT_FOUND = "Device with requested id wasn't found!"; |
76 | public static final String ENTITY_VIEW_WITH_REQUESTED_ID_NOT_FOUND = "Entity-view with requested id wasn't found!"; | 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 | @Autowired | 82 | @Autowired |
79 | protected TenantService tenantService; | 83 | protected TenantService tenantService; |
@@ -100,6 +104,9 @@ public class AccessValidator { | @@ -100,6 +104,9 @@ public class AccessValidator { | ||
100 | protected EntityViewService entityViewService; | 104 | protected EntityViewService entityViewService; |
101 | 105 | ||
102 | @Autowired | 106 | @Autowired |
107 | + protected EdgeService edgeService; | ||
108 | + | ||
109 | + @Autowired | ||
103 | protected AccessControlService accessControlService; | 110 | protected AccessControlService accessControlService; |
104 | 111 | ||
105 | private ExecutorService executor; | 112 | private ExecutorService executor; |
@@ -175,6 +182,9 @@ public class AccessValidator { | @@ -175,6 +182,9 @@ public class AccessValidator { | ||
175 | case ENTITY_VIEW: | 182 | case ENTITY_VIEW: |
176 | validateEntityView(currentUser, operation, entityId, callback); | 183 | validateEntityView(currentUser, operation, entityId, callback); |
177 | return; | 184 | return; |
185 | + case EDGE: | ||
186 | + validateEdge(currentUser, operation, entityId, callback); | ||
187 | + return; | ||
178 | default: | 188 | default: |
179 | //TODO: add support of other entities | 189 | //TODO: add support of other entities |
180 | throw new IllegalStateException("Not Implemented!"); | 190 | throw new IllegalStateException("Not Implemented!"); |
@@ -328,6 +338,26 @@ public class AccessValidator { | @@ -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 | private <T, V> FutureCallback<T> getCallback(FutureCallback<ValidationResult> callback, Function<T, ValidationResult<V>> transformer) { | 361 | private <T, V> FutureCallback<T> getCallback(FutureCallback<ValidationResult> callback, Function<T, ValidationResult<V>> transformer) { |
332 | return new FutureCallback<T>() { | 362 | return new FutureCallback<T>() { |
333 | @Override | 363 | @Override |
@@ -37,6 +37,7 @@ public class CustomerUserPermissions extends AbstractPermissions { | @@ -37,6 +37,7 @@ public class CustomerUserPermissions extends AbstractPermissions { | ||
37 | put(Resource.CUSTOMER, customerPermissionChecker); | 37 | put(Resource.CUSTOMER, customerPermissionChecker); |
38 | put(Resource.DASHBOARD, customerDashboardPermissionChecker); | 38 | put(Resource.DASHBOARD, customerDashboardPermissionChecker); |
39 | put(Resource.ENTITY_VIEW, customerEntityPermissionChecker); | 39 | put(Resource.ENTITY_VIEW, customerEntityPermissionChecker); |
40 | + put(Resource.EDGE, customerEntityPermissionChecker); | ||
40 | put(Resource.USER, userPermissionChecker); | 41 | put(Resource.USER, userPermissionChecker); |
41 | put(Resource.WIDGETS_BUNDLE, widgetsPermissionChecker); | 42 | put(Resource.WIDGETS_BUNDLE, widgetsPermissionChecker); |
42 | put(Resource.WIDGET_TYPE, widgetsPermissionChecker); | 43 | put(Resource.WIDGET_TYPE, widgetsPermissionChecker); |
@@ -18,6 +18,7 @@ package org.thingsboard.server.service.security.permission; | @@ -18,6 +18,7 @@ package org.thingsboard.server.service.security.permission; | ||
18 | public enum Operation { | 18 | public enum Operation { |
19 | 19 | ||
20 | ALL, CREATE, READ, WRITE, DELETE, ASSIGN_TO_CUSTOMER, UNASSIGN_FROM_CUSTOMER, RPC_CALL, | 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,6 +27,7 @@ public enum Resource { | ||
27 | CUSTOMER(EntityType.CUSTOMER), | 27 | CUSTOMER(EntityType.CUSTOMER), |
28 | DASHBOARD(EntityType.DASHBOARD), | 28 | DASHBOARD(EntityType.DASHBOARD), |
29 | ENTITY_VIEW(EntityType.ENTITY_VIEW), | 29 | ENTITY_VIEW(EntityType.ENTITY_VIEW), |
30 | + EDGE(EntityType.EDGE), | ||
30 | TENANT(EntityType.TENANT), | 31 | TENANT(EntityType.TENANT), |
31 | RULE_CHAIN(EntityType.RULE_CHAIN), | 32 | RULE_CHAIN(EntityType.RULE_CHAIN), |
32 | USER(EntityType.USER), | 33 | USER(EntityType.USER), |
@@ -37,6 +37,7 @@ public class TenantAdminPermissions extends AbstractPermissions { | @@ -37,6 +37,7 @@ public class TenantAdminPermissions extends AbstractPermissions { | ||
37 | put(Resource.CUSTOMER, tenantEntityPermissionChecker); | 37 | put(Resource.CUSTOMER, tenantEntityPermissionChecker); |
38 | put(Resource.DASHBOARD, tenantEntityPermissionChecker); | 38 | put(Resource.DASHBOARD, tenantEntityPermissionChecker); |
39 | put(Resource.ENTITY_VIEW, tenantEntityPermissionChecker); | 39 | put(Resource.ENTITY_VIEW, tenantEntityPermissionChecker); |
40 | + put(Resource.EDGE, tenantEntityPermissionChecker); | ||
40 | put(Resource.TENANT, tenantPermissionChecker); | 41 | put(Resource.TENANT, tenantPermissionChecker); |
41 | put(Resource.RULE_CHAIN, tenantEntityPermissionChecker); | 42 | put(Resource.RULE_CHAIN, tenantEntityPermissionChecker); |
42 | put(Resource.USER, userPermissionChecker); | 43 | put(Resource.USER, userPermissionChecker); |
@@ -26,6 +26,7 @@ | @@ -26,6 +26,7 @@ | ||
26 | </appender> | 26 | </appender> |
27 | 27 | ||
28 | <logger name="org.thingsboard.server" level="INFO" /> | 28 | <logger name="org.thingsboard.server" level="INFO" /> |
29 | + <logger name="org.thingsboard.server.service.edge" level="TRACE" /> | ||
29 | <logger name="akka" level="INFO" /> | 30 | <logger name="akka" level="INFO" /> |
30 | 31 | ||
31 | <root level="INFO"> | 32 | <root level="INFO"> |
@@ -294,6 +294,9 @@ caffeine: | @@ -294,6 +294,9 @@ caffeine: | ||
294 | securitySettings: | 294 | securitySettings: |
295 | timeToLiveInMinutes: 1440 | 295 | timeToLiveInMinutes: 1440 |
296 | maxSize: 1 | 296 | maxSize: 1 |
297 | + edges: | ||
298 | + timeToLiveInMinutes: 1440 | ||
299 | + maxSize: 100000 | ||
297 | 300 | ||
298 | redis: | 301 | redis: |
299 | # standalone or cluster | 302 | # standalone or cluster |
@@ -558,6 +561,17 @@ transport: | @@ -558,6 +561,17 @@ transport: | ||
558 | bind_port: "${COAP_BIND_PORT:5683}" | 561 | bind_port: "${COAP_BIND_PORT:5683}" |
559 | timeout: "${COAP_TIMEOUT:10000}" | 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 | swagger: | 575 | swagger: |
562 | api_path_regex: "${SWAGGER_API_PATH_REGEX:/api.*}" | 576 | api_path_regex: "${SWAGGER_API_PATH_REGEX:/api.*}" |
563 | security_path_regex: "${SWAGGER_SECURITY_PATH_REGEX:/api.*}" | 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 | +} |
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 | +} |
@@ -22,6 +22,7 @@ import org.thingsboard.server.common.data.asset.AssetInfo; | @@ -22,6 +22,7 @@ import org.thingsboard.server.common.data.asset.AssetInfo; | ||
22 | import org.thingsboard.server.common.data.asset.AssetSearchQuery; | 22 | import org.thingsboard.server.common.data.asset.AssetSearchQuery; |
23 | import org.thingsboard.server.common.data.id.AssetId; | 23 | import org.thingsboard.server.common.data.id.AssetId; |
24 | import org.thingsboard.server.common.data.id.CustomerId; | 24 | import org.thingsboard.server.common.data.id.CustomerId; |
25 | +import org.thingsboard.server.common.data.id.EdgeId; | ||
25 | import org.thingsboard.server.common.data.id.TenantId; | 26 | import org.thingsboard.server.common.data.id.TenantId; |
26 | import org.thingsboard.server.common.data.page.PageData; | 27 | import org.thingsboard.server.common.data.page.PageData; |
27 | import org.thingsboard.server.common.data.page.PageLink; | 28 | import org.thingsboard.server.common.data.page.PageLink; |
@@ -74,4 +75,12 @@ public interface AssetService { | @@ -74,4 +75,12 @@ public interface AssetService { | ||
74 | ListenableFuture<List<Asset>> findAssetsByQuery(TenantId tenantId, AssetSearchQuery query); | 75 | ListenableFuture<List<Asset>> findAssetsByQuery(TenantId tenantId, AssetSearchQuery query); |
75 | 76 | ||
76 | ListenableFuture<List<EntitySubtype>> findAssetTypesByTenantId(TenantId tenantId); | 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,6 +20,7 @@ import org.thingsboard.server.common.data.Dashboard; | ||
20 | import org.thingsboard.server.common.data.DashboardInfo; | 20 | import org.thingsboard.server.common.data.DashboardInfo; |
21 | import org.thingsboard.server.common.data.id.CustomerId; | 21 | import org.thingsboard.server.common.data.id.CustomerId; |
22 | import org.thingsboard.server.common.data.id.DashboardId; | 22 | import org.thingsboard.server.common.data.id.DashboardId; |
23 | +import org.thingsboard.server.common.data.id.EdgeId; | ||
23 | import org.thingsboard.server.common.data.id.TenantId; | 24 | import org.thingsboard.server.common.data.id.TenantId; |
24 | import org.thingsboard.server.common.data.page.PageData; | 25 | import org.thingsboard.server.common.data.page.PageData; |
25 | import org.thingsboard.server.common.data.page.PageLink; | 26 | import org.thingsboard.server.common.data.page.PageLink; |
@@ -53,4 +54,13 @@ public interface DashboardService { | @@ -53,4 +54,13 @@ public interface DashboardService { | ||
53 | 54 | ||
54 | void updateCustomerDashboards(TenantId tenantId, CustomerId customerId); | 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,6 +22,7 @@ import org.thingsboard.server.common.data.EntitySubtype; | ||
22 | import org.thingsboard.server.common.data.device.DeviceSearchQuery; | 22 | import org.thingsboard.server.common.data.device.DeviceSearchQuery; |
23 | import org.thingsboard.server.common.data.id.CustomerId; | 23 | import org.thingsboard.server.common.data.id.CustomerId; |
24 | import org.thingsboard.server.common.data.id.DeviceId; | 24 | import org.thingsboard.server.common.data.id.DeviceId; |
25 | +import org.thingsboard.server.common.data.id.EdgeId; | ||
25 | import org.thingsboard.server.common.data.id.TenantId; | 26 | import org.thingsboard.server.common.data.id.TenantId; |
26 | import org.thingsboard.server.common.data.page.PageData; | 27 | import org.thingsboard.server.common.data.page.PageData; |
27 | import org.thingsboard.server.common.data.page.PageLink; | 28 | import org.thingsboard.server.common.data.page.PageLink; |
@@ -76,4 +77,11 @@ public interface DeviceService { | @@ -76,4 +77,11 @@ public interface DeviceService { | ||
76 | 77 | ||
77 | ListenableFuture<List<EntitySubtype>> findDeviceTypesByTenantId(TenantId tenantId); | 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,9 +19,9 @@ import com.google.common.util.concurrent.ListenableFuture; | ||
19 | import org.thingsboard.server.common.data.EntitySubtype; | 19 | import org.thingsboard.server.common.data.EntitySubtype; |
20 | import org.thingsboard.server.common.data.EntityView; | 20 | import org.thingsboard.server.common.data.EntityView; |
21 | import org.thingsboard.server.common.data.EntityViewInfo; | 21 | import org.thingsboard.server.common.data.EntityViewInfo; |
22 | -import org.thingsboard.server.common.data.Tenant; | ||
23 | import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery; | 22 | import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery; |
24 | import org.thingsboard.server.common.data.id.CustomerId; | 23 | import org.thingsboard.server.common.data.id.CustomerId; |
24 | +import org.thingsboard.server.common.data.id.EdgeId; | ||
25 | import org.thingsboard.server.common.data.id.EntityId; | 25 | import org.thingsboard.server.common.data.id.EntityId; |
26 | import org.thingsboard.server.common.data.id.EntityViewId; | 26 | import org.thingsboard.server.common.data.id.EntityViewId; |
27 | import org.thingsboard.server.common.data.id.TenantId; | 27 | import org.thingsboard.server.common.data.id.TenantId; |
@@ -76,4 +76,12 @@ public interface EntityViewService { | @@ -76,4 +76,12 @@ public interface EntityViewService { | ||
76 | void deleteEntityViewsByTenantId(TenantId tenantId); | 76 | void deleteEntityViewsByTenantId(TenantId tenantId); |
77 | 77 | ||
78 | ListenableFuture<List<EntitySubtype>> findEntityViewTypesByTenantId(TenantId tenantId); | 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,14 +16,17 @@ | ||
16 | package org.thingsboard.server.dao.rule; | 16 | package org.thingsboard.server.dao.rule; |
17 | 17 | ||
18 | import com.google.common.util.concurrent.ListenableFuture; | 18 | import com.google.common.util.concurrent.ListenableFuture; |
19 | +import org.thingsboard.server.common.data.id.EdgeId; | ||
19 | import org.thingsboard.server.common.data.id.RuleChainId; | 20 | import org.thingsboard.server.common.data.id.RuleChainId; |
20 | import org.thingsboard.server.common.data.id.RuleNodeId; | 21 | import org.thingsboard.server.common.data.id.RuleNodeId; |
21 | import org.thingsboard.server.common.data.id.TenantId; | 22 | import org.thingsboard.server.common.data.id.TenantId; |
22 | import org.thingsboard.server.common.data.page.PageData; | 23 | import org.thingsboard.server.common.data.page.PageData; |
23 | import org.thingsboard.server.common.data.page.PageLink; | 24 | import org.thingsboard.server.common.data.page.PageLink; |
25 | +import org.thingsboard.server.common.data.page.TimePageLink; | ||
24 | import org.thingsboard.server.common.data.relation.EntityRelation; | 26 | import org.thingsboard.server.common.data.relation.EntityRelation; |
25 | import org.thingsboard.server.common.data.rule.RuleChain; | 27 | import org.thingsboard.server.common.data.rule.RuleChain; |
26 | import org.thingsboard.server.common.data.rule.RuleChainMetaData; | 28 | import org.thingsboard.server.common.data.rule.RuleChainMetaData; |
29 | +import org.thingsboard.server.common.data.rule.RuleChainType; | ||
27 | import org.thingsboard.server.common.data.rule.RuleNode; | 30 | import org.thingsboard.server.common.data.rule.RuleNode; |
28 | 31 | ||
29 | import java.util.List; | 32 | import java.util.List; |
@@ -59,8 +62,19 @@ public interface RuleChainService { | @@ -59,8 +62,19 @@ public interface RuleChainService { | ||
59 | 62 | ||
60 | PageData<RuleChain> findTenantRuleChains(TenantId tenantId, PageLink pageLink); | 63 | PageData<RuleChain> findTenantRuleChains(TenantId tenantId, PageLink pageLink); |
61 | 64 | ||
65 | + PageData<RuleChain> findTenantRuleChainsByType(TenantId tenantId, RuleChainType type, PageLink pageLink); | ||
66 | + | ||
62 | void deleteRuleChainById(TenantId tenantId, RuleChainId ruleChainId); | 67 | void deleteRuleChainById(TenantId tenantId, RuleChainId ruleChainId); |
63 | 68 | ||
64 | void deleteRuleChainsByTenantId(TenantId tenantId); | 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,6 +22,7 @@ public class CacheConstants { | ||
22 | public static final String SESSIONS_CACHE = "sessions"; | 22 | public static final String SESSIONS_CACHE = "sessions"; |
23 | public static final String ASSET_CACHE = "assets"; | 23 | public static final String ASSET_CACHE = "assets"; |
24 | public static final String ENTITY_VIEW_CACHE = "entityViews"; | 24 | public static final String ENTITY_VIEW_CACHE = "entityViews"; |
25 | + public static final String EDGE_CACHE = "edges"; | ||
25 | public static final String CLAIM_DEVICES_CACHE = "claimDevices"; | 26 | public static final String CLAIM_DEVICES_CACHE = "claimDevices"; |
26 | public static final String SECURITY_SETTINGS_CACHE = "securitySettings"; | 27 | public static final String SECURITY_SETTINGS_CACHE = "securitySettings"; |
27 | } | 28 | } |
@@ -16,8 +16,12 @@ | @@ -16,8 +16,12 @@ | ||
16 | package org.thingsboard.server.common.data; | 16 | package org.thingsboard.server.common.data; |
17 | 17 | ||
18 | import com.fasterxml.jackson.annotation.JsonProperty; | 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 | import org.thingsboard.server.common.data.id.CustomerId; | 22 | import org.thingsboard.server.common.data.id.CustomerId; |
20 | import org.thingsboard.server.common.data.id.DashboardId; | 23 | import org.thingsboard.server.common.data.id.DashboardId; |
24 | +import org.thingsboard.server.common.data.id.EdgeId; | ||
21 | import org.thingsboard.server.common.data.id.TenantId; | 25 | import org.thingsboard.server.common.data.id.TenantId; |
22 | 26 | ||
23 | import java.util.*; | 27 | import java.util.*; |
@@ -28,6 +32,9 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa | @@ -28,6 +32,9 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa | ||
28 | private String title; | 32 | private String title; |
29 | private Set<ShortCustomerInfo> assignedCustomers; | 33 | private Set<ShortCustomerInfo> assignedCustomers; |
30 | 34 | ||
35 | + @Getter @Setter | ||
36 | + private Set<ShortEdgeInfo> assignedEdges; | ||
37 | + | ||
31 | public DashboardInfo() { | 38 | public DashboardInfo() { |
32 | super(); | 39 | super(); |
33 | } | 40 | } |
@@ -116,6 +123,55 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa | @@ -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 | @Override | 175 | @Override |
120 | @JsonProperty(access = JsonProperty.Access.READ_ONLY) | 176 | @JsonProperty(access = JsonProperty.Access.READ_ONLY) |
121 | public String getName() { | 177 | public String getName() { |
@@ -57,6 +57,8 @@ public class DataConstants { | @@ -57,6 +57,8 @@ public class DataConstants { | ||
57 | public static final String ATTRIBUTES_DELETED = "ATTRIBUTES_DELETED"; | 57 | public static final String ATTRIBUTES_DELETED = "ATTRIBUTES_DELETED"; |
58 | public static final String ALARM_ACK = "ALARM_ACK"; | 58 | public static final String ALARM_ACK = "ALARM_ACK"; |
59 | public static final String ALARM_CLEAR = "ALARM_CLEAR"; | 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 | public static final String RPC_CALL_FROM_SERVER_TO_DEVICE = "RPC_CALL_FROM_SERVER_TO_DEVICE"; | 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,4 +66,5 @@ public class DataConstants { | ||
64 | public static final String SECRET_KEY_FIELD_NAME = "secretKey"; | 66 | public static final String SECRET_KEY_FIELD_NAME = "secretKey"; |
65 | public static final String DURATION_MS_FIELD_NAME = "durationMs"; | 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,6 +18,7 @@ package org.thingsboard.server.common.data; | ||
18 | import lombok.EqualsAndHashCode; | 18 | import lombok.EqualsAndHashCode; |
19 | import org.thingsboard.server.common.data.id.CustomerId; | 19 | import org.thingsboard.server.common.data.id.CustomerId; |
20 | import org.thingsboard.server.common.data.id.DeviceId; | 20 | import org.thingsboard.server.common.data.id.DeviceId; |
21 | +import org.thingsboard.server.common.data.id.EdgeId; | ||
21 | import org.thingsboard.server.common.data.id.TenantId; | 22 | import org.thingsboard.server.common.data.id.TenantId; |
22 | 23 | ||
23 | @EqualsAndHashCode(callSuper = true) | 24 | @EqualsAndHashCode(callSuper = true) |
@@ -27,6 +28,7 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen | @@ -27,6 +28,7 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen | ||
27 | 28 | ||
28 | private TenantId tenantId; | 29 | private TenantId tenantId; |
29 | private CustomerId customerId; | 30 | private CustomerId customerId; |
31 | + private EdgeId edgeId; | ||
30 | private String name; | 32 | private String name; |
31 | private String type; | 33 | private String type; |
32 | private String label; | 34 | private String label; |
@@ -46,6 +48,7 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen | @@ -46,6 +48,7 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen | ||
46 | this.name = device.getName(); | 48 | this.name = device.getName(); |
47 | this.type = device.getType(); | 49 | this.type = device.getType(); |
48 | this.label = device.getLabel(); | 50 | this.label = device.getLabel(); |
51 | + this.edgeId = device.getEdgeId(); | ||
49 | } | 52 | } |
50 | 53 | ||
51 | public TenantId getTenantId() { | 54 | public TenantId getTenantId() { |
@@ -64,6 +67,14 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen | @@ -64,6 +67,14 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen | ||
64 | this.customerId = customerId; | 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 | @Override | 78 | @Override |
68 | public String getName() { | 79 | public String getName() { |
69 | return name; | 80 | return name; |
@@ -101,6 +112,8 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen | @@ -101,6 +112,8 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen | ||
101 | builder.append(tenantId); | 112 | builder.append(tenantId); |
102 | builder.append(", customerId="); | 113 | builder.append(", customerId="); |
103 | builder.append(customerId); | 114 | builder.append(customerId); |
115 | + builder.append(", edgeId="); | ||
116 | + builder.append(edgeId); | ||
104 | builder.append(", name="); | 117 | builder.append(", name="); |
105 | builder.append(name); | 118 | builder.append(name); |
106 | builder.append(", type="); | 119 | builder.append(", type="); |
@@ -19,5 +19,5 @@ package org.thingsboard.server.common.data; | @@ -19,5 +19,5 @@ package org.thingsboard.server.common.data; | ||
19 | * @author Andrew Shvayka | 19 | * @author Andrew Shvayka |
20 | */ | 20 | */ |
21 | public enum EntityType { | 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,6 +19,7 @@ import lombok.AllArgsConstructor; | ||
19 | import lombok.Data; | 19 | import lombok.Data; |
20 | import lombok.EqualsAndHashCode; | 20 | import lombok.EqualsAndHashCode; |
21 | import org.thingsboard.server.common.data.id.CustomerId; | 21 | import org.thingsboard.server.common.data.id.CustomerId; |
22 | +import org.thingsboard.server.common.data.id.EdgeId; | ||
22 | import org.thingsboard.server.common.data.id.EntityId; | 23 | import org.thingsboard.server.common.data.id.EntityId; |
23 | import org.thingsboard.server.common.data.id.EntityViewId; | 24 | import org.thingsboard.server.common.data.id.EntityViewId; |
24 | import org.thingsboard.server.common.data.id.TenantId; | 25 | import org.thingsboard.server.common.data.id.TenantId; |
@@ -39,6 +40,7 @@ public class EntityView extends SearchTextBasedWithAdditionalInfo<EntityViewId> | @@ -39,6 +40,7 @@ public class EntityView extends SearchTextBasedWithAdditionalInfo<EntityViewId> | ||
39 | private EntityId entityId; | 40 | private EntityId entityId; |
40 | private TenantId tenantId; | 41 | private TenantId tenantId; |
41 | private CustomerId customerId; | 42 | private CustomerId customerId; |
43 | + private EdgeId edgeId; | ||
42 | private String name; | 44 | private String name; |
43 | private String type; | 45 | private String type; |
44 | private TelemetryEntityView keys; | 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 | +} |
@@ -20,6 +20,7 @@ import lombok.EqualsAndHashCode; | @@ -20,6 +20,7 @@ import lombok.EqualsAndHashCode; | ||
20 | import org.thingsboard.server.common.data.*; | 20 | import org.thingsboard.server.common.data.*; |
21 | import org.thingsboard.server.common.data.id.AssetId; | 21 | import org.thingsboard.server.common.data.id.AssetId; |
22 | import org.thingsboard.server.common.data.id.CustomerId; | 22 | import org.thingsboard.server.common.data.id.CustomerId; |
23 | +import org.thingsboard.server.common.data.id.EdgeId; | ||
23 | import org.thingsboard.server.common.data.id.TenantId; | 24 | import org.thingsboard.server.common.data.id.TenantId; |
24 | 25 | ||
25 | @EqualsAndHashCode(callSuper = true) | 26 | @EqualsAndHashCode(callSuper = true) |
@@ -29,6 +30,7 @@ public class Asset extends SearchTextBasedWithAdditionalInfo<AssetId> implements | @@ -29,6 +30,7 @@ public class Asset extends SearchTextBasedWithAdditionalInfo<AssetId> implements | ||
29 | 30 | ||
30 | private TenantId tenantId; | 31 | private TenantId tenantId; |
31 | private CustomerId customerId; | 32 | private CustomerId customerId; |
33 | + private EdgeId edgeId; | ||
32 | private String name; | 34 | private String name; |
33 | private String type; | 35 | private String type; |
34 | private String label; | 36 | private String label; |
@@ -45,6 +47,7 @@ public class Asset extends SearchTextBasedWithAdditionalInfo<AssetId> implements | @@ -45,6 +47,7 @@ public class Asset extends SearchTextBasedWithAdditionalInfo<AssetId> implements | ||
45 | super(asset); | 47 | super(asset); |
46 | this.tenantId = asset.getTenantId(); | 48 | this.tenantId = asset.getTenantId(); |
47 | this.customerId = asset.getCustomerId(); | 49 | this.customerId = asset.getCustomerId(); |
50 | + this.edgeId = asset.getEdgeId(); | ||
48 | this.name = asset.getName(); | 51 | this.name = asset.getName(); |
49 | this.type = asset.getType(); | 52 | this.type = asset.getType(); |
50 | this.label = asset.getLabel(); | 53 | this.label = asset.getLabel(); |
@@ -66,6 +69,14 @@ public class Asset extends SearchTextBasedWithAdditionalInfo<AssetId> implements | @@ -66,6 +69,14 @@ public class Asset extends SearchTextBasedWithAdditionalInfo<AssetId> implements | ||
66 | this.customerId = customerId; | 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 | @Override | 80 | @Override |
70 | public String getName() { | 81 | public String getName() { |
71 | return name; | 82 | return name; |
@@ -103,6 +114,8 @@ public class Asset extends SearchTextBasedWithAdditionalInfo<AssetId> implements | @@ -103,6 +114,8 @@ public class Asset extends SearchTextBasedWithAdditionalInfo<AssetId> implements | ||
103 | builder.append(tenantId); | 114 | builder.append(tenantId); |
104 | builder.append(", customerId="); | 115 | builder.append(", customerId="); |
105 | builder.append(customerId); | 116 | builder.append(customerId); |
117 | + builder.append(", edgeId="); | ||
118 | + builder.append(edgeId); | ||
106 | builder.append(", name="); | 119 | builder.append(", name="); |
107 | builder.append(name); | 120 | builder.append(name); |
108 | builder.append(", type="); | 121 | builder.append(", type="); |
@@ -40,7 +40,9 @@ public enum ActionType { | @@ -40,7 +40,9 @@ public enum ActionType { | ||
40 | ALARM_CLEAR(false), | 40 | ALARM_CLEAR(false), |
41 | LOGIN(false), | 41 | LOGIN(false), |
42 | LOGOUT(false), | 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 | private final boolean isRead; | 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 | +} |
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 | +} |
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 | +} |
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 | +} |
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 | +} |
@@ -63,6 +63,8 @@ public class EntityIdFactory { | @@ -63,6 +63,8 @@ public class EntityIdFactory { | ||
63 | return new WidgetsBundleId(uuid); | 63 | return new WidgetsBundleId(uuid); |
64 | case WIDGET_TYPE: | 64 | case WIDGET_TYPE: |
65 | return new WidgetTypeId(uuid); | 65 | return new WidgetTypeId(uuid); |
66 | + case EDGE: | ||
67 | + return new EdgeId(uuid); | ||
66 | } | 68 | } |
67 | throw new IllegalArgumentException("EntityType " + type + " is not supported!"); | 69 | throw new IllegalArgumentException("EntityType " + type + " is not supported!"); |
68 | } | 70 | } |
@@ -32,6 +32,7 @@ public class EntityRelation implements Serializable { | @@ -32,6 +32,7 @@ public class EntityRelation implements Serializable { | ||
32 | 32 | ||
33 | private static final long serialVersionUID = 2807343040519543363L; | 33 | private static final long serialVersionUID = 2807343040519543363L; |
34 | 34 | ||
35 | + public static final String EDGE_TYPE = "ManagedByEdge"; | ||
35 | public static final String CONTAINS_TYPE = "Contains"; | 36 | public static final String CONTAINS_TYPE = "Contains"; |
36 | public static final String MANAGES_TYPE = "Manages"; | 37 | public static final String MANAGES_TYPE = "Manages"; |
37 | 38 |
@@ -23,10 +23,16 @@ import lombok.extern.slf4j.Slf4j; | @@ -23,10 +23,16 @@ import lombok.extern.slf4j.Slf4j; | ||
23 | import org.thingsboard.server.common.data.HasName; | 23 | import org.thingsboard.server.common.data.HasName; |
24 | import org.thingsboard.server.common.data.HasTenantId; | 24 | import org.thingsboard.server.common.data.HasTenantId; |
25 | import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; | 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 | import org.thingsboard.server.common.data.id.RuleChainId; | 29 | import org.thingsboard.server.common.data.id.RuleChainId; |
27 | import org.thingsboard.server.common.data.id.RuleNodeId; | 30 | import org.thingsboard.server.common.data.id.RuleNodeId; |
28 | import org.thingsboard.server.common.data.id.TenantId; | 31 | import org.thingsboard.server.common.data.id.TenantId; |
29 | 32 | ||
33 | +import java.util.HashSet; | ||
34 | +import java.util.Set; | ||
35 | + | ||
30 | @Data | 36 | @Data |
31 | @EqualsAndHashCode(callSuper = true) | 37 | @EqualsAndHashCode(callSuper = true) |
32 | @Slf4j | 38 | @Slf4j |
@@ -36,13 +42,16 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo<RuleChainId> im | @@ -36,13 +42,16 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo<RuleChainId> im | ||
36 | 42 | ||
37 | private TenantId tenantId; | 43 | private TenantId tenantId; |
38 | private String name; | 44 | private String name; |
45 | + private RuleChainType type; | ||
39 | private RuleNodeId firstRuleNodeId; | 46 | private RuleNodeId firstRuleNodeId; |
40 | private boolean root; | 47 | private boolean root; |
41 | private boolean debugMode; | 48 | private boolean debugMode; |
42 | private transient JsonNode configuration; | 49 | private transient JsonNode configuration; |
50 | + private Set<ShortEdgeInfo> assignedEdges; | ||
43 | @JsonIgnore | 51 | @JsonIgnore |
44 | private byte[] configurationBytes; | 52 | private byte[] configurationBytes; |
45 | 53 | ||
54 | + | ||
46 | public RuleChain() { | 55 | public RuleChain() { |
47 | super(); | 56 | super(); |
48 | } | 57 | } |
@@ -55,8 +64,10 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo<RuleChainId> im | @@ -55,8 +64,10 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo<RuleChainId> im | ||
55 | super(ruleChain); | 64 | super(ruleChain); |
56 | this.tenantId = ruleChain.getTenantId(); | 65 | this.tenantId = ruleChain.getTenantId(); |
57 | this.name = ruleChain.getName(); | 66 | this.name = ruleChain.getName(); |
67 | + this.type = ruleChain.getType(); | ||
58 | this.firstRuleNodeId = ruleChain.getFirstRuleNodeId(); | 68 | this.firstRuleNodeId = ruleChain.getFirstRuleNodeId(); |
59 | this.root = ruleChain.isRoot(); | 69 | this.root = ruleChain.isRoot(); |
70 | + this.assignedEdges = ruleChain.getAssignedEdges(); | ||
60 | this.setConfiguration(ruleChain.getConfiguration()); | 71 | this.setConfiguration(ruleChain.getConfiguration()); |
61 | } | 72 | } |
62 | 73 | ||
@@ -78,4 +89,52 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo<RuleChainId> im | @@ -78,4 +89,52 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo<RuleChainId> im | ||
78 | setJson(data, json -> this.configuration = json, bytes -> this.configurationBytes = bytes); | 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 | +} |
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> |
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 | +} |
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 | +} |
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 | +} |