Showing
85 changed files
with
3761 additions
and
383 deletions
Too many changes to show.
To preserve performance only 85 of 225 files are displayed.
@@ -43,7 +43,8 @@ | @@ -43,7 +43,8 @@ | ||
43 | "name": "Save Client Attributes", | 43 | "name": "Save Client Attributes", |
44 | "debugMode": false, | 44 | "debugMode": false, |
45 | "configuration": { | 45 | "configuration": { |
46 | - "scope": "CLIENT_SCOPE" | 46 | + "scope": "CLIENT_SCOPE", |
47 | + "notifyDevice": "false" | ||
47 | } | 48 | } |
48 | }, | 49 | }, |
49 | { | 50 | { |
@@ -8,7 +8,7 @@ | @@ -8,7 +8,7 @@ | ||
8 | "configuration": null | 8 | "configuration": null |
9 | }, | 9 | }, |
10 | "metadata": { | 10 | "metadata": { |
11 | - "firstNodeIndex": 2, | 11 | + "firstNodeIndex": 6, |
12 | "nodes": [ | 12 | "nodes": [ |
13 | { | 13 | { |
14 | "additionalInfo": { | 14 | "additionalInfo": { |
@@ -31,7 +31,8 @@ | @@ -31,7 +31,8 @@ | ||
31 | "name": "Save Client Attributes", | 31 | "name": "Save Client Attributes", |
32 | "debugMode": false, | 32 | "debugMode": false, |
33 | "configuration": { | 33 | "configuration": { |
34 | - "scope": "CLIENT_SCOPE" | 34 | + "scope": "CLIENT_SCOPE", |
35 | + "notifyDevice": "false" | ||
35 | } | 36 | } |
36 | }, | 37 | }, |
37 | { | 38 | { |
@@ -81,10 +82,29 @@ | @@ -81,10 +82,29 @@ | ||
81 | "configuration": { | 82 | "configuration": { |
82 | "timeoutInSeconds": 60 | 83 | "timeoutInSeconds": 60 |
83 | } | 84 | } |
85 | + }, | ||
86 | + { | ||
87 | + "additionalInfo": { | ||
88 | + "description": "Process incoming messages from devices with the alarm rules defined in the device profile. Dispatch all incoming messages with \"Success\" relation type.", | ||
89 | + "layoutX": 204, | ||
90 | + "layoutY": 240 | ||
91 | + }, | ||
92 | + "type": "org.thingsboard.rule.engine.profile.TbDeviceProfileNode", | ||
93 | + "name": "Device Profile Node", | ||
94 | + "debugMode": false, | ||
95 | + "configuration": { | ||
96 | + "persistAlarmRulesState": false, | ||
97 | + "fetchAlarmRulesStateOnStart": false | ||
98 | + } | ||
84 | } | 99 | } |
85 | ], | 100 | ], |
86 | "connections": [ | 101 | "connections": [ |
87 | { | 102 | { |
103 | + "fromIndex": 6, | ||
104 | + "toIndex": 2, | ||
105 | + "type": "Success" | ||
106 | + }, | ||
107 | + { | ||
88 | "fromIndex": 2, | 108 | "fromIndex": 2, |
89 | "toIndex": 4, | 109 | "toIndex": 4, |
90 | "type": "Other" | 110 | "type": "Other" |
application/src/main/data/upgrade/3.1.1/schema_update_after.sql
renamed from
application/src/main/data/upgrade/3.1.2/schema_update_after.sql
application/src/main/data/upgrade/3.1.1/schema_update_before.sql
renamed from
application/src/main/data/upgrade/3.1.2/schema_update_before.sql
@@ -58,6 +58,7 @@ import org.thingsboard.server.dao.event.EventService; | @@ -58,6 +58,7 @@ import org.thingsboard.server.dao.event.EventService; | ||
58 | import org.thingsboard.server.dao.nosql.CassandraBufferedRateExecutor; | 58 | import org.thingsboard.server.dao.nosql.CassandraBufferedRateExecutor; |
59 | import org.thingsboard.server.dao.relation.RelationService; | 59 | import org.thingsboard.server.dao.relation.RelationService; |
60 | import org.thingsboard.server.dao.rule.RuleChainService; | 60 | import org.thingsboard.server.dao.rule.RuleChainService; |
61 | +import org.thingsboard.server.dao.rule.RuleNodeStateService; | ||
61 | import org.thingsboard.server.dao.tenant.TenantProfileService; | 62 | import org.thingsboard.server.dao.tenant.TenantProfileService; |
62 | import org.thingsboard.server.dao.tenant.TenantService; | 63 | import org.thingsboard.server.dao.tenant.TenantService; |
63 | import org.thingsboard.server.dao.timeseries.TimeseriesService; | 64 | import org.thingsboard.server.dao.timeseries.TimeseriesService; |
@@ -159,6 +160,10 @@ public class ActorSystemContext { | @@ -159,6 +160,10 @@ public class ActorSystemContext { | ||
159 | private RuleChainService ruleChainService; | 160 | private RuleChainService ruleChainService; |
160 | 161 | ||
161 | @Autowired | 162 | @Autowired |
163 | + @Getter | ||
164 | + private RuleNodeStateService ruleNodeStateService; | ||
165 | + | ||
166 | + @Autowired | ||
162 | private PartitionService partitionService; | 167 | private PartitionService partitionService; |
163 | 168 | ||
164 | @Autowired | 169 | @Autowired |
@@ -40,13 +40,15 @@ import org.thingsboard.server.common.data.id.EntityId; | @@ -40,13 +40,15 @@ import org.thingsboard.server.common.data.id.EntityId; | ||
40 | import org.thingsboard.server.common.data.id.RuleChainId; | 40 | import org.thingsboard.server.common.data.id.RuleChainId; |
41 | import org.thingsboard.server.common.data.id.RuleNodeId; | 41 | import org.thingsboard.server.common.data.id.RuleNodeId; |
42 | import org.thingsboard.server.common.data.id.TenantId; | 42 | import org.thingsboard.server.common.data.id.TenantId; |
43 | +import org.thingsboard.server.common.data.page.PageData; | ||
44 | +import org.thingsboard.server.common.data.page.PageLink; | ||
43 | import org.thingsboard.server.common.data.rule.RuleNode; | 45 | import org.thingsboard.server.common.data.rule.RuleNode; |
46 | +import org.thingsboard.server.common.data.rule.RuleNodeState; | ||
44 | import org.thingsboard.server.common.msg.TbActorMsg; | 47 | import org.thingsboard.server.common.msg.TbActorMsg; |
45 | import org.thingsboard.server.common.msg.TbMsg; | 48 | import org.thingsboard.server.common.msg.TbMsg; |
46 | import org.thingsboard.server.common.msg.TbMsgMetaData; | 49 | import org.thingsboard.server.common.msg.TbMsgMetaData; |
47 | import org.thingsboard.server.common.msg.queue.ServiceType; | 50 | import org.thingsboard.server.common.msg.queue.ServiceType; |
48 | import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; | 51 | import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; |
49 | -import org.thingsboard.server.dao.alarm.AlarmService; | ||
50 | import org.thingsboard.server.dao.asset.AssetService; | 52 | import org.thingsboard.server.dao.asset.AssetService; |
51 | import org.thingsboard.server.dao.attributes.AttributesService; | 53 | import org.thingsboard.server.dao.attributes.AttributesService; |
52 | import org.thingsboard.server.dao.cassandra.CassandraCluster; | 54 | import org.thingsboard.server.dao.cassandra.CassandraCluster; |
@@ -68,7 +70,6 @@ import org.thingsboard.server.service.script.RuleNodeJsScriptEngine; | @@ -68,7 +70,6 @@ import org.thingsboard.server.service.script.RuleNodeJsScriptEngine; | ||
68 | 70 | ||
69 | import java.util.Collections; | 71 | import java.util.Collections; |
70 | import java.util.Set; | 72 | import java.util.Set; |
71 | -import java.util.concurrent.TimeUnit; | ||
72 | import java.util.function.Consumer; | 73 | import java.util.function.Consumer; |
73 | 74 | ||
74 | /** | 75 | /** |
@@ -124,7 +125,7 @@ class DefaultTbContext implements TbContext { | @@ -124,7 +125,7 @@ class DefaultTbContext implements TbContext { | ||
124 | 125 | ||
125 | @Override | 126 | @Override |
126 | public void enqueue(TbMsg tbMsg, String queueName, Runnable onSuccess, Consumer<Throwable> onFailure) { | 127 | public void enqueue(TbMsg tbMsg, String queueName, Runnable onSuccess, Consumer<Throwable> onFailure) { |
127 | - TopicPartitionInfo tpi = mainCtx.resolve(ServiceType.TB_RULE_ENGINE, queueName, getTenantId(), tbMsg.getOriginator()); | 128 | + TopicPartitionInfo tpi = resolvePartition(tbMsg, queueName); |
128 | enqueue(tpi, tbMsg, onFailure, onSuccess); | 129 | enqueue(tpi, tbMsg, onFailure, onSuccess); |
129 | } | 130 | } |
130 | 131 | ||
@@ -141,46 +142,54 @@ class DefaultTbContext implements TbContext { | @@ -141,46 +142,54 @@ class DefaultTbContext implements TbContext { | ||
141 | 142 | ||
142 | @Override | 143 | @Override |
143 | public void enqueueForTellFailure(TbMsg tbMsg, String failureMessage) { | 144 | public void enqueueForTellFailure(TbMsg tbMsg, String failureMessage) { |
144 | - TopicPartitionInfo tpi = mainCtx.resolve(ServiceType.TB_RULE_ENGINE, getTenantId(), tbMsg.getOriginator()); | 145 | + TopicPartitionInfo tpi = resolvePartition(tbMsg); |
145 | enqueueForTellNext(tpi, tbMsg, Collections.singleton(TbRelationTypes.FAILURE), failureMessage, null, null); | 146 | enqueueForTellNext(tpi, tbMsg, Collections.singleton(TbRelationTypes.FAILURE), failureMessage, null, null); |
146 | } | 147 | } |
147 | 148 | ||
148 | @Override | 149 | @Override |
149 | public void enqueueForTellNext(TbMsg tbMsg, String relationType) { | 150 | public void enqueueForTellNext(TbMsg tbMsg, String relationType) { |
150 | - TopicPartitionInfo tpi = mainCtx.resolve(ServiceType.TB_RULE_ENGINE, getTenantId(), tbMsg.getOriginator()); | 151 | + TopicPartitionInfo tpi = resolvePartition(tbMsg); |
151 | enqueueForTellNext(tpi, tbMsg, Collections.singleton(relationType), null, null, null); | 152 | enqueueForTellNext(tpi, tbMsg, Collections.singleton(relationType), null, null, null); |
152 | } | 153 | } |
153 | 154 | ||
154 | @Override | 155 | @Override |
155 | public void enqueueForTellNext(TbMsg tbMsg, Set<String> relationTypes) { | 156 | public void enqueueForTellNext(TbMsg tbMsg, Set<String> relationTypes) { |
156 | - TopicPartitionInfo tpi = mainCtx.resolve(ServiceType.TB_RULE_ENGINE, getTenantId(), tbMsg.getOriginator()); | 157 | + TopicPartitionInfo tpi = resolvePartition(tbMsg); |
157 | enqueueForTellNext(tpi, tbMsg, relationTypes, null, null, null); | 158 | enqueueForTellNext(tpi, tbMsg, relationTypes, null, null, null); |
158 | } | 159 | } |
159 | 160 | ||
160 | @Override | 161 | @Override |
161 | public void enqueueForTellNext(TbMsg tbMsg, String relationType, Runnable onSuccess, Consumer<Throwable> onFailure) { | 162 | public void enqueueForTellNext(TbMsg tbMsg, String relationType, Runnable onSuccess, Consumer<Throwable> onFailure) { |
162 | - TopicPartitionInfo tpi = mainCtx.resolve(ServiceType.TB_RULE_ENGINE, getTenantId(), tbMsg.getOriginator()); | 163 | + TopicPartitionInfo tpi = resolvePartition(tbMsg); |
163 | enqueueForTellNext(tpi, tbMsg, Collections.singleton(relationType), null, onSuccess, onFailure); | 164 | enqueueForTellNext(tpi, tbMsg, Collections.singleton(relationType), null, onSuccess, onFailure); |
164 | } | 165 | } |
165 | 166 | ||
166 | @Override | 167 | @Override |
167 | public void enqueueForTellNext(TbMsg tbMsg, Set<String> relationTypes, Runnable onSuccess, Consumer<Throwable> onFailure) { | 168 | public void enqueueForTellNext(TbMsg tbMsg, Set<String> relationTypes, Runnable onSuccess, Consumer<Throwable> onFailure) { |
168 | - TopicPartitionInfo tpi = mainCtx.resolve(ServiceType.TB_RULE_ENGINE, getTenantId(), tbMsg.getOriginator()); | 169 | + TopicPartitionInfo tpi = resolvePartition(tbMsg); |
169 | enqueueForTellNext(tpi, tbMsg, relationTypes, null, onSuccess, onFailure); | 170 | enqueueForTellNext(tpi, tbMsg, relationTypes, null, onSuccess, onFailure); |
170 | } | 171 | } |
171 | 172 | ||
172 | @Override | 173 | @Override |
173 | public void enqueueForTellNext(TbMsg tbMsg, String queueName, String relationType, Runnable onSuccess, Consumer<Throwable> onFailure) { | 174 | public void enqueueForTellNext(TbMsg tbMsg, String queueName, String relationType, Runnable onSuccess, Consumer<Throwable> onFailure) { |
174 | - TopicPartitionInfo tpi = mainCtx.resolve(ServiceType.TB_RULE_ENGINE, queueName, getTenantId(), tbMsg.getOriginator()); | 175 | + TopicPartitionInfo tpi = resolvePartition(tbMsg, queueName); |
175 | enqueueForTellNext(tpi, tbMsg, Collections.singleton(relationType), null, onSuccess, onFailure); | 176 | enqueueForTellNext(tpi, tbMsg, Collections.singleton(relationType), null, onSuccess, onFailure); |
176 | } | 177 | } |
177 | 178 | ||
178 | @Override | 179 | @Override |
179 | public void enqueueForTellNext(TbMsg tbMsg, String queueName, Set<String> relationTypes, Runnable onSuccess, Consumer<Throwable> onFailure) { | 180 | public void enqueueForTellNext(TbMsg tbMsg, String queueName, Set<String> relationTypes, Runnable onSuccess, Consumer<Throwable> onFailure) { |
180 | - TopicPartitionInfo tpi = mainCtx.resolve(ServiceType.TB_RULE_ENGINE, queueName, getTenantId(), tbMsg.getOriginator()); | 181 | + TopicPartitionInfo tpi = resolvePartition(tbMsg, queueName); |
181 | enqueueForTellNext(tpi, tbMsg, relationTypes, null, onSuccess, onFailure); | 182 | enqueueForTellNext(tpi, tbMsg, relationTypes, null, onSuccess, onFailure); |
182 | } | 183 | } |
183 | 184 | ||
185 | + private TopicPartitionInfo resolvePartition(TbMsg tbMsg, String queueName) { | ||
186 | + return mainCtx.resolve(ServiceType.TB_RULE_ENGINE, queueName, getTenantId(), tbMsg.getOriginator()); | ||
187 | + } | ||
188 | + | ||
189 | + private TopicPartitionInfo resolvePartition(TbMsg tbMsg) { | ||
190 | + return resolvePartition(tbMsg, tbMsg.getQueueName()); | ||
191 | + } | ||
192 | + | ||
184 | private void enqueueForTellNext(TopicPartitionInfo tpi, TbMsg source, Set<String> relationTypes, String failureMessage, Runnable onSuccess, Consumer<Throwable> onFailure) { | 193 | private void enqueueForTellNext(TopicPartitionInfo tpi, TbMsg source, Set<String> relationTypes, String failureMessage, Runnable onSuccess, Consumer<Throwable> onFailure) { |
185 | RuleChainId ruleChainId = nodeCtx.getSelf().getRuleChainId(); | 194 | RuleChainId ruleChainId = nodeCtx.getSelf().getRuleChainId(); |
186 | RuleNodeId ruleNodeId = nodeCtx.getSelf().getId(); | 195 | RuleNodeId ruleNodeId = nodeCtx.getSelf().getId(); |
@@ -430,6 +439,30 @@ class DefaultTbContext implements TbContext { | @@ -430,6 +439,30 @@ class DefaultTbContext implements TbContext { | ||
430 | return mainCtx.getRedisTemplate(); | 439 | return mainCtx.getRedisTemplate(); |
431 | } | 440 | } |
432 | 441 | ||
442 | + @Override | ||
443 | + public PageData<RuleNodeState> findRuleNodeStates(PageLink pageLink) { | ||
444 | + if (log.isDebugEnabled()) { | ||
445 | + log.debug("[{}][{}] Fetch Rule Node States.", getTenantId(), getSelfId()); | ||
446 | + } | ||
447 | + return mainCtx.getRuleNodeStateService().findByRuleNodeId(getTenantId(), getSelfId(), pageLink); | ||
448 | + } | ||
449 | + | ||
450 | + @Override | ||
451 | + public RuleNodeState findRuleNodeStateForEntity(EntityId entityId) { | ||
452 | + if (log.isDebugEnabled()) { | ||
453 | + log.debug("[{}][{}][{}] Fetch Rule Node State for entity.", getTenantId(), getSelfId(), entityId); | ||
454 | + } | ||
455 | + return mainCtx.getRuleNodeStateService().findByRuleNodeIdAndEntityId(getTenantId(), getSelfId(), entityId); | ||
456 | + } | ||
457 | + | ||
458 | + @Override | ||
459 | + public RuleNodeState saveRuleNodeState(RuleNodeState state) { | ||
460 | + if (log.isDebugEnabled()) { | ||
461 | + log.debug("[{}][{}][{}] Persist Rule Node State for entity: {}", getTenantId(), getSelfId(), state.getEntityId(), state.getStateData()); | ||
462 | + } | ||
463 | + state.setRuleNodeId(getSelfId()); | ||
464 | + return mainCtx.getRuleNodeStateService().save(getTenantId(), state); | ||
465 | + } | ||
433 | 466 | ||
434 | private TbMsgMetaData getActionMetaData(RuleNodeId ruleNodeId) { | 467 | private TbMsgMetaData getActionMetaData(RuleNodeId ruleNodeId) { |
435 | TbMsgMetaData metaData = new TbMsgMetaData(); | 468 | TbMsgMetaData metaData = new TbMsgMetaData(); |
@@ -47,6 +47,7 @@ import org.thingsboard.server.common.msg.queue.PartitionChangeMsg; | @@ -47,6 +47,7 @@ import org.thingsboard.server.common.msg.queue.PartitionChangeMsg; | ||
47 | import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg; | 47 | import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg; |
48 | import org.thingsboard.server.common.msg.queue.RuleEngineException; | 48 | import org.thingsboard.server.common.msg.queue.RuleEngineException; |
49 | import org.thingsboard.server.common.msg.queue.ServiceType; | 49 | import org.thingsboard.server.common.msg.queue.ServiceType; |
50 | +import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper; | ||
50 | 51 | ||
51 | import java.util.List; | 52 | import java.util.List; |
52 | import java.util.Optional; | 53 | import java.util.Optional; |
@@ -116,6 +117,9 @@ public class TenantActor extends RuleChainManagerActor { | @@ -116,6 +117,9 @@ public class TenantActor extends RuleChainManagerActor { | ||
116 | if (msg.getMsgType().equals(MsgType.QUEUE_TO_RULE_ENGINE_MSG)) { | 117 | if (msg.getMsgType().equals(MsgType.QUEUE_TO_RULE_ENGINE_MSG)) { |
117 | QueueToRuleEngineMsg queueMsg = (QueueToRuleEngineMsg) msg; | 118 | QueueToRuleEngineMsg queueMsg = (QueueToRuleEngineMsg) msg; |
118 | queueMsg.getTbMsg().getCallback().onSuccess(); | 119 | queueMsg.getTbMsg().getCallback().onSuccess(); |
120 | + } else if (msg.getMsgType().equals(MsgType.TRANSPORT_TO_DEVICE_ACTOR_MSG)){ | ||
121 | + TransportToDeviceActorMsgWrapper transportMsg = (TransportToDeviceActorMsgWrapper) msg; | ||
122 | + transportMsg.getCallback().onSuccess(); | ||
119 | } | 123 | } |
120 | return true; | 124 | return true; |
121 | } | 125 | } |
@@ -90,7 +90,7 @@ public class AlarmController extends BaseController { | @@ -90,7 +90,7 @@ public class AlarmController extends BaseController { | ||
90 | checkEntity(alarm.getId(), alarm, Resource.ALARM); | 90 | checkEntity(alarm.getId(), alarm, Resource.ALARM); |
91 | 91 | ||
92 | Alarm savedAlarm = checkNotNull(alarmService.createOrUpdateAlarm(alarm)); | 92 | Alarm savedAlarm = checkNotNull(alarmService.createOrUpdateAlarm(alarm)); |
93 | - logEntityAction(savedAlarm.getId(), savedAlarm, | 93 | + logEntityAction(savedAlarm.getOriginator(), savedAlarm, |
94 | getCurrentUser().getCustomerId(), | 94 | getCurrentUser().getCustomerId(), |
95 | alarm.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); | 95 | alarm.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); |
96 | return savedAlarm; | 96 | return savedAlarm; |
@@ -126,7 +126,7 @@ public class AlarmController extends BaseController { | @@ -126,7 +126,7 @@ public class AlarmController extends BaseController { | ||
126 | long ackTs = System.currentTimeMillis(); | 126 | long ackTs = System.currentTimeMillis(); |
127 | alarmService.ackAlarm(getCurrentUser().getTenantId(), alarmId, ackTs).get(); | 127 | alarmService.ackAlarm(getCurrentUser().getTenantId(), alarmId, ackTs).get(); |
128 | alarm.setAckTs(ackTs); | 128 | alarm.setAckTs(ackTs); |
129 | - logEntityAction(alarmId, alarm, getCurrentUser().getCustomerId(), ActionType.ALARM_ACK, null); | 129 | + logEntityAction(alarm.getOriginator(), alarm, getCurrentUser().getCustomerId(), ActionType.ALARM_ACK, null); |
130 | } catch (Exception e) { | 130 | } catch (Exception e) { |
131 | throw handleException(e); | 131 | throw handleException(e); |
132 | } | 132 | } |
@@ -143,7 +143,7 @@ public class AlarmController extends BaseController { | @@ -143,7 +143,7 @@ public class AlarmController extends BaseController { | ||
143 | long clearTs = System.currentTimeMillis(); | 143 | long clearTs = System.currentTimeMillis(); |
144 | alarmService.clearAlarm(getCurrentUser().getTenantId(), alarmId, null, clearTs).get(); | 144 | alarmService.clearAlarm(getCurrentUser().getTenantId(), alarmId, null, clearTs).get(); |
145 | alarm.setClearTs(clearTs); | 145 | alarm.setClearTs(clearTs); |
146 | - logEntityAction(alarmId, alarm, getCurrentUser().getCustomerId(), ActionType.ALARM_CLEAR, null); | 146 | + logEntityAction(alarm.getOriginator(), alarm, getCurrentUser().getCustomerId(), ActionType.ALARM_CLEAR, null); |
147 | } catch (Exception e) { | 147 | } catch (Exception e) { |
148 | throw handleException(e); | 148 | throw handleException(e); |
149 | } | 149 | } |
@@ -24,6 +24,7 @@ import org.springframework.beans.factory.annotation.Autowired; | @@ -24,6 +24,7 @@ import org.springframework.beans.factory.annotation.Autowired; | ||
24 | import org.springframework.beans.factory.annotation.Value; | 24 | import org.springframework.beans.factory.annotation.Value; |
25 | import org.springframework.http.HttpStatus; | 25 | import org.springframework.http.HttpStatus; |
26 | import org.springframework.security.access.prepost.PreAuthorize; | 26 | import org.springframework.security.access.prepost.PreAuthorize; |
27 | +import org.springframework.util.CollectionUtils; | ||
27 | import org.springframework.util.StringUtils; | 28 | import org.springframework.util.StringUtils; |
28 | import org.springframework.web.bind.annotation.PathVariable; | 29 | import org.springframework.web.bind.annotation.PathVariable; |
29 | import org.springframework.web.bind.annotation.RequestBody; | 30 | import org.springframework.web.bind.annotation.RequestBody; |
@@ -49,6 +50,8 @@ import org.thingsboard.server.common.data.page.PageLink; | @@ -49,6 +50,8 @@ import org.thingsboard.server.common.data.page.PageLink; | ||
49 | import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; | 50 | import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; |
50 | import org.thingsboard.server.common.data.rule.DefaultRuleChainCreateRequest; | 51 | import org.thingsboard.server.common.data.rule.DefaultRuleChainCreateRequest; |
51 | import org.thingsboard.server.common.data.rule.RuleChain; | 52 | import org.thingsboard.server.common.data.rule.RuleChain; |
53 | +import org.thingsboard.server.common.data.rule.RuleChainData; | ||
54 | +import org.thingsboard.server.common.data.rule.RuleChainImportResult; | ||
52 | import org.thingsboard.server.common.data.rule.RuleChainMetaData; | 55 | import org.thingsboard.server.common.data.rule.RuleChainMetaData; |
53 | import org.thingsboard.server.common.data.rule.RuleNode; | 56 | import org.thingsboard.server.common.data.rule.RuleNode; |
54 | import org.thingsboard.server.common.msg.TbMsg; | 57 | import org.thingsboard.server.common.msg.TbMsg; |
@@ -386,6 +389,36 @@ public class RuleChainController extends BaseController { | @@ -386,6 +389,36 @@ public class RuleChainController extends BaseController { | ||
386 | } | 389 | } |
387 | } | 390 | } |
388 | 391 | ||
392 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | ||
393 | + @RequestMapping(value = "/ruleChains/export", params = {"limit"}, method = RequestMethod.GET) | ||
394 | + @ResponseBody | ||
395 | + public RuleChainData exportRuleChains(@RequestParam("limit") int limit) throws ThingsboardException { | ||
396 | + try { | ||
397 | + TenantId tenantId = getCurrentUser().getTenantId(); | ||
398 | + PageLink pageLink = new PageLink(limit); | ||
399 | + return checkNotNull(ruleChainService.exportTenantRuleChains(tenantId, pageLink)); | ||
400 | + } catch (Exception e) { | ||
401 | + throw handleException(e); | ||
402 | + } | ||
403 | + } | ||
404 | + | ||
405 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | ||
406 | + @RequestMapping(value = "/ruleChains/import", method = RequestMethod.POST) | ||
407 | + @ResponseBody | ||
408 | + public void importRuleChains(@RequestBody RuleChainData ruleChainData, @RequestParam(required = false, defaultValue = "false") boolean overwrite) throws ThingsboardException { | ||
409 | + try { | ||
410 | + TenantId tenantId = getCurrentUser().getTenantId(); | ||
411 | + List<RuleChainImportResult> importResults = ruleChainService.importTenantRuleChains(tenantId, ruleChainData, overwrite); | ||
412 | + if (!CollectionUtils.isEmpty(importResults)) { | ||
413 | + for (RuleChainImportResult importResult : importResults) { | ||
414 | + tbClusterService.onEntityStateChange(importResult.getTenantId(), importResult.getRuleChainId(), importResult.getLifecycleEvent()); | ||
415 | + } | ||
416 | + } | ||
417 | + } catch (Exception e) { | ||
418 | + throw handleException(e); | ||
419 | + } | ||
420 | + } | ||
421 | + | ||
389 | private String msgToOutput(TbMsg msg) throws Exception { | 422 | private String msgToOutput(TbMsg msg) throws Exception { |
390 | ObjectNode msgData = objectMapper.createObjectNode(); | 423 | ObjectNode msgData = objectMapper.createObjectNode(); |
391 | if (!StringUtils.isEmpty(msg.getData())) { | 424 | if (!StringUtils.isEmpty(msg.getData())) { |
@@ -176,13 +176,12 @@ public class ThingsboardInstallService { | @@ -176,13 +176,12 @@ public class ThingsboardInstallService { | ||
176 | log.info("Upgrading ThingsBoard from version 3.1.0 to 3.1.1 ..."); | 176 | log.info("Upgrading ThingsBoard from version 3.1.0 to 3.1.1 ..."); |
177 | databaseEntitiesUpgradeService.upgradeDatabase("3.1.0"); | 177 | databaseEntitiesUpgradeService.upgradeDatabase("3.1.0"); |
178 | case "3.1.1": | 178 | case "3.1.1": |
179 | - log.info("Upgrading ThingsBoard from version 3.1.1 to 3.1.2 ..."); | 179 | + log.info("Upgrading ThingsBoard from version 3.1.1 to 3.2.0 ..."); |
180 | if (databaseTsUpgradeService != null) { | 180 | if (databaseTsUpgradeService != null) { |
181 | databaseTsUpgradeService.upgradeDatabase("3.1.1"); | 181 | databaseTsUpgradeService.upgradeDatabase("3.1.1"); |
182 | } | 182 | } |
183 | - case "3.1.2": | ||
184 | - log.info("Upgrading ThingsBoard from version 3.1.2 to 3.2.0 ..."); | ||
185 | - databaseEntitiesUpgradeService.upgradeDatabase("3.1.2"); | 183 | + databaseEntitiesUpgradeService.upgradeDatabase("3.1.1"); |
184 | + dataUpdateService.updateData("3.1.1"); | ||
186 | log.info("Updating system data..."); | 185 | log.info("Updating system data..."); |
187 | systemDataLoaderService.updateSystemWidgets(); | 186 | systemDataLoaderService.updateSystemWidgets(); |
188 | break; | 187 | break; |
@@ -324,7 +324,7 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService | @@ -324,7 +324,7 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService | ||
324 | log.info("Schema updated."); | 324 | log.info("Schema updated."); |
325 | } | 325 | } |
326 | break; | 326 | break; |
327 | - case "3.1.2": | 327 | + case "3.1.1": |
328 | try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { | 328 | try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { |
329 | log.info("Updating schema ..."); | 329 | log.info("Updating schema ..."); |
330 | if (isOldSchema(conn, 3001000)) { | 330 | if (isOldSchema(conn, 3001000)) { |
@@ -339,7 +339,20 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService | @@ -339,7 +339,20 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService | ||
339 | } catch (Exception e) { | 339 | } catch (Exception e) { |
340 | } | 340 | } |
341 | 341 | ||
342 | - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.1.2", "schema_update_before.sql"); | 342 | + try { |
343 | + conn.createStatement().execute("CREATE TABLE IF NOT EXISTS rule_node_state (" + | ||
344 | + " id uuid NOT NULL CONSTRAINT rule_node_state_pkey PRIMARY KEY," + | ||
345 | + " created_time bigint NOT NULL," + | ||
346 | + " rule_node_id uuid NOT NULL," + | ||
347 | + " entity_type varchar(32) NOT NULL," + | ||
348 | + " entity_id uuid NOT NULL," + | ||
349 | + " state_data varchar(16384) NOT NULL," + | ||
350 | + " CONSTRAINT rule_node_state_unq_key UNIQUE (rule_node_id, entity_id)," + | ||
351 | + " CONSTRAINT fk_rule_node_state_node_id FOREIGN KEY (rule_node_id) REFERENCES rule_node(id) ON DELETE CASCADE)"); | ||
352 | + } catch (Exception e) { | ||
353 | + } | ||
354 | + | ||
355 | + schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.1.1", "schema_update_before.sql"); | ||
343 | loadSql(schemaUpdateFile, conn); | 356 | loadSql(schemaUpdateFile, conn); |
344 | 357 | ||
345 | log.info("Creating default tenant profiles..."); | 358 | log.info("Creating default tenant profiles..."); |
@@ -357,7 +370,8 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService | @@ -357,7 +370,8 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService | ||
357 | List<EntitySubtype> deviceTypes = deviceService.findDeviceTypesByTenantId(tenant.getId()).get(); | 370 | List<EntitySubtype> deviceTypes = deviceService.findDeviceTypesByTenantId(tenant.getId()).get(); |
358 | try { | 371 | try { |
359 | deviceProfileService.createDefaultDeviceProfile(tenant.getId()); | 372 | deviceProfileService.createDefaultDeviceProfile(tenant.getId()); |
360 | - } catch (Exception e){} | 373 | + } catch (Exception e) { |
374 | + } | ||
361 | for (EntitySubtype deviceType : deviceTypes) { | 375 | for (EntitySubtype deviceType : deviceTypes) { |
362 | try { | 376 | try { |
363 | deviceProfileService.findOrCreateDeviceProfile(tenant.getId(), deviceType.getType()); | 377 | deviceProfileService.findOrCreateDeviceProfile(tenant.getId(), deviceType.getType()); |
@@ -371,7 +385,7 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService | @@ -371,7 +385,7 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService | ||
371 | log.info("Updating device profiles..."); | 385 | log.info("Updating device profiles..."); |
372 | conn.createStatement().execute("call update_device_profiles()"); | 386 | conn.createStatement().execute("call update_device_profiles()"); |
373 | 387 | ||
374 | - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.1.2", "schema_update_after.sql"); | 388 | + schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.1.1", "schema_update_after.sql"); |
375 | loadSql(schemaUpdateFile, conn); | 389 | loadSql(schemaUpdateFile, conn); |
376 | 390 | ||
377 | conn.createStatement().execute("UPDATE tb_schema_settings SET schema_version = 3002000;"); | 391 | conn.createStatement().execute("UPDATE tb_schema_settings SET schema_version = 3002000;"); |
@@ -15,6 +15,8 @@ | @@ -15,6 +15,8 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.service.install.update; | 16 | package org.thingsboard.server.service.install.update; |
17 | 17 | ||
18 | +import com.fasterxml.jackson.databind.JsonNode; | ||
19 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
18 | import com.google.common.util.concurrent.FutureCallback; | 20 | import com.google.common.util.concurrent.FutureCallback; |
19 | import com.google.common.util.concurrent.Futures; | 21 | import com.google.common.util.concurrent.Futures; |
20 | import com.google.common.util.concurrent.ListenableFuture; | 22 | import com.google.common.util.concurrent.ListenableFuture; |
@@ -23,9 +25,13 @@ import lombok.extern.slf4j.Slf4j; | @@ -23,9 +25,13 @@ import lombok.extern.slf4j.Slf4j; | ||
23 | import org.springframework.beans.factory.annotation.Autowired; | 25 | import org.springframework.beans.factory.annotation.Autowired; |
24 | import org.springframework.context.annotation.Profile; | 26 | import org.springframework.context.annotation.Profile; |
25 | import org.springframework.stereotype.Service; | 27 | import org.springframework.stereotype.Service; |
28 | +import org.springframework.util.StringUtils; | ||
29 | +import org.thingsboard.rule.engine.profile.TbDeviceProfileNode; | ||
30 | +import org.thingsboard.rule.engine.profile.TbDeviceProfileNodeConfiguration; | ||
26 | import org.thingsboard.server.common.data.EntityView; | 31 | import org.thingsboard.server.common.data.EntityView; |
27 | import org.thingsboard.server.common.data.SearchTextBased; | 32 | import org.thingsboard.server.common.data.SearchTextBased; |
28 | import org.thingsboard.server.common.data.Tenant; | 33 | import org.thingsboard.server.common.data.Tenant; |
34 | +import org.thingsboard.server.common.data.id.EntityId; | ||
29 | import org.thingsboard.server.common.data.id.EntityViewId; | 35 | import org.thingsboard.server.common.data.id.EntityViewId; |
30 | import org.thingsboard.server.common.data.id.TenantId; | 36 | import org.thingsboard.server.common.data.id.TenantId; |
31 | import org.thingsboard.server.common.data.id.UUIDBased; | 37 | import org.thingsboard.server.common.data.id.UUIDBased; |
@@ -35,10 +41,13 @@ import org.thingsboard.server.common.data.kv.TsKvEntry; | @@ -35,10 +41,13 @@ import org.thingsboard.server.common.data.kv.TsKvEntry; | ||
35 | import org.thingsboard.server.common.data.page.PageData; | 41 | import org.thingsboard.server.common.data.page.PageData; |
36 | import org.thingsboard.server.common.data.page.PageLink; | 42 | import org.thingsboard.server.common.data.page.PageLink; |
37 | import org.thingsboard.server.common.data.rule.RuleChain; | 43 | import org.thingsboard.server.common.data.rule.RuleChain; |
44 | +import org.thingsboard.server.common.data.rule.RuleChainMetaData; | ||
45 | +import org.thingsboard.server.common.data.rule.RuleNode; | ||
38 | import org.thingsboard.server.dao.entityview.EntityViewService; | 46 | import org.thingsboard.server.dao.entityview.EntityViewService; |
39 | import org.thingsboard.server.dao.rule.RuleChainService; | 47 | import org.thingsboard.server.dao.rule.RuleChainService; |
40 | import org.thingsboard.server.dao.tenant.TenantService; | 48 | import org.thingsboard.server.dao.tenant.TenantService; |
41 | import org.thingsboard.server.dao.timeseries.TimeseriesService; | 49 | import org.thingsboard.server.dao.timeseries.TimeseriesService; |
50 | +import org.thingsboard.server.dao.util.mapping.JacksonUtil; | ||
42 | import org.thingsboard.server.service.install.InstallScripts; | 51 | import org.thingsboard.server.service.install.InstallScripts; |
43 | 52 | ||
44 | import javax.annotation.Nullable; | 53 | import javax.annotation.Nullable; |
@@ -49,6 +58,7 @@ import java.util.concurrent.ExecutionException; | @@ -49,6 +58,7 @@ import java.util.concurrent.ExecutionException; | ||
49 | import java.util.stream.Collectors; | 58 | import java.util.stream.Collectors; |
50 | 59 | ||
51 | import static org.apache.commons.lang.StringUtils.isBlank; | 60 | import static org.apache.commons.lang.StringUtils.isBlank; |
61 | +import static org.thingsboard.server.service.install.DatabaseHelper.objectMapper; | ||
52 | 62 | ||
53 | @Service | 63 | @Service |
54 | @Profile("install") | 64 | @Profile("install") |
@@ -81,6 +91,10 @@ public class DefaultDataUpdateService implements DataUpdateService { | @@ -81,6 +91,10 @@ public class DefaultDataUpdateService implements DataUpdateService { | ||
81 | log.info("Updating data from version 3.0.1 to 3.1.0 ..."); | 91 | log.info("Updating data from version 3.0.1 to 3.1.0 ..."); |
82 | tenantsEntityViewsUpdater.updateEntities(null); | 92 | tenantsEntityViewsUpdater.updateEntities(null); |
83 | break; | 93 | break; |
94 | + case "3.1.1": | ||
95 | + log.info("Updating data from version 3.1.1 to 3.2.0 ..."); | ||
96 | + tenantsRootRuleChainUpdater.updateEntities(null); | ||
97 | + break; | ||
84 | default: | 98 | default: |
85 | throw new RuntimeException("Unable to update data, unsupported fromVersion: " + fromVersion); | 99 | throw new RuntimeException("Unable to update data, unsupported fromVersion: " + fromVersion); |
86 | } | 100 | } |
@@ -107,6 +121,60 @@ public class DefaultDataUpdateService implements DataUpdateService { | @@ -107,6 +121,60 @@ public class DefaultDataUpdateService implements DataUpdateService { | ||
107 | } | 121 | } |
108 | }; | 122 | }; |
109 | 123 | ||
124 | + private PaginatedUpdater<String, Tenant> tenantsRootRuleChainUpdater = | ||
125 | + new PaginatedUpdater<String, Tenant>() { | ||
126 | + | ||
127 | + @Override | ||
128 | + protected PageData<Tenant> findEntities(String region, PageLink pageLink) { | ||
129 | + return tenantService.findTenants(pageLink); | ||
130 | + } | ||
131 | + | ||
132 | + @Override | ||
133 | + protected void updateEntity(Tenant tenant) { | ||
134 | + try { | ||
135 | + RuleChain ruleChain = ruleChainService.getRootTenantRuleChain(tenant.getId()); | ||
136 | + if (ruleChain == null) { | ||
137 | + installScripts.createDefaultRuleChains(tenant.getId()); | ||
138 | + } else { | ||
139 | + RuleChainMetaData md = ruleChainService.loadRuleChainMetaData(tenant.getId(), ruleChain.getId()); | ||
140 | + int oldIdx = md.getFirstNodeIndex(); | ||
141 | + int newIdx = md.getNodes().size(); | ||
142 | + | ||
143 | + if (md.getNodes().size() < oldIdx) { | ||
144 | + // Skip invalid rule chains | ||
145 | + return; | ||
146 | + } | ||
147 | + | ||
148 | + RuleNode oldFirstNode = md.getNodes().get(oldIdx); | ||
149 | + if (oldFirstNode.getType().equals(TbDeviceProfileNode.class.getName())) { | ||
150 | + // No need to update the rule node twice. | ||
151 | + return; | ||
152 | + } | ||
153 | + | ||
154 | + RuleNode ruleNode = new RuleNode(); | ||
155 | + ruleNode.setRuleChainId(ruleChain.getId()); | ||
156 | + ruleNode.setName("Device Profile Node"); | ||
157 | + ruleNode.setType(TbDeviceProfileNode.class.getName()); | ||
158 | + ruleNode.setDebugMode(false); | ||
159 | + TbDeviceProfileNodeConfiguration ruleNodeConfiguration = new TbDeviceProfileNodeConfiguration().defaultConfiguration(); | ||
160 | + ruleNode.setConfiguration(JacksonUtil.valueToTree(ruleNodeConfiguration)); | ||
161 | + ObjectNode additionalInfo = JacksonUtil.newObjectNode(); | ||
162 | + additionalInfo.put("description", "Process incoming messages from devices with the alarm rules defined in the device profile. Dispatch all incoming messages with \"Success\" relation type."); | ||
163 | + additionalInfo.put("layoutX", 204); | ||
164 | + additionalInfo.put("layoutY", 240); | ||
165 | + ruleNode.setAdditionalInfo(additionalInfo); | ||
166 | + | ||
167 | + md.getNodes().add(ruleNode); | ||
168 | + md.setFirstNodeIndex(newIdx); | ||
169 | + md.addConnectionInfo(newIdx, oldIdx, "Success"); | ||
170 | + ruleChainService.saveRuleChainMetaData(tenant.getId(), md); | ||
171 | + } | ||
172 | + } catch (Exception e) { | ||
173 | + log.error("Unable to update Tenant", e); | ||
174 | + } | ||
175 | + } | ||
176 | + }; | ||
177 | + | ||
110 | private PaginatedUpdater<String, Tenant> tenantsEntityViewsUpdater = | 178 | private PaginatedUpdater<String, Tenant> tenantsEntityViewsUpdater = |
111 | new PaginatedUpdater<String, Tenant>() { | 179 | new PaginatedUpdater<String, Tenant>() { |
112 | 180 | ||
@@ -121,30 +189,30 @@ public class DefaultDataUpdateService implements DataUpdateService { | @@ -121,30 +189,30 @@ public class DefaultDataUpdateService implements DataUpdateService { | ||
121 | } | 189 | } |
122 | }; | 190 | }; |
123 | 191 | ||
124 | - private void updateTenantEntityViews(TenantId tenantId) { | ||
125 | - PageLink pageLink = new PageLink(100); | ||
126 | - PageData<EntityView> pageData = entityViewService.findEntityViewByTenantId(tenantId, pageLink); | ||
127 | - boolean hasNext = true; | ||
128 | - while (hasNext) { | ||
129 | - List<ListenableFuture<List<Void>>> updateFutures = new ArrayList<>(); | ||
130 | - for (EntityView entityView : pageData.getData()) { | ||
131 | - updateFutures.add(updateEntityViewLatestTelemetry(entityView)); | ||
132 | - } | ||
133 | - | ||
134 | - try { | ||
135 | - Futures.allAsList(updateFutures).get(); | ||
136 | - } catch (InterruptedException | ExecutionException e) { | ||
137 | - log.error("Failed to copy latest telemetry to entity view", e); | ||
138 | - } | ||
139 | - | ||
140 | - if (pageData.hasNext()) { | ||
141 | - pageLink = pageLink.nextPageLink(); | ||
142 | - pageData = entityViewService.findEntityViewByTenantId(tenantId, pageLink); | ||
143 | - } else { | ||
144 | - hasNext = false; | ||
145 | - } | ||
146 | - } | ||
147 | - } | 192 | + private void updateTenantEntityViews(TenantId tenantId) { |
193 | + PageLink pageLink = new PageLink(100); | ||
194 | + PageData<EntityView> pageData = entityViewService.findEntityViewByTenantId(tenantId, pageLink); | ||
195 | + boolean hasNext = true; | ||
196 | + while (hasNext) { | ||
197 | + List<ListenableFuture<List<Void>>> updateFutures = new ArrayList<>(); | ||
198 | + for (EntityView entityView : pageData.getData()) { | ||
199 | + updateFutures.add(updateEntityViewLatestTelemetry(entityView)); | ||
200 | + } | ||
201 | + | ||
202 | + try { | ||
203 | + Futures.allAsList(updateFutures).get(); | ||
204 | + } catch (InterruptedException | ExecutionException e) { | ||
205 | + log.error("Failed to copy latest telemetry to entity view", e); | ||
206 | + } | ||
207 | + | ||
208 | + if (pageData.hasNext()) { | ||
209 | + pageLink = pageLink.nextPageLink(); | ||
210 | + pageData = entityViewService.findEntityViewByTenantId(tenantId, pageLink); | ||
211 | + } else { | ||
212 | + hasNext = false; | ||
213 | + } | ||
214 | + } | ||
215 | + } | ||
148 | 216 | ||
149 | private ListenableFuture<List<Void>> updateEntityViewLatestTelemetry(EntityView entityView) { | 217 | private ListenableFuture<List<Void>> updateEntityViewLatestTelemetry(EntityView entityView) { |
150 | EntityViewId entityId = entityView.getId(); | 218 | EntityViewId entityId = entityView.getId(); |
@@ -160,13 +228,13 @@ public class DefaultDataUpdateService implements DataUpdateService { | @@ -160,13 +228,13 @@ public class DefaultDataUpdateService implements DataUpdateService { | ||
160 | keysFuture = Futures.immediateFuture(keys); | 228 | keysFuture = Futures.immediateFuture(keys); |
161 | } | 229 | } |
162 | ListenableFuture<List<TsKvEntry>> latestFuture = Futures.transformAsync(keysFuture, fetchKeys -> { | 230 | ListenableFuture<List<TsKvEntry>> latestFuture = Futures.transformAsync(keysFuture, fetchKeys -> { |
163 | - List<ReadTsKvQuery> queries = fetchKeys.stream().filter(key -> !isBlank(key)).map(key -> new BaseReadTsKvQuery(key, startTs, endTs, 1, "DESC")).collect(Collectors.toList()); | ||
164 | - if (!queries.isEmpty()) { | ||
165 | - return tsService.findAll(TenantId.SYS_TENANT_ID, entityView.getEntityId(), queries); | ||
166 | - } else { | ||
167 | - return Futures.immediateFuture(null); | ||
168 | - } | ||
169 | - }, MoreExecutors.directExecutor()); | 231 | + List<ReadTsKvQuery> queries = fetchKeys.stream().filter(key -> !isBlank(key)).map(key -> new BaseReadTsKvQuery(key, startTs, endTs, 1, "DESC")).collect(Collectors.toList()); |
232 | + if (!queries.isEmpty()) { | ||
233 | + return tsService.findAll(TenantId.SYS_TENANT_ID, entityView.getEntityId(), queries); | ||
234 | + } else { | ||
235 | + return Futures.immediateFuture(null); | ||
236 | + } | ||
237 | + }, MoreExecutors.directExecutor()); | ||
170 | return Futures.transformAsync(latestFuture, latestValues -> { | 238 | return Futures.transformAsync(latestFuture, latestValues -> { |
171 | if (latestValues != null && !latestValues.isEmpty()) { | 239 | if (latestValues != null && !latestValues.isEmpty()) { |
172 | ListenableFuture<List<Void>> saveFuture = tsService.saveLatest(TenantId.SYS_TENANT_ID, entityId, latestValues); | 240 | ListenableFuture<List<Void>> saveFuture = tsService.saveLatest(TenantId.SYS_TENANT_ID, entityId, latestValues); |
@@ -50,9 +50,9 @@ public class DefaultTbDeviceProfileCache implements TbDeviceProfileCache { | @@ -50,9 +50,9 @@ public class DefaultTbDeviceProfileCache implements TbDeviceProfileCache { | ||
50 | public DeviceProfile get(TenantId tenantId, DeviceProfileId deviceProfileId) { | 50 | public DeviceProfile get(TenantId tenantId, DeviceProfileId deviceProfileId) { |
51 | DeviceProfile profile = deviceProfilesMap.get(deviceProfileId); | 51 | DeviceProfile profile = deviceProfilesMap.get(deviceProfileId); |
52 | if (profile == null) { | 52 | if (profile == null) { |
53 | - deviceProfileFetchLock.lock(); | ||
54 | profile = deviceProfilesMap.get(deviceProfileId); | 53 | profile = deviceProfilesMap.get(deviceProfileId); |
55 | if (profile == null) { | 54 | if (profile == null) { |
55 | + deviceProfileFetchLock.lock(); | ||
56 | try { | 56 | try { |
57 | profile = deviceProfileService.findDeviceProfileById(tenantId, deviceProfileId); | 57 | profile = deviceProfileService.findDeviceProfileById(tenantId, deviceProfileId); |
58 | if (profile != null) { | 58 | if (profile != null) { |
@@ -226,6 +226,11 @@ public class DefaultSubscriptionManagerService implements SubscriptionManagerSer | @@ -226,6 +226,11 @@ public class DefaultSubscriptionManagerService implements SubscriptionManagerSer | ||
226 | 226 | ||
227 | @Override | 227 | @Override |
228 | public void onAttributesUpdate(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes, TbCallback callback) { | 228 | public void onAttributesUpdate(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes, TbCallback callback) { |
229 | + onAttributesUpdate(tenantId, entityId, scope, attributes, true, callback); | ||
230 | + } | ||
231 | + | ||
232 | + @Override | ||
233 | + public void onAttributesUpdate(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes, boolean notifyDevice, TbCallback callback) { | ||
229 | onLocalTelemetrySubUpdate(entityId, | 234 | onLocalTelemetrySubUpdate(entityId, |
230 | s -> { | 235 | s -> { |
231 | if (TbSubscriptionType.ATTRIBUTES.equals(s.getType())) { | 236 | if (TbSubscriptionType.ATTRIBUTES.equals(s.getType())) { |
@@ -254,7 +259,7 @@ public class DefaultSubscriptionManagerService implements SubscriptionManagerSer | @@ -254,7 +259,7 @@ public class DefaultSubscriptionManagerService implements SubscriptionManagerSer | ||
254 | deviceStateService.onDeviceInactivityTimeoutUpdate(new DeviceId(entityId.getId()), attribute.getLongValue().orElse(0L)); | 259 | deviceStateService.onDeviceInactivityTimeoutUpdate(new DeviceId(entityId.getId()), attribute.getLongValue().orElse(0L)); |
255 | } | 260 | } |
256 | } | 261 | } |
257 | - } else if (TbAttributeSubscriptionScope.SHARED_SCOPE.name().equalsIgnoreCase(scope)) { | 262 | + } else if (TbAttributeSubscriptionScope.SHARED_SCOPE.name().equalsIgnoreCase(scope) && notifyDevice) { |
258 | clusterService.pushMsgToCore(DeviceAttributesEventNotificationMsg.onUpdate(tenantId, | 263 | clusterService.pushMsgToCore(DeviceAttributesEventNotificationMsg.onUpdate(tenantId, |
259 | new DeviceId(entityId.getId()), DataConstants.SHARED_SCOPE, new ArrayList<>(attributes)) | 264 | new DeviceId(entityId.getId()), DataConstants.SHARED_SCOPE, new ArrayList<>(attributes)) |
260 | , null); | 265 | , null); |
@@ -17,13 +17,12 @@ package org.thingsboard.server.service.subscription; | @@ -17,13 +17,12 @@ package org.thingsboard.server.service.subscription; | ||
17 | 17 | ||
18 | import org.springframework.context.ApplicationListener; | 18 | import org.springframework.context.ApplicationListener; |
19 | import org.thingsboard.server.common.data.alarm.Alarm; | 19 | import org.thingsboard.server.common.data.alarm.Alarm; |
20 | -import org.thingsboard.server.common.data.id.AlarmId; | ||
21 | import org.thingsboard.server.common.data.id.EntityId; | 20 | import org.thingsboard.server.common.data.id.EntityId; |
22 | import org.thingsboard.server.common.data.id.TenantId; | 21 | import org.thingsboard.server.common.data.id.TenantId; |
23 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; | 22 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; |
24 | import org.thingsboard.server.common.data.kv.TsKvEntry; | 23 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
25 | -import org.thingsboard.server.queue.discovery.PartitionChangeEvent; | ||
26 | import org.thingsboard.server.common.msg.queue.TbCallback; | 24 | import org.thingsboard.server.common.msg.queue.TbCallback; |
25 | +import org.thingsboard.server.queue.discovery.PartitionChangeEvent; | ||
27 | 26 | ||
28 | import java.util.List; | 27 | import java.util.List; |
29 | 28 | ||
@@ -37,9 +36,13 @@ public interface SubscriptionManagerService extends ApplicationListener<Partitio | @@ -37,9 +36,13 @@ public interface SubscriptionManagerService extends ApplicationListener<Partitio | ||
37 | 36 | ||
38 | void onAttributesUpdate(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes, TbCallback callback); | 37 | void onAttributesUpdate(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes, TbCallback callback); |
39 | 38 | ||
39 | + void onAttributesUpdate(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes, boolean notifyDevice, TbCallback callback); | ||
40 | + | ||
40 | void onAttributesDelete(TenantId tenantId, EntityId entityId, String scope, List<String> keys, TbCallback empty); | 41 | void onAttributesDelete(TenantId tenantId, EntityId entityId, String scope, List<String> keys, TbCallback empty); |
41 | 42 | ||
42 | void onAlarmUpdate(TenantId tenantId, EntityId entityId, Alarm alarm, TbCallback callback); | 43 | void onAlarmUpdate(TenantId tenantId, EntityId entityId, Alarm alarm, TbCallback callback); |
43 | 44 | ||
44 | void onAlarmDeleted(TenantId tenantId, EntityId entityId, Alarm alarm, TbCallback callback); | 45 | void onAlarmDeleted(TenantId tenantId, EntityId entityId, Alarm alarm, TbCallback callback); |
46 | + | ||
47 | + | ||
45 | } | 48 | } |
@@ -171,9 +171,14 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer | @@ -171,9 +171,14 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer | ||
171 | 171 | ||
172 | @Override | 172 | @Override |
173 | public void saveAndNotify(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes, FutureCallback<Void> callback) { | 173 | public void saveAndNotify(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes, FutureCallback<Void> callback) { |
174 | + saveAndNotify(tenantId, entityId, scope, attributes, true, callback); | ||
175 | + } | ||
176 | + | ||
177 | + @Override | ||
178 | + public void saveAndNotify(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes, boolean notifyDevice, FutureCallback<Void> callback) { | ||
174 | ListenableFuture<List<Void>> saveFuture = attrService.save(tenantId, entityId, scope, attributes); | 179 | ListenableFuture<List<Void>> saveFuture = attrService.save(tenantId, entityId, scope, attributes); |
175 | addMainCallback(saveFuture, callback); | 180 | addMainCallback(saveFuture, callback); |
176 | - addWsCallback(saveFuture, success -> onAttributesUpdate(tenantId, entityId, scope, attributes)); | 181 | + addWsCallback(saveFuture, success -> onAttributesUpdate(tenantId, entityId, scope, attributes, notifyDevice)); |
177 | } | 182 | } |
178 | 183 | ||
179 | @Override | 184 | @Override |
@@ -236,11 +241,11 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer | @@ -236,11 +241,11 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer | ||
236 | , System.currentTimeMillis())), callback); | 241 | , System.currentTimeMillis())), callback); |
237 | } | 242 | } |
238 | 243 | ||
239 | - private void onAttributesUpdate(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes) { | 244 | + private void onAttributesUpdate(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes, boolean notifyDevice) { |
240 | TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, entityId); | 245 | TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, entityId); |
241 | if (currentPartitions.contains(tpi)) { | 246 | if (currentPartitions.contains(tpi)) { |
242 | if (subscriptionManagerService.isPresent()) { | 247 | if (subscriptionManagerService.isPresent()) { |
243 | - subscriptionManagerService.get().onAttributesUpdate(tenantId, entityId, scope, attributes, TbCallback.EMPTY); | 248 | + subscriptionManagerService.get().onAttributesUpdate(tenantId, entityId, scope, attributes, notifyDevice, TbCallback.EMPTY); |
244 | } else { | 249 | } else { |
245 | log.warn("Possible misconfiguration because subscriptionManagerService is null!"); | 250 | log.warn("Possible misconfiguration because subscriptionManagerService is null!"); |
246 | } | 251 | } |
@@ -225,6 +225,8 @@ public class DefaultTransportApiService implements TransportApiService { | @@ -225,6 +225,8 @@ public class DefaultTransportApiService implements TransportApiService { | ||
225 | device.setName(requestMsg.getDeviceName()); | 225 | device.setName(requestMsg.getDeviceName()); |
226 | device.setType(requestMsg.getDeviceType()); | 226 | device.setType(requestMsg.getDeviceType()); |
227 | device.setCustomerId(gateway.getCustomerId()); | 227 | device.setCustomerId(gateway.getCustomerId()); |
228 | + DeviceProfile deviceProfile = deviceProfileService.findOrCreateDeviceProfile(gateway.getTenantId(), requestMsg.getDeviceType()); | ||
229 | + device.setDeviceProfileId(deviceProfile.getId()); | ||
228 | device = deviceService.saveDevice(device); | 230 | device = deviceService.saveDevice(device); |
229 | relationService.saveRelationAsync(TenantId.SYS_TENANT_ID, new EntityRelation(gateway.getId(), device.getId(), "Created")); | 231 | relationService.saveRelationAsync(TenantId.SYS_TENANT_ID, new EntityRelation(gateway.getId(), device.getId(), "Created")); |
230 | deviceStateService.onDeviceAdded(device); | 232 | deviceStateService.onDeviceAdded(device); |
@@ -439,6 +439,9 @@ updates: | @@ -439,6 +439,9 @@ updates: | ||
439 | # Enable/disable updates checking. | 439 | # Enable/disable updates checking. |
440 | enabled: "${UPDATES_ENABLED:true}" | 440 | enabled: "${UPDATES_ENABLED:true}" |
441 | 441 | ||
442 | +# spring freemarker configuration | ||
443 | +spring.freemarker.checkTemplateLocation: "false" | ||
444 | + | ||
442 | # spring CORS configuration | 445 | # spring CORS configuration |
443 | spring.mvc.cors: | 446 | spring.mvc.cors: |
444 | mappings: | 447 | mappings: |
@@ -605,6 +608,8 @@ transport: | @@ -605,6 +608,8 @@ transport: | ||
605 | key_password: "${MQTT_SSL_KEY_PASSWORD:server_key_password}" | 608 | key_password: "${MQTT_SSL_KEY_PASSWORD:server_key_password}" |
606 | # Type of the key store | 609 | # Type of the key store |
607 | key_store_type: "${MQTT_SSL_KEY_STORE_TYPE:JKS}" | 610 | key_store_type: "${MQTT_SSL_KEY_STORE_TYPE:JKS}" |
611 | + # Skip certificate validity check for client certificates. | ||
612 | + skip_validity_check_for_client_cert: "${MQTT_SSL_SKIP_VALIDITY_CHECK_FOR_CLIENT_CERT:false}" | ||
608 | # Local CoAP transport parameters | 613 | # Local CoAP transport parameters |
609 | coap: | 614 | coap: |
610 | # Enable/disable coap transport protocol. | 615 | # Enable/disable coap transport protocol. |
@@ -226,6 +226,10 @@ public abstract class AbstractWebTest { | @@ -226,6 +226,10 @@ public abstract class AbstractWebTest { | ||
226 | login(CUSTOMER_USER_EMAIL, CUSTOMER_USER_PASSWORD); | 226 | login(CUSTOMER_USER_EMAIL, CUSTOMER_USER_PASSWORD); |
227 | } | 227 | } |
228 | 228 | ||
229 | + protected void loginUser(String userName, String password) throws Exception { | ||
230 | + login(userName, password); | ||
231 | + } | ||
232 | + | ||
229 | private Tenant savedDifferentTenant; | 233 | private Tenant savedDifferentTenant; |
230 | 234 | ||
231 | protected void loginDifferentTenant() throws Exception { | 235 | protected void loginDifferentTenant() throws Exception { |
@@ -251,15 +255,27 @@ public abstract class AbstractWebTest { | @@ -251,15 +255,27 @@ public abstract class AbstractWebTest { | ||
251 | protected User createUserAndLogin(User user, String password) throws Exception { | 255 | protected User createUserAndLogin(User user, String password) throws Exception { |
252 | User savedUser = doPost("/api/user", user, User.class); | 256 | User savedUser = doPost("/api/user", user, User.class); |
253 | logout(); | 257 | logout(); |
258 | + JsonNode activateRequest = getActivateRequest(password); | ||
259 | + JsonNode tokenInfo = readResponse(doPost("/api/noauth/activate", activateRequest).andExpect(status().isOk()), JsonNode.class); | ||
260 | + validateAndSetJwtToken(tokenInfo, user.getEmail()); | ||
261 | + return savedUser; | ||
262 | + } | ||
263 | + | ||
264 | + protected User createUser(User user, String password) throws Exception { | ||
265 | + User savedUser = doPost("/api/user", user, User.class); | ||
266 | + JsonNode activateRequest = getActivateRequest(password); | ||
267 | + ResultActions resultActions = doPost("/api/noauth/activate", activateRequest); | ||
268 | + resultActions.andExpect(status().isOk()); | ||
269 | + return savedUser; | ||
270 | + } | ||
271 | + | ||
272 | + private JsonNode getActivateRequest(String password) throws Exception { | ||
254 | doGet("/api/noauth/activate?activateToken={activateToken}", TestMailService.currentActivateToken) | 273 | doGet("/api/noauth/activate?activateToken={activateToken}", TestMailService.currentActivateToken) |
255 | .andExpect(status().isSeeOther()) | 274 | .andExpect(status().isSeeOther()) |
256 | .andExpect(header().string(HttpHeaders.LOCATION, "/login/createPassword?activateToken=" + TestMailService.currentActivateToken)); | 275 | .andExpect(header().string(HttpHeaders.LOCATION, "/login/createPassword?activateToken=" + TestMailService.currentActivateToken)); |
257 | - JsonNode activateRequest = new ObjectMapper().createObjectNode() | 276 | + return new ObjectMapper().createObjectNode() |
258 | .put("activateToken", TestMailService.currentActivateToken) | 277 | .put("activateToken", TestMailService.currentActivateToken) |
259 | .put("password", password); | 278 | .put("password", password); |
260 | - JsonNode tokenInfo = readResponse(doPost("/api/noauth/activate", activateRequest).andExpect(status().isOk()), JsonNode.class); | ||
261 | - validateAndSetJwtToken(tokenInfo, user.getEmail()); | ||
262 | - return savedUser; | ||
263 | } | 279 | } |
264 | 280 | ||
265 | protected void login(String username, String password) throws Exception { | 281 | protected void login(String username, String password) throws Exception { |
@@ -442,6 +458,10 @@ public abstract class AbstractWebTest { | @@ -442,6 +458,10 @@ public abstract class AbstractWebTest { | ||
442 | return readResponse(doPostAsync(urlTemplate, content, timeout, params).andExpect(resultMatcher), responseClass); | 458 | return readResponse(doPostAsync(urlTemplate, content, timeout, params).andExpect(resultMatcher), responseClass); |
443 | } | 459 | } |
444 | 460 | ||
461 | + protected <T> T doPostClaimAsync(String urlTemplate, Object content, Class<T> responseClass, ResultMatcher resultMatcher, String... params) throws Exception { | ||
462 | + return readResponse(doPostAsync(urlTemplate, content, DEFAULT_TIMEOUT, params).andExpect(resultMatcher), responseClass); | ||
463 | + } | ||
464 | + | ||
445 | protected <T> T doDelete(String urlTemplate, Class<T> responseClass, String... params) throws Exception { | 465 | protected <T> T doDelete(String urlTemplate, Class<T> responseClass, String... params) throws Exception { |
446 | return readResponse(doDelete(urlTemplate, params).andExpect(status().isOk()), responseClass); | 466 | return readResponse(doDelete(urlTemplate, params).andExpect(status().isOk()), responseClass); |
447 | } | 467 | } |
@@ -22,6 +22,7 @@ import org.apache.commons.lang3.RandomStringUtils; | @@ -22,6 +22,7 @@ import org.apache.commons.lang3.RandomStringUtils; | ||
22 | import org.eclipse.paho.client.mqttv3.MqttAsyncClient; | 22 | import org.eclipse.paho.client.mqttv3.MqttAsyncClient; |
23 | import org.eclipse.paho.client.mqttv3.MqttConnectOptions; | 23 | import org.eclipse.paho.client.mqttv3.MqttConnectOptions; |
24 | import org.eclipse.paho.client.mqttv3.MqttMessage; | 24 | import org.eclipse.paho.client.mqttv3.MqttMessage; |
25 | +import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; | ||
25 | import org.junit.After; | 26 | import org.junit.After; |
26 | import org.junit.Assert; | 27 | import org.junit.Assert; |
27 | import org.junit.Before; | 28 | import org.junit.Before; |
@@ -424,7 +425,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes | @@ -424,7 +425,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes | ||
424 | assertNotNull(accessToken); | 425 | assertNotNull(accessToken); |
425 | 426 | ||
426 | String clientId = MqttAsyncClient.generateClientId(); | 427 | String clientId = MqttAsyncClient.generateClientId(); |
427 | - MqttAsyncClient client = new MqttAsyncClient("tcp://localhost:1883", clientId); | 428 | + MqttAsyncClient client = new MqttAsyncClient("tcp://localhost:1883", clientId, new MemoryPersistence()); |
428 | 429 | ||
429 | MqttConnectOptions options = new MqttConnectOptions(); | 430 | MqttConnectOptions options = new MqttConnectOptions(); |
430 | options.setUserName(accessToken); | 431 | options.setUserName(accessToken); |
@@ -466,7 +467,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes | @@ -466,7 +467,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes | ||
466 | assertNotNull(accessToken); | 467 | assertNotNull(accessToken); |
467 | 468 | ||
468 | String clientId = MqttAsyncClient.generateClientId(); | 469 | String clientId = MqttAsyncClient.generateClientId(); |
469 | - MqttAsyncClient client = new MqttAsyncClient("tcp://localhost:1883", clientId); | 470 | + MqttAsyncClient client = new MqttAsyncClient("tcp://localhost:1883", clientId, new MemoryPersistence()); |
470 | 471 | ||
471 | MqttConnectOptions options = new MqttConnectOptions(); | 472 | MqttConnectOptions options = new MqttConnectOptions(); |
472 | options.setUserName(accessToken); | 473 | options.setUserName(accessToken); |
@@ -34,7 +34,7 @@ public class ControllerSqlTestSuite { | @@ -34,7 +34,7 @@ public class ControllerSqlTestSuite { | ||
34 | 34 | ||
35 | @ClassRule | 35 | @ClassRule |
36 | public static CustomSqlUnit sqlUnit = new CustomSqlUnit( | 36 | public static CustomSqlUnit sqlUnit = new CustomSqlUnit( |
37 | - Arrays.asList("sql/schema-ts-hsql.sql", "sql/schema-entities-hsql.sql", "sql/schema-entities-idx.sql", "sql/system-data.sql"), | 37 | + Arrays.asList("sql/schema-types-hsql.sql", "sql/schema-ts-hsql.sql", "sql/schema-entities-hsql.sql", "sql/schema-entities-idx.sql", "sql/system-data.sql"), |
38 | "sql/hsql/drop-all-tables.sql", | 38 | "sql/hsql/drop-all-tables.sql", |
39 | "sql-test.properties"); | 39 | "sql-test.properties"); |
40 | 40 |
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.mqtt; | ||
17 | + | ||
18 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
19 | +import lombok.extern.slf4j.Slf4j; | ||
20 | +import org.eclipse.paho.client.mqttv3.MqttAsyncClient; | ||
21 | +import org.eclipse.paho.client.mqttv3.MqttConnectOptions; | ||
22 | +import org.eclipse.paho.client.mqttv3.MqttException; | ||
23 | +import org.eclipse.paho.client.mqttv3.MqttMessage; | ||
24 | +import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; | ||
25 | +import org.junit.Assert; | ||
26 | +import org.springframework.util.StringUtils; | ||
27 | +import org.thingsboard.server.common.data.Device; | ||
28 | +import org.thingsboard.server.common.data.DeviceProfile; | ||
29 | +import org.thingsboard.server.common.data.DeviceProfileType; | ||
30 | +import org.thingsboard.server.common.data.DeviceTransportType; | ||
31 | +import org.thingsboard.server.common.data.Tenant; | ||
32 | +import org.thingsboard.server.common.data.TransportPayloadType; | ||
33 | +import org.thingsboard.server.common.data.User; | ||
34 | +import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; | ||
35 | +import org.thingsboard.server.common.data.device.profile.DeviceProfileData; | ||
36 | +import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration; | ||
37 | +import org.thingsboard.server.common.data.security.Authority; | ||
38 | +import org.thingsboard.server.common.data.security.DeviceCredentials; | ||
39 | +import org.thingsboard.server.controller.AbstractControllerTest; | ||
40 | +import org.thingsboard.server.gen.transport.TransportProtos; | ||
41 | + | ||
42 | +import java.util.ArrayList; | ||
43 | +import java.util.List; | ||
44 | +import java.util.concurrent.atomic.AtomicInteger; | ||
45 | +import java.util.function.Supplier; | ||
46 | + | ||
47 | +import static org.junit.Assert.assertEquals; | ||
48 | +import static org.junit.Assert.assertNotNull; | ||
49 | +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; | ||
50 | + | ||
51 | +@Slf4j | ||
52 | +public abstract class AbstractMqttIntegrationTest extends AbstractControllerTest { | ||
53 | + | ||
54 | + protected static final String MQTT_URL = "tcp://localhost:1883"; | ||
55 | + | ||
56 | + private static final AtomicInteger atomicInteger = new AtomicInteger(2); | ||
57 | + | ||
58 | + protected Tenant savedTenant; | ||
59 | + protected User tenantAdmin; | ||
60 | + | ||
61 | + protected Device savedDevice; | ||
62 | + protected String accessToken; | ||
63 | + | ||
64 | + protected Device savedGateway; | ||
65 | + protected String gatewayAccessToken; | ||
66 | + | ||
67 | + protected void processBeforeTest(String deviceName, String gatewayName, TransportPayloadType payloadType, String telemetryTopic, String attributesTopic) throws Exception { | ||
68 | + loginSysAdmin(); | ||
69 | + | ||
70 | + Tenant tenant = new Tenant(); | ||
71 | + tenant.setTitle("My tenant"); | ||
72 | + savedTenant = doPost("/api/tenant", tenant, Tenant.class); | ||
73 | + Assert.assertNotNull(savedTenant); | ||
74 | + | ||
75 | + tenantAdmin = new User(); | ||
76 | + tenantAdmin.setAuthority(Authority.TENANT_ADMIN); | ||
77 | + tenantAdmin.setTenantId(savedTenant.getId()); | ||
78 | + tenantAdmin.setEmail("tenant" + atomicInteger.getAndIncrement() + "@thingsboard.org"); | ||
79 | + tenantAdmin.setFirstName("Joe"); | ||
80 | + tenantAdmin.setLastName("Downs"); | ||
81 | + | ||
82 | + tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1"); | ||
83 | + | ||
84 | + Device device = new Device(); | ||
85 | + device.setName(deviceName); | ||
86 | + device.setType("default"); | ||
87 | + | ||
88 | + Device gateway = new Device(); | ||
89 | + gateway.setName(gatewayName); | ||
90 | + gateway.setType("default"); | ||
91 | + ObjectNode additionalInfo = mapper.createObjectNode(); | ||
92 | + additionalInfo.put("gateway", true); | ||
93 | + gateway.setAdditionalInfo(additionalInfo); | ||
94 | + | ||
95 | + if (payloadType != null) { | ||
96 | + DeviceProfile mqttDeviceProfile = createMqttDeviceProfile(payloadType, telemetryTopic, attributesTopic); | ||
97 | + DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", mqttDeviceProfile, DeviceProfile.class); | ||
98 | + device.setType(savedDeviceProfile.getName()); | ||
99 | + device.setDeviceProfileId(savedDeviceProfile.getId()); | ||
100 | + gateway.setType(savedDeviceProfile.getName()); | ||
101 | + gateway.setDeviceProfileId(savedDeviceProfile.getId()); | ||
102 | + } | ||
103 | + | ||
104 | + savedDevice = doPost("/api/device", device, Device.class); | ||
105 | + | ||
106 | + DeviceCredentials deviceCredentials = | ||
107 | + doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class); | ||
108 | + | ||
109 | + savedGateway = doPost("/api/device", gateway, Device.class); | ||
110 | + | ||
111 | + DeviceCredentials gatewayCredentials = | ||
112 | + doGet("/api/device/" + savedGateway.getId().getId().toString() + "/credentials", DeviceCredentials.class); | ||
113 | + | ||
114 | + assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId()); | ||
115 | + accessToken = deviceCredentials.getCredentialsId(); | ||
116 | + assertNotNull(accessToken); | ||
117 | + | ||
118 | + assertEquals(savedGateway.getId(), gatewayCredentials.getDeviceId()); | ||
119 | + gatewayAccessToken = gatewayCredentials.getCredentialsId(); | ||
120 | + assertNotNull(gatewayAccessToken); | ||
121 | + | ||
122 | + } | ||
123 | + | ||
124 | + protected void processAfterTest() throws Exception { | ||
125 | + loginSysAdmin(); | ||
126 | + if (savedTenant != null) { | ||
127 | + doDelete("/api/tenant/" + savedTenant.getId().getId().toString()).andExpect(status().isOk()); | ||
128 | + } | ||
129 | + } | ||
130 | + | ||
131 | + protected MqttAsyncClient getMqttAsyncClient(String accessToken) throws MqttException { | ||
132 | + String clientId = MqttAsyncClient.generateClientId(); | ||
133 | + MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId, new MemoryPersistence()); | ||
134 | + | ||
135 | + MqttConnectOptions options = new MqttConnectOptions(); | ||
136 | + options.setUserName(accessToken); | ||
137 | + client.connect(options).waitForCompletion(); | ||
138 | + return client; | ||
139 | + } | ||
140 | + | ||
141 | + protected void publishMqttMsg(MqttAsyncClient client, byte[] payload, String topic) throws MqttException { | ||
142 | + MqttMessage message = new MqttMessage(); | ||
143 | + message.setPayload(payload); | ||
144 | + client.publish(topic, message); | ||
145 | + } | ||
146 | + | ||
147 | + protected List<TransportProtos.KeyValueProto> getKvProtos(List<String> expectedKeys) { | ||
148 | + List<TransportProtos.KeyValueProto> keyValueProtos = new ArrayList<>(); | ||
149 | + TransportProtos.KeyValueProto strKeyValueProto = getKeyValueProto(expectedKeys.get(0), "value1", TransportProtos.KeyValueType.STRING_V); | ||
150 | + TransportProtos.KeyValueProto boolKeyValueProto = getKeyValueProto(expectedKeys.get(1), "true", TransportProtos.KeyValueType.BOOLEAN_V); | ||
151 | + TransportProtos.KeyValueProto dblKeyValueProto = getKeyValueProto(expectedKeys.get(2), "3.0", TransportProtos.KeyValueType.DOUBLE_V); | ||
152 | + TransportProtos.KeyValueProto longKeyValueProto = getKeyValueProto(expectedKeys.get(3), "4", TransportProtos.KeyValueType.LONG_V); | ||
153 | + TransportProtos.KeyValueProto jsonKeyValueProto = getKeyValueProto(expectedKeys.get(4), "{\"someNumber\": 42, \"someArray\": [1,2,3], \"someNestedObject\": {\"key\": \"value\"}}", TransportProtos.KeyValueType.JSON_V); | ||
154 | + keyValueProtos.add(strKeyValueProto); | ||
155 | + keyValueProtos.add(boolKeyValueProto); | ||
156 | + keyValueProtos.add(dblKeyValueProto); | ||
157 | + keyValueProtos.add(longKeyValueProto); | ||
158 | + keyValueProtos.add(jsonKeyValueProto); | ||
159 | + return keyValueProtos; | ||
160 | + } | ||
161 | + | ||
162 | + protected TransportProtos.KeyValueProto getKeyValueProto(String key, String strValue, TransportProtos.KeyValueType type) { | ||
163 | + TransportProtos.KeyValueProto.Builder keyValueProtoBuilder = TransportProtos.KeyValueProto.newBuilder(); | ||
164 | + keyValueProtoBuilder.setKey(key); | ||
165 | + keyValueProtoBuilder.setType(type); | ||
166 | + switch (type) { | ||
167 | + case BOOLEAN_V: | ||
168 | + keyValueProtoBuilder.setBoolV(Boolean.parseBoolean(strValue)); | ||
169 | + break; | ||
170 | + case LONG_V: | ||
171 | + keyValueProtoBuilder.setLongV(Long.parseLong(strValue)); | ||
172 | + break; | ||
173 | + case DOUBLE_V: | ||
174 | + keyValueProtoBuilder.setDoubleV(Double.parseDouble(strValue)); | ||
175 | + break; | ||
176 | + case STRING_V: | ||
177 | + keyValueProtoBuilder.setStringV(strValue); | ||
178 | + break; | ||
179 | + case JSON_V: | ||
180 | + keyValueProtoBuilder.setJsonV(strValue); | ||
181 | + break; | ||
182 | + } | ||
183 | + return keyValueProtoBuilder.build(); | ||
184 | + } | ||
185 | + | ||
186 | + protected DeviceProfile createMqttDeviceProfile(TransportPayloadType transportPayloadType, String telemetryTopic, String attributesTopic) { | ||
187 | + DeviceProfile deviceProfile = new DeviceProfile(); | ||
188 | + deviceProfile.setName(transportPayloadType.name()); | ||
189 | + deviceProfile.setType(DeviceProfileType.DEFAULT); | ||
190 | + deviceProfile.setTransportType(DeviceTransportType.MQTT); | ||
191 | + deviceProfile.setDescription(transportPayloadType.name() + " Test"); | ||
192 | + DeviceProfileData deviceProfileData = new DeviceProfileData(); | ||
193 | + DefaultDeviceProfileConfiguration configuration = new DefaultDeviceProfileConfiguration(); | ||
194 | + MqttDeviceProfileTransportConfiguration transportConfiguration = new MqttDeviceProfileTransportConfiguration(); | ||
195 | + transportConfiguration.setTransportPayloadType(transportPayloadType); | ||
196 | + if (!StringUtils.isEmpty(telemetryTopic)) { | ||
197 | + transportConfiguration.setDeviceTelemetryTopic(telemetryTopic); | ||
198 | + } | ||
199 | + if (!StringUtils.isEmpty(attributesTopic)) { | ||
200 | + transportConfiguration.setDeviceAttributesTopic(attributesTopic); | ||
201 | + } | ||
202 | + deviceProfileData.setTransportConfiguration(transportConfiguration); | ||
203 | + deviceProfileData.setConfiguration(configuration); | ||
204 | + deviceProfile.setProfileData(deviceProfileData); | ||
205 | + deviceProfile.setDefault(false); | ||
206 | + deviceProfile.setDefaultRuleChainId(null); | ||
207 | + return deviceProfile; | ||
208 | + } | ||
209 | + | ||
210 | + protected TransportProtos.PostAttributeMsg getPostAttributeMsg(List<String> expectedKeys) { | ||
211 | + List<TransportProtos.KeyValueProto> kvProtos = getKvProtos(expectedKeys); | ||
212 | + TransportProtos.PostAttributeMsg.Builder builder = TransportProtos.PostAttributeMsg.newBuilder(); | ||
213 | + builder.addAllKv(kvProtos); | ||
214 | + return builder.build(); | ||
215 | + } | ||
216 | + | ||
217 | + protected <T> T doExecuteWithRetriesAndInterval(SupplierWithThrowable<T> supplier, int retries, int intervalMs) throws Exception { | ||
218 | + int count = 0; | ||
219 | + T result = null; | ||
220 | + Throwable lastException = null; | ||
221 | + while (count < retries) { | ||
222 | + try { | ||
223 | + result = supplier.get(); | ||
224 | + if (result != null) { | ||
225 | + return result; | ||
226 | + } | ||
227 | + } catch (Throwable e) { | ||
228 | + lastException = e; | ||
229 | + } | ||
230 | + count++; | ||
231 | + if (count < retries) { | ||
232 | + Thread.sleep(intervalMs); | ||
233 | + } | ||
234 | + } | ||
235 | + if (lastException != null) { | ||
236 | + throw new RuntimeException(lastException); | ||
237 | + } else { | ||
238 | + return result; | ||
239 | + } | ||
240 | + } | ||
241 | + | ||
242 | + @FunctionalInterface | ||
243 | + public interface SupplierWithThrowable<T> { | ||
244 | + T get() throws Throwable; | ||
245 | + } | ||
246 | +} |
@@ -33,7 +33,7 @@ public class MqttNoSqlTestSuite { | @@ -33,7 +33,7 @@ public class MqttNoSqlTestSuite { | ||
33 | 33 | ||
34 | @ClassRule | 34 | @ClassRule |
35 | public static CustomSqlUnit sqlUnit = new CustomSqlUnit( | 35 | public static CustomSqlUnit sqlUnit = new CustomSqlUnit( |
36 | - Arrays.asList("sql/schema-entities-hsql.sql", "sql/system-data.sql"), | 36 | + Arrays.asList("sql/schema-types-hsql.sql", "sql/schema-entities-hsql.sql", "sql/system-data.sql"), |
37 | "sql/hsql/drop-all-tables.sql", | 37 | "sql/hsql/drop-all-tables.sql", |
38 | "nosql-test.properties"); | 38 | "nosql-test.properties"); |
39 | 39 |
@@ -27,13 +27,17 @@ import java.util.Arrays; | @@ -27,13 +27,17 @@ import java.util.Arrays; | ||
27 | @RunWith(ClasspathSuite.class) | 27 | @RunWith(ClasspathSuite.class) |
28 | @ClasspathSuite.ClassnameFilters({ | 28 | @ClasspathSuite.ClassnameFilters({ |
29 | "org.thingsboard.server.mqtt.rpc.sql.*Test", | 29 | "org.thingsboard.server.mqtt.rpc.sql.*Test", |
30 | - "org.thingsboard.server.mqtt.telemetry.sql.*Test" | 30 | + "org.thingsboard.server.mqtt.telemetry.timeseries.sql.*Test", |
31 | + "org.thingsboard.server.mqtt.telemetry.attributes.sql.*Test", | ||
32 | + "org.thingsboard.server.mqtt.attributes.updates.sql.*Test", | ||
33 | + "org.thingsboard.server.mqtt.attributes.request.sql.*Test", | ||
34 | + "org.thingsboard.server.mqtt.claim.sql.*Test" | ||
31 | }) | 35 | }) |
32 | public class MqttSqlTestSuite { | 36 | public class MqttSqlTestSuite { |
33 | 37 | ||
34 | @ClassRule | 38 | @ClassRule |
35 | public static CustomSqlUnit sqlUnit = new CustomSqlUnit( | 39 | public static CustomSqlUnit sqlUnit = new CustomSqlUnit( |
36 | - Arrays.asList("sql/schema-ts-hsql.sql", "sql/schema-entities-hsql.sql", "sql/system-data.sql"), | 40 | + Arrays.asList("sql/schema-types-hsql.sql", "sql/schema-ts-hsql.sql", "sql/schema-entities-hsql.sql", "sql/system-data.sql"), |
37 | "sql/hsql/drop-all-tables.sql", | 41 | "sql/hsql/drop-all-tables.sql", |
38 | "sql-test.properties"); | 42 | "sql-test.properties"); |
39 | 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.mqtt.attributes; | ||
17 | + | ||
18 | +import lombok.extern.slf4j.Slf4j; | ||
19 | +import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; | ||
20 | +import org.eclipse.paho.client.mqttv3.MqttCallback; | ||
21 | +import org.eclipse.paho.client.mqttv3.MqttMessage; | ||
22 | +import org.thingsboard.server.common.data.TransportPayloadType; | ||
23 | +import org.thingsboard.server.gen.transport.TransportProtos; | ||
24 | +import org.thingsboard.server.mqtt.AbstractMqttIntegrationTest; | ||
25 | + | ||
26 | +import java.util.ArrayList; | ||
27 | +import java.util.List; | ||
28 | +import java.util.concurrent.CountDownLatch; | ||
29 | + | ||
30 | +@Slf4j | ||
31 | +public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqttIntegrationTest { | ||
32 | + | ||
33 | + protected static final String POST_ATTRIBUTES_PAYLOAD = "{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73," + | ||
34 | + "\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}}"; | ||
35 | + | ||
36 | + protected void processBeforeTest(String deviceName, String gatewayName, TransportPayloadType payloadType, String telemetryTopic, String attributesTopic) throws Exception { | ||
37 | + super.processBeforeTest(deviceName, gatewayName, payloadType, telemetryTopic, attributesTopic); | ||
38 | + } | ||
39 | + | ||
40 | + protected void processAfterTest() throws Exception { | ||
41 | + super.processAfterTest(); | ||
42 | + } | ||
43 | + | ||
44 | + protected List<TransportProtos.TsKvProto> getTsKvProtoList() { | ||
45 | + TransportProtos.TsKvProto tsKvProtoAttribute1 = getTsKvProto("attribute1", "value1", TransportProtos.KeyValueType.STRING_V); | ||
46 | + TransportProtos.TsKvProto tsKvProtoAttribute2 = getTsKvProto("attribute2", "true", TransportProtos.KeyValueType.BOOLEAN_V); | ||
47 | + TransportProtos.TsKvProto tsKvProtoAttribute3 = getTsKvProto("attribute3", "42.0", TransportProtos.KeyValueType.DOUBLE_V); | ||
48 | + TransportProtos.TsKvProto tsKvProtoAttribute4 = getTsKvProto("attribute4", "73", TransportProtos.KeyValueType.LONG_V); | ||
49 | + TransportProtos.TsKvProto tsKvProtoAttribute5 = getTsKvProto("attribute5", "{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}", TransportProtos.KeyValueType.JSON_V); | ||
50 | + List<TransportProtos.TsKvProto> tsKvProtoList = new ArrayList<>(); | ||
51 | + tsKvProtoList.add(tsKvProtoAttribute1); | ||
52 | + tsKvProtoList.add(tsKvProtoAttribute2); | ||
53 | + tsKvProtoList.add(tsKvProtoAttribute3); | ||
54 | + tsKvProtoList.add(tsKvProtoAttribute4); | ||
55 | + tsKvProtoList.add(tsKvProtoAttribute5); | ||
56 | + return tsKvProtoList; | ||
57 | + } | ||
58 | + | ||
59 | + | ||
60 | + protected TransportProtos.TsKvProto getTsKvProto(String key, String value, TransportProtos.KeyValueType keyValueType) { | ||
61 | + TransportProtos.TsKvProto.Builder tsKvProtoBuilder = TransportProtos.TsKvProto.newBuilder(); | ||
62 | + TransportProtos.KeyValueProto keyValueProto = getKeyValueProto(key, value, keyValueType); | ||
63 | + tsKvProtoBuilder.setKv(keyValueProto); | ||
64 | + return tsKvProtoBuilder.build(); | ||
65 | + } | ||
66 | + | ||
67 | + protected TestMqttCallback getTestMqttCallback() { | ||
68 | + CountDownLatch latch = new CountDownLatch(1); | ||
69 | + return new TestMqttCallback(latch); | ||
70 | + } | ||
71 | + | ||
72 | + protected static class TestMqttCallback implements MqttCallback { | ||
73 | + | ||
74 | + private final CountDownLatch latch; | ||
75 | + private Integer qoS; | ||
76 | + private byte[] payloadBytes; | ||
77 | + | ||
78 | + TestMqttCallback(CountDownLatch latch) { | ||
79 | + this.latch = latch; | ||
80 | + } | ||
81 | + | ||
82 | + public int getQoS() { | ||
83 | + return qoS; | ||
84 | + } | ||
85 | + | ||
86 | + public byte[] getPayloadBytes() { | ||
87 | + return payloadBytes; | ||
88 | + } | ||
89 | + | ||
90 | + public CountDownLatch getLatch() { | ||
91 | + return latch; | ||
92 | + } | ||
93 | + | ||
94 | + @Override | ||
95 | + public void connectionLost(Throwable throwable) { | ||
96 | + } | ||
97 | + | ||
98 | + @Override | ||
99 | + public void messageArrived(String requestTopic, MqttMessage mqttMessage) throws Exception { | ||
100 | + qoS = mqttMessage.getQos(); | ||
101 | + payloadBytes = mqttMessage.getPayload(); | ||
102 | + latch.countDown(); | ||
103 | + } | ||
104 | + | ||
105 | + @Override | ||
106 | + public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) { | ||
107 | + | ||
108 | + } | ||
109 | + } | ||
110 | + | ||
111 | +} |
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.mqtt.attributes.request; | ||
17 | + | ||
18 | +import com.google.protobuf.InvalidProtocolBufferException; | ||
19 | +import io.netty.handler.codec.mqtt.MqttQoS; | ||
20 | +import lombok.extern.slf4j.Slf4j; | ||
21 | +import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; | ||
22 | +import org.eclipse.paho.client.mqttv3.MqttAsyncClient; | ||
23 | +import org.eclipse.paho.client.mqttv3.MqttCallback; | ||
24 | +import org.eclipse.paho.client.mqttv3.MqttException; | ||
25 | +import org.eclipse.paho.client.mqttv3.MqttMessage; | ||
26 | +import org.junit.After; | ||
27 | +import org.junit.Before; | ||
28 | +import org.junit.Test; | ||
29 | +import org.thingsboard.server.common.data.Device; | ||
30 | +import org.thingsboard.server.common.data.device.profile.MqttTopics; | ||
31 | +import org.thingsboard.server.dao.util.mapping.JacksonUtil; | ||
32 | +import org.thingsboard.server.mqtt.attributes.AbstractMqttAttributesIntegrationTest; | ||
33 | + | ||
34 | +import java.nio.charset.StandardCharsets; | ||
35 | +import java.util.concurrent.CountDownLatch; | ||
36 | +import java.util.concurrent.TimeUnit; | ||
37 | + | ||
38 | +import static org.junit.Assert.assertEquals; | ||
39 | +import static org.junit.Assert.assertFalse; | ||
40 | +import static org.junit.Assert.assertNotNull; | ||
41 | +import static org.junit.Assert.assertTrue; | ||
42 | +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; | ||
43 | + | ||
44 | +@Slf4j | ||
45 | +public abstract class AbstractMqttAttributesRequestIntegrationTest extends AbstractMqttAttributesIntegrationTest { | ||
46 | + | ||
47 | + @Before | ||
48 | + public void beforeTest() throws Exception { | ||
49 | + processBeforeTest("Test Request attribute values from the server", "Gateway Test Request attribute values from the server", null, null, null); | ||
50 | + } | ||
51 | + | ||
52 | + @After | ||
53 | + public void afterTest() throws Exception { | ||
54 | + processAfterTest(); | ||
55 | + } | ||
56 | + | ||
57 | + @Test | ||
58 | + public void testRequestAttributesValuesFromTheServer() throws Exception { | ||
59 | + processTestRequestAttributesValuesFromTheServer(); | ||
60 | + } | ||
61 | + | ||
62 | + @Test | ||
63 | + public void testRequestAttributesValuesFromTheServerGateway() throws Exception { | ||
64 | + processTestGatewayRequestAttributesValuesFromTheServer(); | ||
65 | + } | ||
66 | + | ||
67 | + protected void processTestRequestAttributesValuesFromTheServer() throws Exception { | ||
68 | + | ||
69 | + MqttAsyncClient client = getMqttAsyncClient(accessToken); | ||
70 | + | ||
71 | + postAttributesAndSubscribeToTopic(savedDevice, client); | ||
72 | + | ||
73 | + Thread.sleep(1000); | ||
74 | + | ||
75 | + TestMqttCallback callback = getTestMqttCallback(); | ||
76 | + client.setCallback(callback); | ||
77 | + | ||
78 | + validateResponse(client, callback.getLatch(), callback); | ||
79 | + } | ||
80 | + | ||
81 | + protected void processTestGatewayRequestAttributesValuesFromTheServer() throws Exception { | ||
82 | + | ||
83 | + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); | ||
84 | + | ||
85 | + postGatewayDeviceClientAttributes(client); | ||
86 | + | ||
87 | + Device savedDevice = doExecuteWithRetriesAndInterval(() -> doGet("/api/tenant/devices?deviceName=" + "Gateway Device Request Attributes", Device.class), | ||
88 | + 20, | ||
89 | + 100); | ||
90 | + | ||
91 | + assertNotNull(savedDevice); | ||
92 | + | ||
93 | + Thread.sleep(1000); | ||
94 | + | ||
95 | + doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk()); | ||
96 | + | ||
97 | + Thread.sleep(1000); | ||
98 | + | ||
99 | + client.subscribe(MqttTopics.GATEWAY_ATTRIBUTES_RESPONSE_TOPIC, MqttQoS.AT_LEAST_ONCE.value()); | ||
100 | + | ||
101 | + TestMqttCallback clientAttributesCallback = getTestMqttCallback(); | ||
102 | + client.setCallback(clientAttributesCallback); | ||
103 | + validateClientResponseGateway(client, clientAttributesCallback); | ||
104 | + | ||
105 | + TestMqttCallback sharedAttributesCallback = getTestMqttCallback(); | ||
106 | + client.setCallback(sharedAttributesCallback); | ||
107 | + validateSharedResponseGateway(client, sharedAttributesCallback); | ||
108 | + } | ||
109 | + | ||
110 | + protected void postAttributesAndSubscribeToTopic(Device savedDevice, MqttAsyncClient client) throws Exception { | ||
111 | + doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk()); | ||
112 | + client.publish(MqttTopics.DEVICE_ATTRIBUTES_TOPIC, new MqttMessage(POST_ATTRIBUTES_PAYLOAD.getBytes())); | ||
113 | + client.subscribe(MqttTopics.DEVICE_ATTRIBUTES_RESPONSES_TOPIC, MqttQoS.AT_MOST_ONCE.value()); | ||
114 | + } | ||
115 | + | ||
116 | + protected void postGatewayDeviceClientAttributes(MqttAsyncClient client) throws Exception { | ||
117 | + String postClientAttributes = "{\"" + "Gateway Device Request Attributes" + "\":{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73,\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}}}"; | ||
118 | + client.publish(MqttTopics.GATEWAY_ATTRIBUTES_TOPIC, new MqttMessage(postClientAttributes.getBytes())); | ||
119 | + } | ||
120 | + | ||
121 | + protected void validateResponse(MqttAsyncClient client, CountDownLatch latch, TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException { | ||
122 | + String keys = "attribute1,attribute2,attribute3,attribute4,attribute5"; | ||
123 | + String payloadStr = "{\"clientKeys\":\"" + keys + "\", \"sharedKeys\":\"" + keys + "\"}"; | ||
124 | + MqttMessage mqttMessage = new MqttMessage(); | ||
125 | + mqttMessage.setPayload(payloadStr.getBytes()); | ||
126 | + client.publish(MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX + "1", mqttMessage); | ||
127 | + latch.await(3, TimeUnit.SECONDS); | ||
128 | + assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS()); | ||
129 | + String expectedRequestPayload = "{\"client\":{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73,\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}},\"shared\":{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73,\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}}}"; | ||
130 | + assertEquals(JacksonUtil.toJsonNode(expectedRequestPayload), JacksonUtil.toJsonNode(new String(callback.getPayloadBytes(), StandardCharsets.UTF_8))); | ||
131 | + } | ||
132 | + | ||
133 | + protected void validateClientResponseGateway(MqttAsyncClient client, TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException { | ||
134 | + String payloadStr = "{\"id\": 1, \"device\": \"" + "Gateway Device Request Attributes" + "\", \"client\": true, \"keys\": [\"attribute1\", \"attribute2\", \"attribute3\", \"attribute4\", \"attribute5\"]}"; | ||
135 | + MqttMessage mqttMessage = new MqttMessage(); | ||
136 | + mqttMessage.setPayload(payloadStr.getBytes()); | ||
137 | + client.publish(MqttTopics.GATEWAY_ATTRIBUTES_REQUEST_TOPIC, mqttMessage); | ||
138 | + callback.getLatch().await(3, TimeUnit.SECONDS); | ||
139 | + assertEquals(MqttQoS.AT_LEAST_ONCE.value(), callback.getQoS()); | ||
140 | + String expectedRequestPayload = "{\"id\":1,\"device\":\"" + "Gateway Device Request Attributes" + "\",\"values\":{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73,\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}}}"; | ||
141 | + assertEquals(JacksonUtil.toJsonNode(expectedRequestPayload), JacksonUtil.toJsonNode(new String(callback.getPayloadBytes(), StandardCharsets.UTF_8))); | ||
142 | + } | ||
143 | + | ||
144 | + protected void validateSharedResponseGateway(MqttAsyncClient client, TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException { | ||
145 | + String payloadStr = "{\"id\": 1, \"device\": \"" + "Gateway Device Request Attributes" + "\", \"client\": false, \"keys\": [\"attribute1\", \"attribute2\", \"attribute3\", \"attribute4\", \"attribute5\"]}"; | ||
146 | + MqttMessage mqttMessage = new MqttMessage(); | ||
147 | + mqttMessage.setPayload(payloadStr.getBytes()); | ||
148 | + client.publish(MqttTopics.GATEWAY_ATTRIBUTES_REQUEST_TOPIC, mqttMessage); | ||
149 | + callback.getLatch().await(3, TimeUnit.SECONDS); | ||
150 | + assertEquals(MqttQoS.AT_LEAST_ONCE.value(), callback.getQoS()); | ||
151 | + String expectedRequestPayload = "{\"id\":1,\"device\":\"" + "Gateway Device Request Attributes" + "\",\"values\":{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73,\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}}}"; | ||
152 | + assertEquals(JacksonUtil.toJsonNode(expectedRequestPayload), JacksonUtil.toJsonNode(new String(callback.getPayloadBytes(), StandardCharsets.UTF_8))); | ||
153 | + } | ||
154 | +} |
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.mqtt.attributes.request; | ||
17 | + | ||
18 | +import lombok.extern.slf4j.Slf4j; | ||
19 | +import org.junit.After; | ||
20 | +import org.junit.Before; | ||
21 | +import org.junit.Ignore; | ||
22 | +import org.junit.Test; | ||
23 | +import org.thingsboard.server.common.data.TransportPayloadType; | ||
24 | + | ||
25 | +import static org.junit.Assert.assertEquals; | ||
26 | +import static org.junit.Assert.assertNotNull; | ||
27 | +import static org.junit.Assert.assertTrue; | ||
28 | + | ||
29 | +@Slf4j | ||
30 | +public abstract class AbstractMqttAttributesRequestJsonIntegrationTest extends AbstractMqttAttributesRequestIntegrationTest { | ||
31 | + | ||
32 | + @Before | ||
33 | + public void beforeTest() throws Exception { | ||
34 | + processBeforeTest("Test Request attribute values from the server json", "Gateway Test Request attribute values from the server json", TransportPayloadType.JSON, null, null); | ||
35 | + } | ||
36 | + | ||
37 | + @After | ||
38 | + public void afterTest() throws Exception { | ||
39 | + processAfterTest(); | ||
40 | + } | ||
41 | + | ||
42 | + @Test | ||
43 | + public void testRequestAttributesValuesFromTheServer() throws Exception { | ||
44 | + processTestRequestAttributesValuesFromTheServer(); | ||
45 | + } | ||
46 | + | ||
47 | + @Test | ||
48 | + public void testRequestAttributesValuesFromTheServerGateway() throws Exception { | ||
49 | + processTestGatewayRequestAttributesValuesFromTheServer(); | ||
50 | + } | ||
51 | +} |
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.mqtt.attributes.request; | ||
17 | + | ||
18 | +import com.google.protobuf.InvalidProtocolBufferException; | ||
19 | +import io.netty.handler.codec.mqtt.MqttQoS; | ||
20 | +import lombok.extern.slf4j.Slf4j; | ||
21 | +import org.eclipse.paho.client.mqttv3.MqttAsyncClient; | ||
22 | +import org.eclipse.paho.client.mqttv3.MqttException; | ||
23 | +import org.eclipse.paho.client.mqttv3.MqttMessage; | ||
24 | +import org.junit.After; | ||
25 | +import org.junit.Before; | ||
26 | +import org.junit.Test; | ||
27 | +import org.thingsboard.server.common.data.Device; | ||
28 | +import org.thingsboard.server.common.data.TransportPayloadType; | ||
29 | +import org.thingsboard.server.common.data.device.profile.MqttTopics; | ||
30 | +import org.thingsboard.server.gen.transport.TransportApiProtos; | ||
31 | +import org.thingsboard.server.gen.transport.TransportProtos; | ||
32 | + | ||
33 | +import java.util.ArrayList; | ||
34 | +import java.util.Arrays; | ||
35 | +import java.util.List; | ||
36 | +import java.util.concurrent.CountDownLatch; | ||
37 | +import java.util.concurrent.TimeUnit; | ||
38 | +import java.util.stream.Collectors; | ||
39 | + | ||
40 | +import static org.junit.Assert.assertEquals; | ||
41 | +import static org.junit.Assert.assertTrue; | ||
42 | +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; | ||
43 | + | ||
44 | +@Slf4j | ||
45 | +public abstract class AbstractMqttAttributesRequestProtoIntegrationTest extends AbstractMqttAttributesRequestIntegrationTest { | ||
46 | + | ||
47 | + @Before | ||
48 | + public void beforeTest() throws Exception { | ||
49 | + processBeforeTest("Test Request attribute values from the server proto", "Gateway Test Request attribute values from the server proto", TransportPayloadType.PROTOBUF, null, null); | ||
50 | + } | ||
51 | + | ||
52 | + @After | ||
53 | + public void afterTest() throws Exception { | ||
54 | + processAfterTest(); | ||
55 | + } | ||
56 | + | ||
57 | + @Test | ||
58 | + public void testRequestAttributesValuesFromTheServer() throws Exception { | ||
59 | + processTestRequestAttributesValuesFromTheServer(); | ||
60 | + } | ||
61 | + | ||
62 | + | ||
63 | + @Test | ||
64 | + public void testRequestAttributesValuesFromTheServerGateway() throws Exception { | ||
65 | + processTestGatewayRequestAttributesValuesFromTheServer(); | ||
66 | + } | ||
67 | + | ||
68 | + protected void postAttributesAndSubscribeToTopic(Device savedDevice, MqttAsyncClient client) throws Exception { | ||
69 | + doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk()); | ||
70 | + String keys = "attribute1,attribute2,attribute3,attribute4,attribute5"; | ||
71 | + List<String> expectedKeys = Arrays.asList(keys.split(",")); | ||
72 | + TransportProtos.PostAttributeMsg postAttributeMsg = getPostAttributeMsg(expectedKeys); | ||
73 | + byte[] payload = postAttributeMsg.toByteArray(); | ||
74 | + client.publish(MqttTopics.DEVICE_ATTRIBUTES_TOPIC, new MqttMessage(payload)); | ||
75 | + client.subscribe(MqttTopics.DEVICE_ATTRIBUTES_RESPONSES_TOPIC, MqttQoS.AT_MOST_ONCE.value()); | ||
76 | + } | ||
77 | + | ||
78 | + protected void postGatewayDeviceClientAttributes(MqttAsyncClient client) throws Exception { | ||
79 | + String keys = "attribute1,attribute2,attribute3,attribute4,attribute5"; | ||
80 | + List<String> expectedKeys = Arrays.asList(keys.split(",")); | ||
81 | + TransportProtos.PostAttributeMsg postAttributeMsg = getPostAttributeMsg(expectedKeys); | ||
82 | + TransportApiProtos.AttributesMsg.Builder attributesMsgBuilder = TransportApiProtos.AttributesMsg.newBuilder(); | ||
83 | + attributesMsgBuilder.setDeviceName("Gateway Device Request Attributes"); | ||
84 | + attributesMsgBuilder.setMsg(postAttributeMsg); | ||
85 | + TransportApiProtos.AttributesMsg attributesMsg = attributesMsgBuilder.build(); | ||
86 | + TransportApiProtos.GatewayAttributesMsg.Builder gatewayAttributeMsgBuilder = TransportApiProtos.GatewayAttributesMsg.newBuilder(); | ||
87 | + gatewayAttributeMsgBuilder.addMsg(attributesMsg); | ||
88 | + byte[] bytes = gatewayAttributeMsgBuilder.build().toByteArray(); | ||
89 | + client.publish(MqttTopics.GATEWAY_ATTRIBUTES_TOPIC, new MqttMessage(bytes)); | ||
90 | + } | ||
91 | + | ||
92 | + protected void validateResponse(MqttAsyncClient client, CountDownLatch latch, TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException { | ||
93 | + String keys = "attribute1,attribute2,attribute3,attribute4,attribute5"; | ||
94 | + TransportApiProtos.AttributesRequest.Builder attributesRequestBuilder = TransportApiProtos.AttributesRequest.newBuilder(); | ||
95 | + attributesRequestBuilder.setClientKeys(keys); | ||
96 | + attributesRequestBuilder.setSharedKeys(keys); | ||
97 | + TransportApiProtos.AttributesRequest attributesRequest = attributesRequestBuilder.build(); | ||
98 | + MqttMessage mqttMessage = new MqttMessage(); | ||
99 | + mqttMessage.setPayload(attributesRequest.toByteArray()); | ||
100 | + client.publish(MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX + "1", mqttMessage); | ||
101 | + latch.await(3, TimeUnit.SECONDS); | ||
102 | + assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS()); | ||
103 | + TransportProtos.GetAttributeResponseMsg expectedAttributesResponse = getExpectedAttributeResponseMsg(); | ||
104 | + TransportProtos.GetAttributeResponseMsg actualAttributesResponse = TransportProtos.GetAttributeResponseMsg.parseFrom(callback.getPayloadBytes()); | ||
105 | + assertEquals(expectedAttributesResponse.getRequestId(), actualAttributesResponse.getRequestId()); | ||
106 | + List<TransportProtos.KeyValueProto> expectedClientKeyValueProtos = expectedAttributesResponse.getClientAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); | ||
107 | + List<TransportProtos.KeyValueProto> expectedSharedKeyValueProtos = expectedAttributesResponse.getSharedAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); | ||
108 | + List<TransportProtos.KeyValueProto> actualClientKeyValueProtos = actualAttributesResponse.getClientAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); | ||
109 | + List<TransportProtos.KeyValueProto> actualSharedKeyValueProtos = actualAttributesResponse.getSharedAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); | ||
110 | + assertTrue(actualClientKeyValueProtos.containsAll(expectedClientKeyValueProtos)); | ||
111 | + assertTrue(actualSharedKeyValueProtos.containsAll(expectedSharedKeyValueProtos)); | ||
112 | + } | ||
113 | + | ||
114 | + protected void validateClientResponseGateway(MqttAsyncClient client, TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException { | ||
115 | + String keys = "attribute1,attribute2,attribute3,attribute4,attribute5"; | ||
116 | + TransportApiProtos.GatewayAttributesRequestMsg gatewayAttributesRequestMsg = getGatewayAttributesRequestMsg(keys, true); | ||
117 | + client.publish(MqttTopics.GATEWAY_ATTRIBUTES_REQUEST_TOPIC, new MqttMessage(gatewayAttributesRequestMsg.toByteArray())); | ||
118 | + callback.getLatch().await(3, TimeUnit.SECONDS); | ||
119 | + assertEquals(MqttQoS.AT_LEAST_ONCE.value(), callback.getQoS()); | ||
120 | + TransportApiProtos.GatewayAttributeResponseMsg expectedGatewayAttributeResponseMsg = getExpectedGatewayAttributeResponseMsg(true); | ||
121 | + TransportApiProtos.GatewayAttributeResponseMsg actualGatewayAttributeResponseMsg = TransportApiProtos.GatewayAttributeResponseMsg.parseFrom(callback.getPayloadBytes()); | ||
122 | + assertEquals(expectedGatewayAttributeResponseMsg.getDeviceName(), actualGatewayAttributeResponseMsg.getDeviceName()); | ||
123 | + | ||
124 | + TransportProtos.GetAttributeResponseMsg expectedResponseMsg = expectedGatewayAttributeResponseMsg.getResponseMsg(); | ||
125 | + TransportProtos.GetAttributeResponseMsg actualResponseMsg = actualGatewayAttributeResponseMsg.getResponseMsg(); | ||
126 | + assertEquals(expectedResponseMsg.getRequestId(), actualResponseMsg.getRequestId()); | ||
127 | + | ||
128 | + List<TransportProtos.KeyValueProto> expectedClientKeyValueProtos = expectedResponseMsg.getClientAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); | ||
129 | + List<TransportProtos.KeyValueProto> actualClientKeyValueProtos = actualResponseMsg.getClientAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); | ||
130 | + assertTrue(actualClientKeyValueProtos.containsAll(expectedClientKeyValueProtos)); | ||
131 | + } | ||
132 | + | ||
133 | + protected void validateSharedResponseGateway(MqttAsyncClient client, TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException { | ||
134 | + String keys = "attribute1,attribute2,attribute3,attribute4,attribute5"; | ||
135 | + TransportApiProtos.GatewayAttributesRequestMsg gatewayAttributesRequestMsg = getGatewayAttributesRequestMsg(keys, false); | ||
136 | + client.publish(MqttTopics.GATEWAY_ATTRIBUTES_REQUEST_TOPIC, new MqttMessage(gatewayAttributesRequestMsg.toByteArray())); | ||
137 | + callback.getLatch().await(3, TimeUnit.SECONDS); | ||
138 | + assertEquals(MqttQoS.AT_LEAST_ONCE.value(), callback.getQoS()); | ||
139 | + TransportApiProtos.GatewayAttributeResponseMsg expectedGatewayAttributeResponseMsg = getExpectedGatewayAttributeResponseMsg(false); | ||
140 | + TransportApiProtos.GatewayAttributeResponseMsg actualGatewayAttributeResponseMsg = TransportApiProtos.GatewayAttributeResponseMsg.parseFrom(callback.getPayloadBytes()); | ||
141 | + assertEquals(expectedGatewayAttributeResponseMsg.getDeviceName(), actualGatewayAttributeResponseMsg.getDeviceName()); | ||
142 | + | ||
143 | + TransportProtos.GetAttributeResponseMsg expectedResponseMsg = expectedGatewayAttributeResponseMsg.getResponseMsg(); | ||
144 | + TransportProtos.GetAttributeResponseMsg actualResponseMsg = actualGatewayAttributeResponseMsg.getResponseMsg(); | ||
145 | + assertEquals(expectedResponseMsg.getRequestId(), actualResponseMsg.getRequestId()); | ||
146 | + | ||
147 | + List<TransportProtos.KeyValueProto> expectedSharedKeyValueProtos = expectedResponseMsg.getSharedAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); | ||
148 | + List<TransportProtos.KeyValueProto> actualSharedKeyValueProtos = actualResponseMsg.getSharedAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); | ||
149 | + | ||
150 | + assertTrue(actualSharedKeyValueProtos.containsAll(expectedSharedKeyValueProtos)); | ||
151 | + } | ||
152 | + | ||
153 | + private TransportApiProtos.GatewayAttributesRequestMsg getGatewayAttributesRequestMsg(String keys, boolean client) { | ||
154 | + return TransportApiProtos.GatewayAttributesRequestMsg.newBuilder() | ||
155 | + .setClient(client) | ||
156 | + .addAllKeys(Arrays.asList(keys.split(","))) | ||
157 | + .setDeviceName("Gateway Device Request Attributes") | ||
158 | + .setId(1).build(); | ||
159 | + } | ||
160 | + | ||
161 | + private TransportProtos.GetAttributeResponseMsg getExpectedAttributeResponseMsg() { | ||
162 | + TransportProtos.GetAttributeResponseMsg.Builder result = TransportProtos.GetAttributeResponseMsg.newBuilder(); | ||
163 | + List<TransportProtos.TsKvProto> tsKvProtoList = getTsKvProtoList(); | ||
164 | + result.addAllClientAttributeList(tsKvProtoList); | ||
165 | + result.addAllSharedAttributeList(tsKvProtoList); | ||
166 | + result.setRequestId(1); | ||
167 | + return result.build(); | ||
168 | + } | ||
169 | + | ||
170 | + private TransportApiProtos.GatewayAttributeResponseMsg getExpectedGatewayAttributeResponseMsg(boolean client) { | ||
171 | + TransportApiProtos.GatewayAttributeResponseMsg.Builder gatewayAttributeResponseMsg = TransportApiProtos.GatewayAttributeResponseMsg.newBuilder(); | ||
172 | + TransportProtos.GetAttributeResponseMsg.Builder getAttributeResponseMsgBuilder = TransportProtos.GetAttributeResponseMsg.newBuilder(); | ||
173 | + List<TransportProtos.TsKvProto> tsKvProtoList = getTsKvProtoList(); | ||
174 | + if (client) { | ||
175 | + getAttributeResponseMsgBuilder.addAllClientAttributeList(tsKvProtoList); | ||
176 | + } else { | ||
177 | + getAttributeResponseMsgBuilder.addAllSharedAttributeList(tsKvProtoList); | ||
178 | + } | ||
179 | + getAttributeResponseMsgBuilder.setRequestId(1); | ||
180 | + TransportProtos.GetAttributeResponseMsg getAttributeResponseMsg = getAttributeResponseMsgBuilder.build(); | ||
181 | + gatewayAttributeResponseMsg.setDeviceName("Gateway Device Request Attributes"); | ||
182 | + gatewayAttributeResponseMsg.setResponseMsg(getAttributeResponseMsg); | ||
183 | + return gatewayAttributeResponseMsg.build(); | ||
184 | + } | ||
185 | + | ||
186 | + protected List<TransportProtos.KeyValueProto> getKvProtos(List<String> expectedKeys) { | ||
187 | + List<TransportProtos.KeyValueProto> keyValueProtos = new ArrayList<>(); | ||
188 | + TransportProtos.KeyValueProto strKeyValueProto = getKeyValueProto(expectedKeys.get(0), "value1", TransportProtos.KeyValueType.STRING_V); | ||
189 | + TransportProtos.KeyValueProto boolKeyValueProto = getKeyValueProto(expectedKeys.get(1), "true", TransportProtos.KeyValueType.BOOLEAN_V); | ||
190 | + TransportProtos.KeyValueProto dblKeyValueProto = getKeyValueProto(expectedKeys.get(2), "42.0", TransportProtos.KeyValueType.DOUBLE_V); | ||
191 | + TransportProtos.KeyValueProto longKeyValueProto = getKeyValueProto(expectedKeys.get(3), "73", TransportProtos.KeyValueType.LONG_V); | ||
192 | + TransportProtos.KeyValueProto jsonKeyValueProto = getKeyValueProto(expectedKeys.get(4), "{\"someNumber\": 42, \"someArray\": [1,2,3], \"someNestedObject\": {\"key\": \"value\"}}", TransportProtos.KeyValueType.JSON_V); | ||
193 | + keyValueProtos.add(strKeyValueProto); | ||
194 | + keyValueProtos.add(boolKeyValueProto); | ||
195 | + keyValueProtos.add(dblKeyValueProto); | ||
196 | + keyValueProtos.add(longKeyValueProto); | ||
197 | + keyValueProtos.add(jsonKeyValueProto); | ||
198 | + return keyValueProtos; | ||
199 | + } | ||
200 | + | ||
201 | +} |
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.mqtt.attributes.request.nosql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoNoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.attributes.request.AbstractMqttAttributesRequestIntegrationTest; | ||
20 | + | ||
21 | + | ||
22 | +@DaoNoSqlTest | ||
23 | +public class MqttAttributesRequestNoSqlIntegrationTest extends AbstractMqttAttributesRequestIntegrationTest { | ||
24 | +} |
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.mqtt.attributes.request.sql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.attributes.request.AbstractMqttAttributesRequestIntegrationTest; | ||
20 | +import org.thingsboard.server.mqtt.attributes.request.AbstractMqttAttributesRequestJsonIntegrationTest; | ||
21 | + | ||
22 | +@DaoSqlTest | ||
23 | +public class MqttAttributesRequestJsonSqlIntegrationTest extends AbstractMqttAttributesRequestJsonIntegrationTest { | ||
24 | +} |
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.mqtt.attributes.request.sql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.attributes.request.AbstractMqttAttributesRequestJsonIntegrationTest; | ||
20 | +import org.thingsboard.server.mqtt.attributes.request.AbstractMqttAttributesRequestProtoIntegrationTest; | ||
21 | + | ||
22 | +@DaoSqlTest | ||
23 | +public class MqttAttributesRequestProtoSqlIntegrationTest extends AbstractMqttAttributesRequestProtoIntegrationTest { | ||
24 | +} |
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.mqtt.attributes.request.sql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.attributes.request.AbstractMqttAttributesRequestIntegrationTest; | ||
20 | + | ||
21 | +@DaoSqlTest | ||
22 | +public class MqttAttributesRequestSqlIntegrationTest extends AbstractMqttAttributesRequestIntegrationTest { | ||
23 | +} |
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.mqtt.attributes.updates; | ||
17 | + | ||
18 | +import com.google.protobuf.InvalidProtocolBufferException; | ||
19 | +import io.netty.handler.codec.mqtt.MqttQoS; | ||
20 | +import lombok.extern.slf4j.Slf4j; | ||
21 | +import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; | ||
22 | +import org.eclipse.paho.client.mqttv3.MqttAsyncClient; | ||
23 | +import org.eclipse.paho.client.mqttv3.MqttCallback; | ||
24 | +import org.eclipse.paho.client.mqttv3.MqttException; | ||
25 | +import org.eclipse.paho.client.mqttv3.MqttMessage; | ||
26 | +import org.junit.After; | ||
27 | +import org.junit.Before; | ||
28 | +import org.junit.Test; | ||
29 | +import org.thingsboard.server.common.data.Device; | ||
30 | +import org.thingsboard.server.common.data.TransportPayloadType; | ||
31 | +import org.thingsboard.server.common.data.device.profile.MqttTopics; | ||
32 | +import org.thingsboard.server.dao.util.mapping.JacksonUtil; | ||
33 | +import org.thingsboard.server.mqtt.attributes.AbstractMqttAttributesIntegrationTest; | ||
34 | + | ||
35 | +import java.nio.charset.StandardCharsets; | ||
36 | +import java.util.concurrent.CountDownLatch; | ||
37 | +import java.util.concurrent.TimeUnit; | ||
38 | + | ||
39 | +import static org.junit.Assert.assertEquals; | ||
40 | +import static org.junit.Assert.assertNotNull; | ||
41 | +import static org.junit.Assert.assertTrue; | ||
42 | +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; | ||
43 | + | ||
44 | +@Slf4j | ||
45 | +public abstract class AbstractMqttAttributesUpdatesIntegrationTest extends AbstractMqttAttributesIntegrationTest { | ||
46 | + | ||
47 | + private static final String RESPONSE_ATTRIBUTES_PAYLOAD_DELETED = "{\"deleted\":[\"attribute5\"]}"; | ||
48 | + | ||
49 | + private static String getResponseGatewayAttributesUpdatedPayload() { | ||
50 | + return "{\"device\":\"" + "Gateway Device Subscribe to attribute updates" + "\"," + | ||
51 | + "\"data\":{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73,\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}}}"; | ||
52 | + } | ||
53 | + | ||
54 | + private static String getResponseGatewayAttributesDeletedPayload() { | ||
55 | + return "{\"device\":\"" + "Gateway Device Subscribe to attribute updates" + "\",\"data\":{\"deleted\":[\"attribute5\"]}}"; | ||
56 | + } | ||
57 | + | ||
58 | + @Before | ||
59 | + public void beforeTest() throws Exception { | ||
60 | + processBeforeTest("Test Subscribe to attribute updates", "Gateway Test Subscribe to attribute updates", TransportPayloadType.JSON, null, null); | ||
61 | + } | ||
62 | + | ||
63 | + @After | ||
64 | + public void afterTest() throws Exception { | ||
65 | + processAfterTest(); | ||
66 | + } | ||
67 | + | ||
68 | + @Test | ||
69 | + public void testSubscribeToAttributesUpdatesFromTheServer() throws Exception { | ||
70 | + processTestSubscribeToAttributesUpdates(); | ||
71 | + } | ||
72 | + | ||
73 | + @Test | ||
74 | + public void testSubscribeToAttributesUpdatesFromTheServerGateway() throws Exception { | ||
75 | + processGatewayTestSubscribeToAttributesUpdates(); | ||
76 | + } | ||
77 | + | ||
78 | + protected void processTestSubscribeToAttributesUpdates() throws Exception { | ||
79 | + | ||
80 | + MqttAsyncClient client = getMqttAsyncClient(accessToken); | ||
81 | + | ||
82 | + TestMqttCallback onUpdateCallback = getTestMqttCallback(); | ||
83 | + client.setCallback(onUpdateCallback); | ||
84 | + | ||
85 | + client.subscribe(MqttTopics.DEVICE_ATTRIBUTES_TOPIC, MqttQoS.AT_MOST_ONCE.value()); | ||
86 | + | ||
87 | + Thread.sleep(1000); | ||
88 | + | ||
89 | + doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk()); | ||
90 | + onUpdateCallback.getLatch().await(3, TimeUnit.SECONDS); | ||
91 | + | ||
92 | + validateUpdateAttributesResponse(onUpdateCallback); | ||
93 | + | ||
94 | + TestMqttCallback onDeleteCallback = getTestMqttCallback(); | ||
95 | + client.setCallback(onDeleteCallback); | ||
96 | + | ||
97 | + doDelete("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/SHARED_SCOPE?keys=attribute5", String.class); | ||
98 | + onDeleteCallback.getLatch().await(3, TimeUnit.SECONDS); | ||
99 | + | ||
100 | + validateDeleteAttributesResponse(onDeleteCallback); | ||
101 | + } | ||
102 | + | ||
103 | + protected void validateUpdateAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException { | ||
104 | + assertNotNull(callback.getPayloadBytes()); | ||
105 | + String response = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8); | ||
106 | + assertEquals(JacksonUtil.toJsonNode(POST_ATTRIBUTES_PAYLOAD), JacksonUtil.toJsonNode(response)); | ||
107 | + } | ||
108 | + | ||
109 | + protected void validateDeleteAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException { | ||
110 | + assertNotNull(callback.getPayloadBytes()); | ||
111 | + String response = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8); | ||
112 | + assertEquals(JacksonUtil.toJsonNode(RESPONSE_ATTRIBUTES_PAYLOAD_DELETED), JacksonUtil.toJsonNode(response)); | ||
113 | + } | ||
114 | + | ||
115 | + protected void processGatewayTestSubscribeToAttributesUpdates() throws Exception { | ||
116 | + | ||
117 | + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); | ||
118 | + | ||
119 | + TestMqttCallback onUpdateCallback = getTestMqttCallback(); | ||
120 | + client.setCallback(onUpdateCallback); | ||
121 | + | ||
122 | + Device device = new Device(); | ||
123 | + device.setName("Gateway Device Subscribe to attribute updates"); | ||
124 | + device.setType("default"); | ||
125 | + | ||
126 | + byte[] connectPayloadBytes = getConnectPayloadBytes(); | ||
127 | + | ||
128 | + publishMqttMsg(client, connectPayloadBytes, MqttTopics.GATEWAY_CONNECT_TOPIC); | ||
129 | + | ||
130 | + Device savedDevice = doExecuteWithRetriesAndInterval(() -> doGet("/api/tenant/devices?deviceName=" + "Gateway Device Subscribe to attribute updates", Device.class), | ||
131 | + 20, | ||
132 | + 100); | ||
133 | + | ||
134 | + assertNotNull(savedDevice); | ||
135 | + | ||
136 | + client.subscribe(MqttTopics.GATEWAY_ATTRIBUTES_TOPIC, MqttQoS.AT_MOST_ONCE.value()); | ||
137 | + | ||
138 | + Thread.sleep(1000); | ||
139 | + | ||
140 | + doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk()); | ||
141 | + onUpdateCallback.getLatch().await(3, TimeUnit.SECONDS); | ||
142 | + | ||
143 | + validateGatewayUpdateAttributesResponse(onUpdateCallback); | ||
144 | + | ||
145 | + TestMqttCallback onDeleteCallback = getTestMqttCallback(); | ||
146 | + client.setCallback(onDeleteCallback); | ||
147 | + | ||
148 | + doDelete("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/SHARED_SCOPE?keys=attribute5", String.class); | ||
149 | + onDeleteCallback.getLatch().await(3, TimeUnit.SECONDS); | ||
150 | + | ||
151 | + validateGatewayDeleteAttributesResponse(onDeleteCallback); | ||
152 | + | ||
153 | + } | ||
154 | + | ||
155 | + protected void validateGatewayUpdateAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException { | ||
156 | + assertNotNull(callback.getPayloadBytes()); | ||
157 | + String s = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8); | ||
158 | + assertEquals(getResponseGatewayAttributesUpdatedPayload(), s); | ||
159 | + } | ||
160 | + | ||
161 | + protected void validateGatewayDeleteAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException { | ||
162 | + assertNotNull(callback.getPayloadBytes()); | ||
163 | + String s = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8); | ||
164 | + assertEquals(s, getResponseGatewayAttributesDeletedPayload()); | ||
165 | + } | ||
166 | + | ||
167 | + protected byte[] getConnectPayloadBytes() { | ||
168 | + String connectPayload = "{\"device\": \"Gateway Device Subscribe to attribute updates\", \"type\": \"" + TransportPayloadType.JSON.name() + "\"}"; | ||
169 | + return connectPayload.getBytes(); | ||
170 | + } | ||
171 | +} |
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.mqtt.attributes.updates; | ||
17 | + | ||
18 | +import lombok.extern.slf4j.Slf4j; | ||
19 | +import org.junit.After; | ||
20 | +import org.junit.Before; | ||
21 | +import org.junit.Test; | ||
22 | +import org.thingsboard.server.common.data.TransportPayloadType; | ||
23 | + | ||
24 | +import static org.junit.Assert.assertEquals; | ||
25 | +import static org.junit.Assert.assertFalse; | ||
26 | +import static org.junit.Assert.assertNotNull; | ||
27 | +import static org.junit.Assert.assertTrue; | ||
28 | + | ||
29 | +@Slf4j | ||
30 | +public abstract class AbstractMqttAttributesUpdatesJsonIntegrationTest extends AbstractMqttAttributesUpdatesIntegrationTest { | ||
31 | + | ||
32 | + @Before | ||
33 | + public void beforeTest() throws Exception { | ||
34 | + processBeforeTest("Test Subscribe to attribute updates", "Gateway Test Subscribe to attribute updates", TransportPayloadType.JSON, null, null); | ||
35 | + } | ||
36 | + | ||
37 | + @After | ||
38 | + public void afterTest() throws Exception { | ||
39 | + processAfterTest(); | ||
40 | + } | ||
41 | + | ||
42 | + @Test | ||
43 | + public void testSubscribeToAttributesUpdatesFromTheServer() throws Exception { | ||
44 | + processTestSubscribeToAttributesUpdates(); | ||
45 | + } | ||
46 | + | ||
47 | + @Test | ||
48 | + public void testSubscribeToAttributesUpdatesFromTheServerGateway() throws Exception { | ||
49 | + processGatewayTestSubscribeToAttributesUpdates(); | ||
50 | + } | ||
51 | +} |
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.mqtt.attributes.updates; | ||
17 | + | ||
18 | +import com.google.protobuf.InvalidProtocolBufferException; | ||
19 | +import lombok.extern.slf4j.Slf4j; | ||
20 | +import org.junit.After; | ||
21 | +import org.junit.Before; | ||
22 | +import org.junit.Test; | ||
23 | +import org.thingsboard.server.common.data.TransportPayloadType; | ||
24 | +import org.thingsboard.server.common.data.device.profile.MqttTopics; | ||
25 | +import org.thingsboard.server.gen.transport.TransportApiProtos; | ||
26 | +import org.thingsboard.server.gen.transport.TransportProtos; | ||
27 | + | ||
28 | +import java.nio.charset.StandardCharsets; | ||
29 | +import java.util.List; | ||
30 | +import java.util.stream.Collectors; | ||
31 | + | ||
32 | +import static org.junit.Assert.assertEquals; | ||
33 | +import static org.junit.Assert.assertNotNull; | ||
34 | +import static org.junit.Assert.assertTrue; | ||
35 | + | ||
36 | +@Slf4j | ||
37 | +public abstract class AbstractMqttAttributesUpdatesProtoIntegrationTest extends AbstractMqttAttributesUpdatesIntegrationTest { | ||
38 | + | ||
39 | + @Before | ||
40 | + public void beforeTest() throws Exception { | ||
41 | + processBeforeTest("Test Subscribe to attribute updates", "Gateway Test Subscribe to attribute updates", TransportPayloadType.PROTOBUF, null, null); | ||
42 | + } | ||
43 | + | ||
44 | + @After | ||
45 | + public void afterTest() throws Exception { | ||
46 | + processAfterTest(); | ||
47 | + } | ||
48 | + | ||
49 | + @Test | ||
50 | + public void testSubscribeToAttributesUpdatesFromTheServer() throws Exception { | ||
51 | + processTestSubscribeToAttributesUpdates(); | ||
52 | + } | ||
53 | + | ||
54 | + @Test | ||
55 | + public void testSubscribeToAttributesUpdatesFromTheServerGateway() throws Exception { | ||
56 | + processGatewayTestSubscribeToAttributesUpdates(); | ||
57 | + } | ||
58 | + | ||
59 | + protected void validateUpdateAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException { | ||
60 | + assertNotNull(callback.getPayloadBytes()); | ||
61 | + TransportProtos.AttributeUpdateNotificationMsg.Builder attributeUpdateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder(); | ||
62 | + List<TransportProtos.TsKvProto> tsKvProtoList = getTsKvProtoList(); | ||
63 | + attributeUpdateNotificationMsgBuilder.addAllSharedUpdated(tsKvProtoList); | ||
64 | + | ||
65 | + TransportProtos.AttributeUpdateNotificationMsg expectedAttributeUpdateNotificationMsg = attributeUpdateNotificationMsgBuilder.build(); | ||
66 | + TransportProtos.AttributeUpdateNotificationMsg actualAttributeUpdateNotificationMsg = TransportProtos.AttributeUpdateNotificationMsg.parseFrom(callback.getPayloadBytes()); | ||
67 | + | ||
68 | + List<TransportProtos.KeyValueProto> actualSharedUpdatedList = actualAttributeUpdateNotificationMsg.getSharedUpdatedList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); | ||
69 | + List<TransportProtos.KeyValueProto> expectedSharedUpdatedList = expectedAttributeUpdateNotificationMsg.getSharedUpdatedList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); | ||
70 | + | ||
71 | + assertEquals(expectedSharedUpdatedList.size(), actualSharedUpdatedList.size()); | ||
72 | + assertTrue(actualSharedUpdatedList.containsAll(expectedSharedUpdatedList)); | ||
73 | + | ||
74 | + } | ||
75 | + | ||
76 | + protected void validateDeleteAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException { | ||
77 | + assertNotNull(callback.getPayloadBytes()); | ||
78 | + TransportProtos.AttributeUpdateNotificationMsg.Builder attributeUpdateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder(); | ||
79 | + attributeUpdateNotificationMsgBuilder.addSharedDeleted("attribute5"); | ||
80 | + | ||
81 | + TransportProtos.AttributeUpdateNotificationMsg expectedAttributeUpdateNotificationMsg = attributeUpdateNotificationMsgBuilder.build(); | ||
82 | + TransportProtos.AttributeUpdateNotificationMsg actualAttributeUpdateNotificationMsg = TransportProtos.AttributeUpdateNotificationMsg.parseFrom(callback.getPayloadBytes()); | ||
83 | + | ||
84 | + assertEquals(expectedAttributeUpdateNotificationMsg.getSharedDeletedList().size(), actualAttributeUpdateNotificationMsg.getSharedDeletedList().size()); | ||
85 | + assertEquals("attribute5", actualAttributeUpdateNotificationMsg.getSharedDeletedList().get(0)); | ||
86 | + | ||
87 | + } | ||
88 | + | ||
89 | + protected void validateGatewayUpdateAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException { | ||
90 | + assertNotNull(callback.getPayloadBytes()); | ||
91 | + | ||
92 | + TransportProtos.AttributeUpdateNotificationMsg.Builder attributeUpdateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder(); | ||
93 | + List<TransportProtos.TsKvProto> tsKvProtoList = getTsKvProtoList(); | ||
94 | + attributeUpdateNotificationMsgBuilder.addAllSharedUpdated(tsKvProtoList); | ||
95 | + TransportProtos.AttributeUpdateNotificationMsg expectedAttributeUpdateNotificationMsg = attributeUpdateNotificationMsgBuilder.build(); | ||
96 | + | ||
97 | + TransportApiProtos.GatewayAttributeUpdateNotificationMsg.Builder gatewayAttributeUpdateNotificationMsgBuilder = TransportApiProtos.GatewayAttributeUpdateNotificationMsg.newBuilder(); | ||
98 | + gatewayAttributeUpdateNotificationMsgBuilder.setDeviceName("Gateway Device Subscribe to attribute updates"); | ||
99 | + gatewayAttributeUpdateNotificationMsgBuilder.setNotificationMsg(expectedAttributeUpdateNotificationMsg); | ||
100 | + | ||
101 | + TransportApiProtos.GatewayAttributeUpdateNotificationMsg expectedGatewayAttributeUpdateNotificationMsg = gatewayAttributeUpdateNotificationMsgBuilder.build(); | ||
102 | + TransportApiProtos.GatewayAttributeUpdateNotificationMsg actualGatewayAttributeUpdateNotificationMsg = TransportApiProtos.GatewayAttributeUpdateNotificationMsg.parseFrom(callback.getPayloadBytes()); | ||
103 | + | ||
104 | + assertEquals(expectedGatewayAttributeUpdateNotificationMsg.getDeviceName(), actualGatewayAttributeUpdateNotificationMsg.getDeviceName()); | ||
105 | + | ||
106 | + List<TransportProtos.KeyValueProto> actualSharedUpdatedList = actualGatewayAttributeUpdateNotificationMsg.getNotificationMsg().getSharedUpdatedList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); | ||
107 | + List<TransportProtos.KeyValueProto> expectedSharedUpdatedList = expectedGatewayAttributeUpdateNotificationMsg.getNotificationMsg().getSharedUpdatedList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); | ||
108 | + | ||
109 | + assertEquals(expectedSharedUpdatedList.size(), actualSharedUpdatedList.size()); | ||
110 | + assertTrue(actualSharedUpdatedList.containsAll(expectedSharedUpdatedList)); | ||
111 | + | ||
112 | + } | ||
113 | + | ||
114 | + protected void validateGatewayDeleteAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException { | ||
115 | + assertNotNull(callback.getPayloadBytes()); | ||
116 | + TransportProtos.AttributeUpdateNotificationMsg.Builder attributeUpdateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder(); | ||
117 | + attributeUpdateNotificationMsgBuilder.addSharedDeleted("attribute5"); | ||
118 | + TransportProtos.AttributeUpdateNotificationMsg attributeUpdateNotificationMsg = attributeUpdateNotificationMsgBuilder.build(); | ||
119 | + | ||
120 | + TransportApiProtos.GatewayAttributeUpdateNotificationMsg.Builder gatewayAttributeUpdateNotificationMsgBuilder = TransportApiProtos.GatewayAttributeUpdateNotificationMsg.newBuilder(); | ||
121 | + gatewayAttributeUpdateNotificationMsgBuilder.setDeviceName("Gateway Device Subscribe to attribute updates"); | ||
122 | + gatewayAttributeUpdateNotificationMsgBuilder.setNotificationMsg(attributeUpdateNotificationMsg); | ||
123 | + | ||
124 | + TransportApiProtos.GatewayAttributeUpdateNotificationMsg expectedGatewayAttributeUpdateNotificationMsg = gatewayAttributeUpdateNotificationMsgBuilder.build(); | ||
125 | + TransportApiProtos.GatewayAttributeUpdateNotificationMsg actualGatewayAttributeUpdateNotificationMsg = TransportApiProtos.GatewayAttributeUpdateNotificationMsg.parseFrom(callback.getPayloadBytes()); | ||
126 | + | ||
127 | + assertEquals(expectedGatewayAttributeUpdateNotificationMsg.getDeviceName(), actualGatewayAttributeUpdateNotificationMsg.getDeviceName()); | ||
128 | + | ||
129 | + TransportProtos.AttributeUpdateNotificationMsg expectedAttributeUpdateNotificationMsg = expectedGatewayAttributeUpdateNotificationMsg.getNotificationMsg(); | ||
130 | + TransportProtos.AttributeUpdateNotificationMsg actualAttributeUpdateNotificationMsg = actualGatewayAttributeUpdateNotificationMsg.getNotificationMsg(); | ||
131 | + | ||
132 | + assertEquals(expectedAttributeUpdateNotificationMsg.getSharedDeletedList().size(), actualAttributeUpdateNotificationMsg.getSharedDeletedList().size()); | ||
133 | + assertEquals("attribute5", actualAttributeUpdateNotificationMsg.getSharedDeletedList().get(0)); | ||
134 | + | ||
135 | + } | ||
136 | + | ||
137 | + protected byte[] getConnectPayloadBytes() { | ||
138 | + TransportApiProtos.ConnectMsg connectProto = getConnectProto(); | ||
139 | + return connectProto.toByteArray(); | ||
140 | + } | ||
141 | + | ||
142 | + private TransportApiProtos.ConnectMsg getConnectProto() { | ||
143 | + TransportApiProtos.ConnectMsg.Builder builder = TransportApiProtos.ConnectMsg.newBuilder(); | ||
144 | + builder.setDeviceName("Gateway Device Subscribe to attribute updates"); | ||
145 | + builder.setDeviceType(TransportPayloadType.PROTOBUF.name()); | ||
146 | + return builder.build(); | ||
147 | + } | ||
148 | + | ||
149 | +} |
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.mqtt.attributes.updates.nosql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoNoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.attributes.updates.AbstractMqttAttributesUpdatesJsonIntegrationTest; | ||
20 | + | ||
21 | + | ||
22 | +@DaoNoSqlTest | ||
23 | +public class MqttAttributesUpdatesNoSqlIntegrationTest extends AbstractMqttAttributesUpdatesJsonIntegrationTest { | ||
24 | +} |
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.mqtt.attributes.updates.sql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.attributes.updates.AbstractMqttAttributesUpdatesIntegrationTest; | ||
20 | + | ||
21 | +@DaoSqlTest | ||
22 | +public class MqttAttributesUpdatesSqlIntegrationTest extends AbstractMqttAttributesUpdatesIntegrationTest { | ||
23 | +} |
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.mqtt.attributes.updates.sql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.attributes.updates.AbstractMqttAttributesUpdatesIntegrationTest; | ||
20 | +import org.thingsboard.server.mqtt.attributes.updates.AbstractMqttAttributesUpdatesJsonIntegrationTest; | ||
21 | + | ||
22 | +@DaoSqlTest | ||
23 | +public class MqttAttributesUpdatesSqlJsonIntegrationTest extends AbstractMqttAttributesUpdatesJsonIntegrationTest { | ||
24 | +} |
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.mqtt.attributes.updates.sql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.attributes.updates.AbstractMqttAttributesUpdatesJsonIntegrationTest; | ||
20 | +import org.thingsboard.server.mqtt.attributes.updates.AbstractMqttAttributesUpdatesProtoIntegrationTest; | ||
21 | + | ||
22 | +@DaoSqlTest | ||
23 | +public class MqttAttributesUpdatesSqlProtoIntegrationTest extends AbstractMqttAttributesUpdatesProtoIntegrationTest { | ||
24 | +} |
application/src/test/java/org/thingsboard/server/mqtt/claim/AbstractMqttClaimDeviceTest.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.mqtt.claim; | ||
17 | + | ||
18 | +import lombok.extern.slf4j.Slf4j; | ||
19 | +import org.eclipse.paho.client.mqttv3.MqttAsyncClient; | ||
20 | +import org.eclipse.paho.client.mqttv3.MqttMessage; | ||
21 | +import org.junit.After; | ||
22 | +import org.junit.Before; | ||
23 | +import org.junit.Ignore; | ||
24 | +import org.junit.Test; | ||
25 | +import org.thingsboard.server.common.data.ClaimRequest; | ||
26 | +import org.thingsboard.server.common.data.Customer; | ||
27 | +import org.thingsboard.server.common.data.Device; | ||
28 | +import org.thingsboard.server.common.data.User; | ||
29 | +import org.thingsboard.server.common.data.device.profile.MqttTopics; | ||
30 | +import org.thingsboard.server.common.data.security.Authority; | ||
31 | +import org.thingsboard.server.dao.device.claim.ClaimResponse; | ||
32 | +import org.thingsboard.server.dao.device.claim.ClaimResult; | ||
33 | +import org.thingsboard.server.mqtt.AbstractMqttIntegrationTest; | ||
34 | + | ||
35 | +import static org.junit.Assert.assertEquals; | ||
36 | +import static org.junit.Assert.assertNotNull; | ||
37 | +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; | ||
38 | + | ||
39 | +@Slf4j | ||
40 | +public abstract class AbstractMqttClaimDeviceTest extends AbstractMqttIntegrationTest { | ||
41 | + | ||
42 | + protected static final String CUSTOMER_USER_PASSWORD = "customerUser123!"; | ||
43 | + | ||
44 | + protected User customerAdmin; | ||
45 | + protected Customer savedCustomer; | ||
46 | + | ||
47 | + @Before | ||
48 | + public void beforeTest() throws Exception { | ||
49 | + super.processBeforeTest("Test Claim device", "Test Claim gateway", null, null, null); | ||
50 | + createCustomerAndUser(); | ||
51 | + } | ||
52 | + | ||
53 | + protected void createCustomerAndUser() throws Exception { | ||
54 | + Customer customer = new Customer(); | ||
55 | + customer.setTenantId(savedTenant.getId()); | ||
56 | + customer.setTitle("Test Claiming Customer"); | ||
57 | + savedCustomer = doPost("/api/customer", customer, Customer.class); | ||
58 | + assertNotNull(savedCustomer); | ||
59 | + assertEquals(savedTenant.getId(), savedCustomer.getTenantId()); | ||
60 | + | ||
61 | + User user = new User(); | ||
62 | + user.setAuthority(Authority.CUSTOMER_USER); | ||
63 | + user.setTenantId(savedTenant.getId()); | ||
64 | + user.setCustomerId(savedCustomer.getId()); | ||
65 | + user.setEmail("customer@thingsboard.org"); | ||
66 | + | ||
67 | + customerAdmin = createUser(user, CUSTOMER_USER_PASSWORD); | ||
68 | + assertNotNull(customerAdmin); | ||
69 | + assertEquals(customerAdmin.getCustomerId(), savedCustomer.getId()); | ||
70 | + } | ||
71 | + | ||
72 | + @After | ||
73 | + public void afterTest() throws Exception { | ||
74 | + super.processAfterTest(); | ||
75 | + } | ||
76 | + | ||
77 | + @Test | ||
78 | + public void testClaimingDevice() throws Exception { | ||
79 | + processTestClaimingDevice(false); | ||
80 | + } | ||
81 | + | ||
82 | + @Test | ||
83 | + public void testClaimingDeviceWithoutSecretAndDuration() throws Exception { | ||
84 | + processTestClaimingDevice(true); | ||
85 | + } | ||
86 | + | ||
87 | + @Test | ||
88 | + public void testGatewayClaimingDevice() throws Exception { | ||
89 | + processTestGatewayClaimingDevice("Test claiming gateway device", false); | ||
90 | + } | ||
91 | + | ||
92 | + @Test | ||
93 | + public void testGatewayClaimingDeviceWithoutSecretAndDuration() throws Exception { | ||
94 | + processTestGatewayClaimingDevice("Test claiming gateway device empty payload", true); | ||
95 | + } | ||
96 | + | ||
97 | + | ||
98 | + protected void processTestClaimingDevice(boolean emptyPayload) throws Exception { | ||
99 | + MqttAsyncClient client = getMqttAsyncClient(accessToken); | ||
100 | + byte[] payloadBytes; | ||
101 | + byte[] failurePayloadBytes; | ||
102 | + if (emptyPayload) { | ||
103 | + payloadBytes = "{}".getBytes(); | ||
104 | + failurePayloadBytes = "{\"durationMs\":1}".getBytes(); | ||
105 | + } else { | ||
106 | + payloadBytes = "{\"secretKey\":\"value\", \"durationMs\":60000}".getBytes(); | ||
107 | + failurePayloadBytes = "{\"secretKey\":\"value\", \"durationMs\":1}".getBytes(); | ||
108 | + } | ||
109 | + validateClaimResponse(emptyPayload, client, payloadBytes, failurePayloadBytes); | ||
110 | + } | ||
111 | + | ||
112 | + protected void validateClaimResponse(boolean emptyPayload, MqttAsyncClient client, byte[] payloadBytes, byte[] failurePayloadBytes) throws Exception { | ||
113 | + client.publish(MqttTopics.DEVICE_CLAIM_TOPIC, new MqttMessage(failurePayloadBytes)); | ||
114 | + | ||
115 | + loginUser(customerAdmin.getName(), CUSTOMER_USER_PASSWORD); | ||
116 | + ClaimRequest claimRequest; | ||
117 | + if (!emptyPayload) { | ||
118 | + claimRequest = new ClaimRequest("value"); | ||
119 | + } else { | ||
120 | + claimRequest = new ClaimRequest(null); | ||
121 | + } | ||
122 | + | ||
123 | + ClaimResponse claimResponse = doExecuteWithRetriesAndInterval( | ||
124 | + () -> doPostClaimAsync("/api/customer/device/" + savedDevice.getName() + "/claim", claimRequest, ClaimResponse.class, status().isBadRequest()), | ||
125 | + 20, | ||
126 | + 100 | ||
127 | + ); | ||
128 | + | ||
129 | + assertEquals(claimResponse, ClaimResponse.FAILURE); | ||
130 | + | ||
131 | + client.publish(MqttTopics.DEVICE_CLAIM_TOPIC, new MqttMessage(payloadBytes)); | ||
132 | + | ||
133 | + ClaimResult claimResult = doExecuteWithRetriesAndInterval( | ||
134 | + () -> doPostClaimAsync("/api/customer/device/" + savedDevice.getName() + "/claim", claimRequest, ClaimResult.class, status().isOk()), | ||
135 | + 20, | ||
136 | + 100 | ||
137 | + ); | ||
138 | + assertEquals(claimResult.getResponse(), ClaimResponse.SUCCESS); | ||
139 | + Device claimedDevice = claimResult.getDevice(); | ||
140 | + assertNotNull(claimedDevice); | ||
141 | + assertNotNull(claimedDevice.getCustomerId()); | ||
142 | + assertEquals(customerAdmin.getCustomerId(), claimedDevice.getCustomerId()); | ||
143 | + | ||
144 | + claimResponse = doPostClaimAsync("/api/customer/device/" + savedDevice.getName() + "/claim", claimRequest, ClaimResponse.class, status().isBadRequest()); | ||
145 | + assertEquals(claimResponse, ClaimResponse.CLAIMED); | ||
146 | + } | ||
147 | + | ||
148 | + protected void validateGatewayClaimResponse(String deviceName, boolean emptyPayload, MqttAsyncClient client, byte[] failurePayloadBytes, byte[] payloadBytes) throws Exception { | ||
149 | + client.publish(MqttTopics.GATEWAY_CLAIM_TOPIC, new MqttMessage(failurePayloadBytes)); | ||
150 | + | ||
151 | + Device savedDevice = doExecuteWithRetriesAndInterval( | ||
152 | + () -> doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class), | ||
153 | + 20, | ||
154 | + 100 | ||
155 | + ); | ||
156 | + | ||
157 | + assertNotNull(savedDevice); | ||
158 | + | ||
159 | + loginUser(customerAdmin.getName(), CUSTOMER_USER_PASSWORD); | ||
160 | + ClaimRequest claimRequest; | ||
161 | + if (!emptyPayload) { | ||
162 | + claimRequest = new ClaimRequest("value"); | ||
163 | + } else { | ||
164 | + claimRequest = new ClaimRequest(null); | ||
165 | + } | ||
166 | + | ||
167 | + ClaimResponse claimResponse = doPostClaimAsync("/api/customer/device/" + deviceName + "/claim", claimRequest, ClaimResponse.class, status().isBadRequest()); | ||
168 | + assertEquals(claimResponse, ClaimResponse.FAILURE); | ||
169 | + | ||
170 | + client.publish(MqttTopics.GATEWAY_CLAIM_TOPIC, new MqttMessage(payloadBytes)); | ||
171 | + | ||
172 | + ClaimResult claimResult = doExecuteWithRetriesAndInterval( | ||
173 | + () -> doPostClaimAsync("/api/customer/device/" + deviceName + "/claim", claimRequest, ClaimResult.class, status().isOk()), | ||
174 | + 20, | ||
175 | + 100 | ||
176 | + ); | ||
177 | + | ||
178 | + assertEquals(claimResult.getResponse(), ClaimResponse.SUCCESS); | ||
179 | + Device claimedDevice = claimResult.getDevice(); | ||
180 | + assertNotNull(claimedDevice); | ||
181 | + assertNotNull(claimedDevice.getCustomerId()); | ||
182 | + assertEquals(customerAdmin.getCustomerId(), claimedDevice.getCustomerId()); | ||
183 | + | ||
184 | + claimResponse = doPostClaimAsync("/api/customer/device/" + deviceName + "/claim", claimRequest, ClaimResponse.class, status().isBadRequest()); | ||
185 | + assertEquals(claimResponse, ClaimResponse.CLAIMED); | ||
186 | + } | ||
187 | + | ||
188 | + protected void processTestGatewayClaimingDevice(String deviceName, boolean emptyPayload) throws Exception { | ||
189 | + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); | ||
190 | + byte[] failurePayloadBytes; | ||
191 | + byte[] payloadBytes; | ||
192 | + String failurePayload; | ||
193 | + String payload; | ||
194 | + if (emptyPayload) { | ||
195 | + failurePayload = "{\"" + deviceName + "\": " + "{\"durationMs\":1}" + "}"; | ||
196 | + payload = "{\"" + deviceName + "\": " + "{}" + "}"; | ||
197 | + } else { | ||
198 | + failurePayload = "{\"" + deviceName + "\": " + "{\"secretKey\":\"value\", \"durationMs\":1}" + "}"; | ||
199 | + payload = "{\"" + deviceName + "\": " + "{\"secretKey\":\"value\", \"durationMs\":60000}" + "}"; | ||
200 | + } | ||
201 | + payloadBytes = payload.getBytes(); | ||
202 | + failurePayloadBytes = failurePayload.getBytes(); | ||
203 | + validateGatewayClaimResponse(deviceName, emptyPayload, client, failurePayloadBytes, payloadBytes); | ||
204 | + } | ||
205 | + | ||
206 | +} |
application/src/test/java/org/thingsboard/server/mqtt/claim/AbstractMqttClaimJsonDeviceTest.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.mqtt.claim; | ||
17 | + | ||
18 | +import lombok.extern.slf4j.Slf4j; | ||
19 | +import org.junit.After; | ||
20 | +import org.junit.Before; | ||
21 | +import org.junit.Ignore; | ||
22 | +import org.junit.Test; | ||
23 | +import org.thingsboard.server.common.data.TransportPayloadType; | ||
24 | + | ||
25 | +@Slf4j | ||
26 | +public abstract class AbstractMqttClaimJsonDeviceTest extends AbstractMqttClaimDeviceTest { | ||
27 | + | ||
28 | + @Before | ||
29 | + public void beforeTest() throws Exception { | ||
30 | + super.processBeforeTest("Test Claim device", "Test Claim gateway", TransportPayloadType.JSON, null, null); | ||
31 | + createCustomerAndUser(); | ||
32 | + } | ||
33 | + | ||
34 | + @After | ||
35 | + public void afterTest() throws Exception { | ||
36 | + super.afterTest(); | ||
37 | + } | ||
38 | + | ||
39 | + @Test | ||
40 | + public void testClaimingDevice() throws Exception { | ||
41 | + processTestClaimingDevice(false); | ||
42 | + } | ||
43 | + | ||
44 | + @Test | ||
45 | + public void testClaimingDeviceWithoutSecretAndDuration() throws Exception { | ||
46 | + processTestClaimingDevice(true); | ||
47 | + } | ||
48 | + | ||
49 | + @Test | ||
50 | + public void testGatewayClaimingDevice() throws Exception { | ||
51 | + processTestGatewayClaimingDevice("Test claiming gateway device Json", false); | ||
52 | + } | ||
53 | + | ||
54 | + @Test | ||
55 | + public void testGatewayClaimingDeviceWithoutSecretAndDuration() throws Exception { | ||
56 | + processTestGatewayClaimingDevice("Test claiming gateway device empty payload Json", true); | ||
57 | + } | ||
58 | +} |
application/src/test/java/org/thingsboard/server/mqtt/claim/AbstractMqttClaimProtoDeviceTest.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.mqtt.claim; | ||
17 | + | ||
18 | +import lombok.extern.slf4j.Slf4j; | ||
19 | +import org.eclipse.paho.client.mqttv3.MqttAsyncClient; | ||
20 | +import org.junit.After; | ||
21 | +import org.junit.Before; | ||
22 | +import org.junit.Ignore; | ||
23 | +import org.junit.Test; | ||
24 | +import org.thingsboard.server.common.data.TransportPayloadType; | ||
25 | +import org.thingsboard.server.gen.transport.TransportApiProtos; | ||
26 | + | ||
27 | +@Slf4j | ||
28 | +public abstract class AbstractMqttClaimProtoDeviceTest extends AbstractMqttClaimDeviceTest { | ||
29 | + | ||
30 | + @Before | ||
31 | + public void beforeTest() throws Exception { | ||
32 | + processBeforeTest("Test Claim device", "Test Claim gateway", TransportPayloadType.PROTOBUF, null, null); | ||
33 | + createCustomerAndUser(); | ||
34 | + } | ||
35 | + | ||
36 | + @After | ||
37 | + public void afterTest() throws Exception { super.afterTest(); } | ||
38 | + | ||
39 | + @Test | ||
40 | + public void testClaimingDevice() throws Exception { | ||
41 | + processTestClaimingDevice(false); | ||
42 | + } | ||
43 | + | ||
44 | + @Test | ||
45 | + public void testClaimingDeviceWithoutSecretAndDuration() throws Exception { | ||
46 | + processTestClaimingDevice(true); | ||
47 | + } | ||
48 | + | ||
49 | + @Test | ||
50 | + public void testGatewayClaimingDevice() throws Exception { | ||
51 | + processTestGatewayClaimingDevice("Test claiming gateway device Proto", false); | ||
52 | + } | ||
53 | + | ||
54 | + @Test | ||
55 | + public void testGatewayClaimingDeviceWithoutSecretAndDuration() throws Exception { | ||
56 | + processTestGatewayClaimingDevice("Test claiming gateway device empty payload Proto", true); | ||
57 | + } | ||
58 | + | ||
59 | + protected void processTestClaimingDevice(boolean emptyPayload) throws Exception { | ||
60 | + MqttAsyncClient client = getMqttAsyncClient(accessToken); | ||
61 | + byte[] payloadBytes; | ||
62 | + if (emptyPayload) { | ||
63 | + payloadBytes = getClaimDevice(0, emptyPayload).toByteArray(); | ||
64 | + } else { | ||
65 | + payloadBytes = getClaimDevice(60000, emptyPayload).toByteArray(); | ||
66 | + } | ||
67 | + byte[] failurePayloadBytes = getClaimDevice(1, emptyPayload).toByteArray(); | ||
68 | + validateClaimResponse(emptyPayload, client, payloadBytes, failurePayloadBytes); | ||
69 | + } | ||
70 | + | ||
71 | + protected void processTestGatewayClaimingDevice(String deviceName, boolean emptyPayload) throws Exception { | ||
72 | + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); | ||
73 | + byte[] failurePayloadBytes; | ||
74 | + byte[] payloadBytes; | ||
75 | + if (emptyPayload) { | ||
76 | + payloadBytes = getGatewayClaimMsg(deviceName, 0, emptyPayload).toByteArray(); | ||
77 | + } else { | ||
78 | + payloadBytes = getGatewayClaimMsg(deviceName, 60000, emptyPayload).toByteArray(); | ||
79 | + } | ||
80 | + failurePayloadBytes = getGatewayClaimMsg(deviceName, 1, emptyPayload).toByteArray(); | ||
81 | + | ||
82 | + validateGatewayClaimResponse(deviceName, emptyPayload, client, failurePayloadBytes, payloadBytes); | ||
83 | + } | ||
84 | + | ||
85 | + private TransportApiProtos.GatewayClaimMsg getGatewayClaimMsg(String deviceName, long duration, boolean emptyPayload) { | ||
86 | + TransportApiProtos.GatewayClaimMsg.Builder gatewayClaimMsgBuilder = TransportApiProtos.GatewayClaimMsg.newBuilder(); | ||
87 | + TransportApiProtos.ClaimDeviceMsg.Builder claimDeviceMsgBuilder = TransportApiProtos.ClaimDeviceMsg.newBuilder(); | ||
88 | + TransportApiProtos.ClaimDevice.Builder claimDeviceBuilder = TransportApiProtos.ClaimDevice.newBuilder(); | ||
89 | + if (!emptyPayload) { | ||
90 | + claimDeviceBuilder.setSecretKey("value"); | ||
91 | + } | ||
92 | + if (duration > 0) { | ||
93 | + claimDeviceBuilder.setDurationMs(duration); | ||
94 | + } | ||
95 | + TransportApiProtos.ClaimDevice claimDevice = claimDeviceBuilder.build(); | ||
96 | + claimDeviceMsgBuilder.setClaimRequest(claimDevice); | ||
97 | + claimDeviceMsgBuilder.setDeviceName(deviceName); | ||
98 | + TransportApiProtos.ClaimDeviceMsg claimDeviceMsg = claimDeviceMsgBuilder.build(); | ||
99 | + gatewayClaimMsgBuilder.addMsg(claimDeviceMsg); | ||
100 | + return gatewayClaimMsgBuilder.build(); | ||
101 | + } | ||
102 | + | ||
103 | + private TransportApiProtos.ClaimDevice getClaimDevice(long duration, boolean emptyPayload) { | ||
104 | + TransportApiProtos.ClaimDevice.Builder claimDeviceBuilder = TransportApiProtos.ClaimDevice.newBuilder(); | ||
105 | + if (!emptyPayload) { | ||
106 | + claimDeviceBuilder.setSecretKey("value"); | ||
107 | + } | ||
108 | + if (duration > 0) { | ||
109 | + claimDeviceBuilder.setSecretKey("value"); | ||
110 | + claimDeviceBuilder.setDurationMs(duration); | ||
111 | + } | ||
112 | + return claimDeviceBuilder.build(); | ||
113 | + } | ||
114 | + | ||
115 | + | ||
116 | +} |
application/src/test/java/org/thingsboard/server/mqtt/claim/nosql/MqttClaimDeviceNoSqlTest.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.mqtt.claim.nosql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoNoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.claim.AbstractMqttClaimDeviceTest; | ||
20 | + | ||
21 | + | ||
22 | +@DaoNoSqlTest | ||
23 | +public class MqttClaimDeviceNoSqlTest extends AbstractMqttClaimDeviceTest { | ||
24 | +} |
application/src/test/java/org/thingsboard/server/mqtt/claim/sql/MqttClaimDeviceJsonSqlTest.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.mqtt.claim.sql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.claim.AbstractMqttClaimDeviceTest; | ||
20 | +import org.thingsboard.server.mqtt.claim.AbstractMqttClaimJsonDeviceTest; | ||
21 | + | ||
22 | +@DaoSqlTest | ||
23 | +public class MqttClaimDeviceJsonSqlTest extends AbstractMqttClaimJsonDeviceTest { | ||
24 | +} |
application/src/test/java/org/thingsboard/server/mqtt/claim/sql/MqttClaimDeviceProtoSqlTest.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.mqtt.claim.sql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.claim.AbstractMqttClaimJsonDeviceTest; | ||
20 | +import org.thingsboard.server.mqtt.claim.AbstractMqttClaimProtoDeviceTest; | ||
21 | + | ||
22 | +@DaoSqlTest | ||
23 | +public class MqttClaimDeviceProtoSqlTest extends AbstractMqttClaimProtoDeviceTest { | ||
24 | +} |
application/src/test/java/org/thingsboard/server/mqtt/claim/sql/MqttClaimDeviceSqlTest.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.mqtt.claim.sql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.claim.AbstractMqttClaimDeviceTest; | ||
20 | + | ||
21 | +@DaoSqlTest | ||
22 | +public class MqttClaimDeviceSqlTest extends AbstractMqttClaimDeviceTest { | ||
23 | +} |
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.mqtt.rpc; | ||
17 | + | ||
18 | +import com.fasterxml.jackson.databind.JsonNode; | ||
19 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
20 | +import com.google.protobuf.InvalidProtocolBufferException; | ||
21 | +import com.nimbusds.jose.util.StandardCharset; | ||
22 | +import com.datastax.oss.driver.api.core.uuid.Uuids; | ||
23 | +import io.netty.handler.codec.mqtt.MqttQoS; | ||
24 | +import lombok.extern.slf4j.Slf4j; | ||
25 | +import org.apache.commons.lang3.StringUtils; | ||
26 | +import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; | ||
27 | +import org.eclipse.paho.client.mqttv3.MqttAsyncClient; | ||
28 | +import org.eclipse.paho.client.mqttv3.MqttCallback; | ||
29 | +import org.eclipse.paho.client.mqttv3.MqttConnectOptions; | ||
30 | +import org.eclipse.paho.client.mqttv3.MqttException; | ||
31 | +import org.eclipse.paho.client.mqttv3.MqttMessage; | ||
32 | +import org.junit.After; | ||
33 | +import org.junit.Assert; | ||
34 | +import org.junit.Before; | ||
35 | +import org.junit.Ignore; | ||
36 | +import org.junit.Test; | ||
37 | +import org.thingsboard.server.common.data.Device; | ||
38 | +import org.thingsboard.server.common.data.DeviceProfile; | ||
39 | +import org.thingsboard.server.common.data.DeviceProfileType; | ||
40 | +import org.thingsboard.server.common.data.DeviceTransportType; | ||
41 | +import org.thingsboard.server.common.data.Tenant; | ||
42 | +import org.thingsboard.server.common.data.TransportPayloadType; | ||
43 | +import org.thingsboard.server.common.data.User; | ||
44 | +import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; | ||
45 | +import org.thingsboard.server.common.data.device.profile.DeviceProfileData; | ||
46 | +import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration; | ||
47 | +import org.thingsboard.server.common.data.device.profile.MqttTopics; | ||
48 | +import org.thingsboard.server.common.data.security.Authority; | ||
49 | +import org.thingsboard.server.common.data.security.DeviceCredentials; | ||
50 | +import org.thingsboard.server.controller.AbstractControllerTest; | ||
51 | +import org.thingsboard.server.dao.util.mapping.JacksonUtil; | ||
52 | +import org.thingsboard.server.service.security.AccessValidator; | ||
53 | + | ||
54 | +import java.util.Arrays; | ||
55 | +import java.util.concurrent.CountDownLatch; | ||
56 | +import java.util.concurrent.TimeUnit; | ||
57 | +import java.util.concurrent.atomic.AtomicInteger; | ||
58 | + | ||
59 | +import static org.junit.Assert.assertEquals; | ||
60 | +import static org.junit.Assert.assertNotNull; | ||
61 | +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; | ||
62 | + | ||
63 | +/** | ||
64 | + * @author Valerii Sosliuk | ||
65 | + */ | ||
66 | +@Slf4j | ||
67 | +public abstract class AbstractMqttServerSideRpcDefaultIntegrationTest extends AbstractMqttServerSideRpcIntegrationTest { | ||
68 | + | ||
69 | + @Before | ||
70 | + public void beforeTest() throws Exception { | ||
71 | + processBeforeTest("RPC test device", "RPC test gateway", null, null, null); | ||
72 | + } | ||
73 | + | ||
74 | + @After | ||
75 | + public void afterTest() throws Exception { | ||
76 | + super.processAfterTest(); | ||
77 | + } | ||
78 | + | ||
79 | + @Test | ||
80 | + public void testServerMqttOneWayRpcDeviceOffline() throws Exception { | ||
81 | + String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"24\",\"value\": 1},\"timeout\": 6000}"; | ||
82 | + String deviceId = savedDevice.getId().getId().toString(); | ||
83 | + | ||
84 | + doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().is(409), | ||
85 | + asyncContextTimeoutToUseRpcPlugin); | ||
86 | + } | ||
87 | + | ||
88 | + @Test | ||
89 | + public void testServerMqttOneWayRpcDeviceDoesNotExist() throws Exception { | ||
90 | + String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"25\",\"value\": 1}}"; | ||
91 | + String nonExistentDeviceId = Uuids.timeBased().toString(); | ||
92 | + | ||
93 | + String result = doPostAsync("/api/plugins/rpc/oneway/" + nonExistentDeviceId, setGpioRequest, String.class, | ||
94 | + status().isNotFound()); | ||
95 | + Assert.assertEquals(AccessValidator.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result); | ||
96 | + } | ||
97 | + | ||
98 | + @Test | ||
99 | + public void testServerMqttTwoWayRpcDeviceOffline() throws Exception { | ||
100 | + String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"27\",\"value\": 1},\"timeout\": 6000}"; | ||
101 | + String deviceId = savedDevice.getId().getId().toString(); | ||
102 | + | ||
103 | + doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().is(409), | ||
104 | + asyncContextTimeoutToUseRpcPlugin); | ||
105 | + } | ||
106 | + | ||
107 | + @Test | ||
108 | + public void testServerMqttTwoWayRpcDeviceDoesNotExist() throws Exception { | ||
109 | + String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"28\",\"value\": 1}}"; | ||
110 | + String nonExistentDeviceId = Uuids.timeBased().toString(); | ||
111 | + | ||
112 | + String result = doPostAsync("/api/plugins/rpc/twoway/" + nonExistentDeviceId, setGpioRequest, String.class, | ||
113 | + status().isNotFound()); | ||
114 | + Assert.assertEquals(AccessValidator.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result); | ||
115 | + } | ||
116 | + | ||
117 | + @Test | ||
118 | + public void testServerMqttOneWayRpc() throws Exception { | ||
119 | + processOneWayRpcTest(); | ||
120 | + } | ||
121 | + | ||
122 | + @Test | ||
123 | + public void testServerMqttTwoWayRpc() throws Exception { | ||
124 | + processTwoWayRpcTest(); | ||
125 | + } | ||
126 | + | ||
127 | + @Test | ||
128 | + public void testGatewayServerMqttOneWayRpc() throws Exception { | ||
129 | + processOneWayRpcTestGateway("Gateway Device OneWay RPC"); | ||
130 | + } | ||
131 | + | ||
132 | + @Test | ||
133 | + public void testGatewayServerMqttTwoWayRpc() throws Exception { | ||
134 | + processTwoWayRpcTestGateway("Gateway Device TwoWay RPC"); | ||
135 | + } | ||
136 | + | ||
137 | +} |
@@ -16,6 +16,10 @@ | @@ -16,6 +16,10 @@ | ||
16 | package org.thingsboard.server.mqtt.rpc; | 16 | package org.thingsboard.server.mqtt.rpc; |
17 | 17 | ||
18 | import com.datastax.oss.driver.api.core.uuid.Uuids; | 18 | import com.datastax.oss.driver.api.core.uuid.Uuids; |
19 | +import com.fasterxml.jackson.databind.JsonNode; | ||
20 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
21 | +import com.google.protobuf.InvalidProtocolBufferException; | ||
22 | +import com.nimbusds.jose.util.StandardCharset; | ||
19 | import io.netty.handler.codec.mqtt.MqttQoS; | 23 | import io.netty.handler.codec.mqtt.MqttQoS; |
20 | import lombok.extern.slf4j.Slf4j; | 24 | import lombok.extern.slf4j.Slf4j; |
21 | import org.apache.commons.lang3.StringUtils; | 25 | import org.apache.commons.lang3.StringUtils; |
@@ -23,15 +27,28 @@ import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; | @@ -23,15 +27,28 @@ import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; | ||
23 | import org.eclipse.paho.client.mqttv3.MqttAsyncClient; | 27 | import org.eclipse.paho.client.mqttv3.MqttAsyncClient; |
24 | import org.eclipse.paho.client.mqttv3.MqttCallback; | 28 | import org.eclipse.paho.client.mqttv3.MqttCallback; |
25 | import org.eclipse.paho.client.mqttv3.MqttConnectOptions; | 29 | import org.eclipse.paho.client.mqttv3.MqttConnectOptions; |
30 | +import org.eclipse.paho.client.mqttv3.MqttException; | ||
26 | import org.eclipse.paho.client.mqttv3.MqttMessage; | 31 | import org.eclipse.paho.client.mqttv3.MqttMessage; |
27 | -import org.junit.*; | 32 | +import org.junit.After; |
33 | +import org.junit.Assert; | ||
34 | +import org.junit.Before; | ||
35 | +import org.junit.Test; | ||
28 | import org.thingsboard.server.common.data.Device; | 36 | import org.thingsboard.server.common.data.Device; |
37 | +import org.thingsboard.server.common.data.DeviceProfile; | ||
38 | +import org.thingsboard.server.common.data.DeviceProfileType; | ||
39 | +import org.thingsboard.server.common.data.DeviceTransportType; | ||
29 | import org.thingsboard.server.common.data.Tenant; | 40 | import org.thingsboard.server.common.data.Tenant; |
41 | +import org.thingsboard.server.common.data.TransportPayloadType; | ||
30 | import org.thingsboard.server.common.data.User; | 42 | import org.thingsboard.server.common.data.User; |
43 | +import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; | ||
44 | +import org.thingsboard.server.common.data.device.profile.DeviceProfileData; | ||
45 | +import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration; | ||
46 | +import org.thingsboard.server.common.data.device.profile.MqttTopics; | ||
31 | import org.thingsboard.server.common.data.security.Authority; | 47 | import org.thingsboard.server.common.data.security.Authority; |
32 | import org.thingsboard.server.common.data.security.DeviceCredentials; | 48 | import org.thingsboard.server.common.data.security.DeviceCredentials; |
33 | import org.thingsboard.server.controller.AbstractControllerTest; | 49 | import org.thingsboard.server.controller.AbstractControllerTest; |
34 | -import org.thingsboard.server.mqtt.telemetry.AbstractMqttTelemetryIntegrationTest; | 50 | +import org.thingsboard.server.dao.util.mapping.JacksonUtil; |
51 | +import org.thingsboard.server.mqtt.AbstractMqttIntegrationTest; | ||
35 | import org.thingsboard.server.service.security.AccessValidator; | 52 | import org.thingsboard.server.service.security.AccessValidator; |
36 | 53 | ||
37 | import java.util.Arrays; | 54 | import java.util.Arrays; |
@@ -47,72 +64,27 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. | @@ -47,72 +64,27 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. | ||
47 | * @author Valerii Sosliuk | 64 | * @author Valerii Sosliuk |
48 | */ | 65 | */ |
49 | @Slf4j | 66 | @Slf4j |
50 | -public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractControllerTest { | 67 | +public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractMqttIntegrationTest { |
51 | 68 | ||
52 | - private static final String MQTT_URL = "tcp://localhost:1883"; | ||
53 | - private static final Long TIME_TO_HANDLE_REQUEST = 500L; | 69 | + protected static final String DEVICE_RESPONSE = "{\"value1\":\"A\",\"value2\":\"B\"}"; |
54 | 70 | ||
55 | - private Tenant savedTenant; | ||
56 | - private User tenantAdmin; | ||
57 | - private Long asyncContextTimeoutToUseRpcPlugin; | ||
58 | - | ||
59 | - private static final AtomicInteger atomicInteger = new AtomicInteger(2); | ||
60 | - | ||
61 | - | ||
62 | - @Before | ||
63 | - public void beforeTest() throws Exception { | ||
64 | - loginSysAdmin(); | 71 | + protected Long asyncContextTimeoutToUseRpcPlugin; |
65 | 72 | ||
73 | + protected void processBeforeTest(String deviceName, String gatewayName, TransportPayloadType payloadType, String telemetryTopic, String attributesTopic) throws Exception { | ||
74 | + super.processBeforeTest(deviceName, gatewayName, payloadType, telemetryTopic, attributesTopic); | ||
66 | asyncContextTimeoutToUseRpcPlugin = 10000L; | 75 | asyncContextTimeoutToUseRpcPlugin = 10000L; |
67 | - | ||
68 | - Tenant tenant = new Tenant(); | ||
69 | - tenant.setTitle("My tenant"); | ||
70 | - savedTenant = doPost("/api/tenant", tenant, Tenant.class); | ||
71 | - Assert.assertNotNull(savedTenant); | ||
72 | - | ||
73 | - tenantAdmin = new User(); | ||
74 | - tenantAdmin.setAuthority(Authority.TENANT_ADMIN); | ||
75 | - tenantAdmin.setTenantId(savedTenant.getId()); | ||
76 | - tenantAdmin.setEmail("tenant" + atomicInteger.getAndIncrement() + "@thingsboard.org"); | ||
77 | - tenantAdmin.setFirstName("Joe"); | ||
78 | - tenantAdmin.setLastName("Downs"); | ||
79 | - | ||
80 | - createUserAndLogin(tenantAdmin, "testPassword1"); | ||
81 | } | 76 | } |
82 | 77 | ||
83 | - @After | ||
84 | - public void afterTest() throws Exception { | ||
85 | - loginSysAdmin(); | ||
86 | - if (savedTenant != null) { | ||
87 | - doDelete("/api/tenant/" + savedTenant.getId().getId().toString()).andExpect(status().isOk()); | ||
88 | - } | ||
89 | - } | ||
90 | - | ||
91 | - @Test | ||
92 | - public void testServerMqttOneWayRpc() throws Exception { | ||
93 | - Device device = new Device(); | ||
94 | - device.setName("Test One-Way Server-Side RPC"); | ||
95 | - device.setType("default"); | ||
96 | - Device savedDevice = getSavedDevice(device); | ||
97 | - DeviceCredentials deviceCredentials = getDeviceCredentials(savedDevice); | ||
98 | - assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId()); | ||
99 | - String accessToken = deviceCredentials.getCredentialsId(); | ||
100 | - assertNotNull(accessToken); | ||
101 | - | ||
102 | - String clientId = MqttAsyncClient.generateClientId(); | ||
103 | - MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId); | ||
104 | - | ||
105 | - MqttConnectOptions options = new MqttConnectOptions(); | ||
106 | - options.setUserName(accessToken); | ||
107 | - client.connect(options).waitForCompletion(); | 78 | + protected void processOneWayRpcTest() throws Exception { |
79 | + MqttAsyncClient client = getMqttAsyncClient(accessToken); | ||
108 | 80 | ||
109 | CountDownLatch latch = new CountDownLatch(1); | 81 | CountDownLatch latch = new CountDownLatch(1); |
110 | TestMqttCallback callback = new TestMqttCallback(client, latch); | 82 | TestMqttCallback callback = new TestMqttCallback(client, latch); |
111 | client.setCallback(callback); | 83 | client.setCallback(callback); |
112 | 84 | ||
113 | - client.subscribe("v1/devices/me/rpc/request/+", MqttQoS.AT_MOST_ONCE.value()); | 85 | + client.subscribe(MqttTopics.DEVICE_RPC_REQUESTS_SUB_TOPIC, MqttQoS.AT_MOST_ONCE.value()); |
114 | 86 | ||
115 | - Thread.sleep(2000); | 87 | + Thread.sleep(1000); |
116 | 88 | ||
117 | String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}"; | 89 | String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}"; |
118 | String deviceId = savedDevice.getId().getId().toString(); | 90 | String deviceId = savedDevice.getId().getId().toString(); |
@@ -122,100 +94,112 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC | @@ -122,100 +94,112 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC | ||
122 | assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS()); | 94 | assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS()); |
123 | } | 95 | } |
124 | 96 | ||
125 | - @Test | ||
126 | - public void testServerMqttOneWayRpcDeviceOffline() throws Exception { | ||
127 | - Device device = new Device(); | ||
128 | - device.setName("Test One-Way Server-Side RPC Device Offline"); | ||
129 | - device.setType("default"); | ||
130 | - Device savedDevice = getSavedDevice(device); | ||
131 | - DeviceCredentials deviceCredentials = getDeviceCredentials(savedDevice); | ||
132 | - assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId()); | ||
133 | - String accessToken = deviceCredentials.getCredentialsId(); | ||
134 | - assertNotNull(accessToken); | ||
135 | - | ||
136 | - String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"24\",\"value\": 1},\"timeout\": 6000}"; | 97 | + protected void processOneWayRpcTestGateway(String deviceName) throws Exception { |
98 | + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); | ||
99 | + String payload = "{\"device\":\"" + deviceName + "\"}"; | ||
100 | + byte[] payloadBytes = payload.getBytes(); | ||
101 | + validateOneWayRpcGatewayResponse(deviceName, client, payloadBytes); | ||
102 | + } | ||
103 | + | ||
104 | + protected void processTwoWayRpcTest() throws Exception { | ||
105 | + MqttAsyncClient client = getMqttAsyncClient(accessToken); | ||
106 | + client.subscribe(MqttTopics.DEVICE_RPC_REQUESTS_SUB_TOPIC, 1); | ||
107 | + | ||
108 | + CountDownLatch latch = new CountDownLatch(1); | ||
109 | + TestMqttCallback callback = new TestMqttCallback(client, latch); | ||
110 | + client.setCallback(callback); | ||
111 | + | ||
112 | + Thread.sleep(1000); | ||
113 | + | ||
114 | + String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"26\",\"value\": 1}}"; | ||
137 | String deviceId = savedDevice.getId().getId().toString(); | 115 | String deviceId = savedDevice.getId().getId().toString(); |
138 | 116 | ||
139 | - doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().is(409), | ||
140 | - asyncContextTimeoutToUseRpcPlugin); | 117 | + String result = doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk()); |
118 | + String expected = "{\"value1\":\"A\",\"value2\":\"B\"}"; | ||
119 | + latch.await(3, TimeUnit.SECONDS); | ||
120 | + Assert.assertEquals(expected, result); | ||
141 | } | 121 | } |
142 | 122 | ||
143 | - @Test | ||
144 | - public void testServerMqttOneWayRpcDeviceDoesNotExist() throws Exception { | ||
145 | - String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"25\",\"value\": 1}}"; | ||
146 | - String nonExistentDeviceId = Uuids.timeBased().toString(); | 123 | + protected void processTwoWayRpcTestGateway(String deviceName) throws Exception { |
124 | + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); | ||
125 | + | ||
126 | + String payload = "{\"device\":\"" + deviceName + "\"}"; | ||
127 | + byte[] payloadBytes = payload.getBytes(); | ||
147 | 128 | ||
148 | - String result = doPostAsync("/api/plugins/rpc/oneway/" + nonExistentDeviceId, setGpioRequest, String.class, | ||
149 | - status().isNotFound()); | ||
150 | - Assert.assertEquals(AccessValidator.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result); | 129 | + validateTwoWayRpcGateway(deviceName, client, payloadBytes); |
151 | } | 130 | } |
152 | 131 | ||
153 | - @Test | ||
154 | - public void testServerMqttTwoWayRpc() throws Exception { | ||
155 | - Device device = new Device(); | ||
156 | - device.setName("Test Two-Way Server-Side RPC"); | ||
157 | - device.setType("default"); | ||
158 | - Device savedDevice = getSavedDevice(device); | ||
159 | - DeviceCredentials deviceCredentials = getDeviceCredentials(savedDevice); | ||
160 | - assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId()); | ||
161 | - String accessToken = deviceCredentials.getCredentialsId(); | ||
162 | - assertNotNull(accessToken); | 132 | + protected void validateOneWayRpcGatewayResponse(String deviceName, MqttAsyncClient client, byte[] payloadBytes) throws Exception { |
133 | + publishMqttMsg(client, payloadBytes, MqttTopics.GATEWAY_CONNECT_TOPIC); | ||
163 | 134 | ||
164 | - String clientId = MqttAsyncClient.generateClientId(); | ||
165 | - MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId); | 135 | + Device savedDevice = doExecuteWithRetriesAndInterval( |
136 | + () -> getDeviceByName(deviceName), | ||
137 | + 20, | ||
138 | + 100 | ||
139 | + ); | ||
140 | + assertNotNull(savedDevice); | ||
166 | 141 | ||
167 | - MqttConnectOptions options = new MqttConnectOptions(); | ||
168 | - options.setUserName(accessToken); | ||
169 | - client.connect(options).waitForCompletion(); | ||
170 | - client.subscribe("v1/devices/me/rpc/request/+", 1); | ||
171 | - client.setCallback(new TestMqttCallback(client, new CountDownLatch(1))); | 142 | + CountDownLatch latch = new CountDownLatch(1); |
143 | + TestMqttCallback callback = new TestMqttCallback(client, latch); | ||
144 | + client.setCallback(callback); | ||
172 | 145 | ||
173 | - Thread.sleep(2000); | 146 | + client.subscribe(MqttTopics.GATEWAY_RPC_TOPIC, MqttQoS.AT_MOST_ONCE.value()); |
174 | 147 | ||
175 | - String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"26\",\"value\": 1}}"; | ||
176 | - String deviceId = savedDevice.getId().getId().toString(); | 148 | + Thread.sleep(1000); |
177 | 149 | ||
178 | - String result = doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk()); | ||
179 | - Assert.assertEquals("{\"value1\":\"A\",\"value2\":\"B\"}", result); | 150 | + String setGpioRequest = "{\"method\": \"toggle_gpio\", \"params\": {\"pin\":1}}"; |
151 | + String deviceId = savedDevice.getId().getId().toString(); | ||
152 | + String result = doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isOk()); | ||
153 | + Assert.assertTrue(StringUtils.isEmpty(result)); | ||
154 | + latch.await(3, TimeUnit.SECONDS); | ||
155 | + assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS()); | ||
180 | } | 156 | } |
181 | 157 | ||
182 | - @Test | ||
183 | - public void testServerMqttTwoWayRpcDeviceOffline() throws Exception { | ||
184 | - Device device = new Device(); | ||
185 | - device.setName("Test Two-Way Server-Side RPC Device Offline"); | ||
186 | - device.setType("default"); | ||
187 | - Device savedDevice = getSavedDevice(device); | ||
188 | - DeviceCredentials deviceCredentials = getDeviceCredentials(savedDevice); | ||
189 | - assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId()); | ||
190 | - String accessToken = deviceCredentials.getCredentialsId(); | ||
191 | - assertNotNull(accessToken); | ||
192 | - | ||
193 | - String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"27\",\"value\": 1},\"timeout\": 6000}"; | ||
194 | - String deviceId = savedDevice.getId().getId().toString(); | 158 | + protected void validateTwoWayRpcGateway(String deviceName, MqttAsyncClient client, byte[] payloadBytes) throws Exception { |
159 | + publishMqttMsg(client, payloadBytes, MqttTopics.GATEWAY_CONNECT_TOPIC); | ||
195 | 160 | ||
196 | - doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().is(409), | ||
197 | - asyncContextTimeoutToUseRpcPlugin); | ||
198 | - } | 161 | + Device savedDevice = doExecuteWithRetriesAndInterval( |
162 | + () -> getDeviceByName(deviceName), | ||
163 | + 20, | ||
164 | + 100 | ||
165 | + ); | ||
166 | + assertNotNull(savedDevice); | ||
167 | + | ||
168 | + CountDownLatch latch = new CountDownLatch(1); | ||
169 | + TestMqttCallback callback = new TestMqttCallback(client, latch); | ||
170 | + client.setCallback(callback); | ||
171 | + | ||
172 | + client.subscribe(MqttTopics.GATEWAY_RPC_TOPIC, MqttQoS.AT_MOST_ONCE.value()); | ||
199 | 173 | ||
200 | - @Test | ||
201 | - public void testServerMqttTwoWayRpcDeviceDoesNotExist() throws Exception { | ||
202 | - String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"28\",\"value\": 1}}"; | ||
203 | - String nonExistentDeviceId = Uuids.timeBased().toString(); | 174 | + Thread.sleep(1000); |
204 | 175 | ||
205 | - String result = doPostAsync("/api/plugins/rpc/twoway/" + nonExistentDeviceId, setGpioRequest, String.class, | ||
206 | - status().isNotFound()); | ||
207 | - Assert.assertEquals(AccessValidator.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result); | 176 | + String setGpioRequest = "{\"method\": \"toggle_gpio\", \"params\": {\"pin\":1}}"; |
177 | + String deviceId = savedDevice.getId().getId().toString(); | ||
178 | + String result = doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk()); | ||
179 | + latch.await(3, TimeUnit.SECONDS); | ||
180 | + String expected = "{\"success\":true}"; | ||
181 | + assertEquals(expected, result); | ||
182 | + assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS()); | ||
208 | } | 183 | } |
209 | 184 | ||
210 | - private Device getSavedDevice(Device device) throws Exception { | ||
211 | - return doPost("/api/device", device, Device.class); | 185 | + private Device getDeviceByName(String deviceName) throws Exception { |
186 | + return doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class); | ||
212 | } | 187 | } |
213 | 188 | ||
214 | - private DeviceCredentials getDeviceCredentials(Device savedDevice) throws Exception { | ||
215 | - return doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class); | 189 | + protected MqttMessage processMessageArrived(String requestTopic, MqttMessage mqttMessage) throws MqttException, InvalidProtocolBufferException { |
190 | + MqttMessage message = new MqttMessage(); | ||
191 | + if (requestTopic.startsWith(MqttTopics.BASE_DEVICE_API_TOPIC)) { | ||
192 | + message.setPayload(DEVICE_RESPONSE.getBytes(StandardCharset.UTF_8)); | ||
193 | + } else { | ||
194 | + JsonNode requestMsgNode = JacksonUtil.toJsonNode(new String(mqttMessage.getPayload(), StandardCharset.UTF_8)); | ||
195 | + String deviceName = requestMsgNode.get("device").asText(); | ||
196 | + int requestId = requestMsgNode.get("data").get("id").asInt(); | ||
197 | + message.setPayload(("{\"device\": \"" + deviceName + "\", \"id\": " + requestId + ", \"data\": {\"success\": true}}").getBytes(StandardCharset.UTF_8)); | ||
198 | + } | ||
199 | + return message; | ||
216 | } | 200 | } |
217 | 201 | ||
218 | - private static class TestMqttCallback implements MqttCallback { | 202 | + private class TestMqttCallback implements MqttCallback { |
219 | 203 | ||
220 | private final MqttAsyncClient client; | 204 | private final MqttAsyncClient client; |
221 | private final CountDownLatch latch; | 205 | private final CountDownLatch latch; |
@@ -237,11 +221,9 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC | @@ -237,11 +221,9 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC | ||
237 | @Override | 221 | @Override |
238 | public void messageArrived(String requestTopic, MqttMessage mqttMessage) throws Exception { | 222 | public void messageArrived(String requestTopic, MqttMessage mqttMessage) throws Exception { |
239 | log.info("Message Arrived: " + Arrays.toString(mqttMessage.getPayload())); | 223 | log.info("Message Arrived: " + Arrays.toString(mqttMessage.getPayload())); |
240 | - MqttMessage message = new MqttMessage(); | ||
241 | String responseTopic = requestTopic.replace("request", "response"); | 224 | String responseTopic = requestTopic.replace("request", "response"); |
242 | - message.setPayload("{\"value1\":\"A\", \"value2\":\"B\"}".getBytes("UTF-8")); | ||
243 | qoS = mqttMessage.getQos(); | 225 | qoS = mqttMessage.getQos(); |
244 | - client.publish(responseTopic, message); | 226 | + client.publish(responseTopic, processMessageArrived(requestTopic, mqttMessage)); |
245 | latch.countDown(); | 227 | latch.countDown(); |
246 | } | 228 | } |
247 | 229 |
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.mqtt.rpc; | ||
17 | + | ||
18 | +import lombok.extern.slf4j.Slf4j; | ||
19 | +import org.eclipse.paho.client.mqttv3.MqttAsyncClient; | ||
20 | +import org.junit.After; | ||
21 | +import org.junit.Before; | ||
22 | +import org.junit.Ignore; | ||
23 | +import org.junit.Test; | ||
24 | +import org.thingsboard.server.common.data.TransportPayloadType; | ||
25 | + | ||
26 | +@Slf4j | ||
27 | +public abstract class AbstractMqttServerSideRpcJsonIntegrationTest extends AbstractMqttServerSideRpcIntegrationTest { | ||
28 | + | ||
29 | + @Before | ||
30 | + public void beforeTest() throws Exception { | ||
31 | + processBeforeTest("RPC test device", "RPC test gateway", TransportPayloadType.JSON, null, null); | ||
32 | + } | ||
33 | + | ||
34 | + @After | ||
35 | + public void afterTest() throws Exception { | ||
36 | + super.processAfterTest(); | ||
37 | + } | ||
38 | + | ||
39 | + @Test | ||
40 | + public void testServerMqttOneWayRpc() throws Exception { | ||
41 | + processOneWayRpcTest(); | ||
42 | + } | ||
43 | + | ||
44 | + @Test | ||
45 | + public void testServerMqttTwoWayRpc() throws Exception { | ||
46 | + processTwoWayRpcTest(); | ||
47 | + } | ||
48 | + | ||
49 | + @Test | ||
50 | + public void testGatewayServerMqttOneWayRpc() throws Exception { | ||
51 | + processOneWayRpcTestGateway("Gateway Device OneWay RPC Json"); | ||
52 | + } | ||
53 | + | ||
54 | + @Test | ||
55 | + public void testGatewayServerMqttTwoWayRpc() throws Exception { | ||
56 | + processTwoWayRpcTestGateway("Gateway Device TwoWay RPC Json"); | ||
57 | + } | ||
58 | + | ||
59 | + protected void processOneWayRpcTestGateway(String deviceName) throws Exception { | ||
60 | + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); | ||
61 | + String payload = "{\"device\": \"" + deviceName + "\", \"type\": \"" + TransportPayloadType.JSON.name() + "\"}"; | ||
62 | + byte[] payloadBytes = payload.getBytes(); | ||
63 | + validateOneWayRpcGatewayResponse(deviceName, client, payloadBytes); | ||
64 | + } | ||
65 | + | ||
66 | +} |
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.mqtt.rpc; | ||
17 | + | ||
18 | +import com.google.protobuf.InvalidProtocolBufferException; | ||
19 | +import lombok.extern.slf4j.Slf4j; | ||
20 | +import org.eclipse.paho.client.mqttv3.MqttAsyncClient; | ||
21 | +import org.eclipse.paho.client.mqttv3.MqttException; | ||
22 | +import org.eclipse.paho.client.mqttv3.MqttMessage; | ||
23 | +import org.junit.After; | ||
24 | +import org.junit.Before; | ||
25 | +import org.junit.Ignore; | ||
26 | +import org.junit.Test; | ||
27 | +import org.thingsboard.server.common.data.TransportPayloadType; | ||
28 | +import org.thingsboard.server.common.data.device.profile.MqttTopics; | ||
29 | +import org.thingsboard.server.gen.transport.TransportApiProtos; | ||
30 | +import org.thingsboard.server.gen.transport.TransportProtos; | ||
31 | + | ||
32 | +import static org.junit.Assert.assertEquals; | ||
33 | +import static org.junit.Assert.assertNotNull; | ||
34 | + | ||
35 | +@Slf4j | ||
36 | +public abstract class AbstractMqttServerSideRpcProtoIntegrationTest extends AbstractMqttServerSideRpcIntegrationTest { | ||
37 | + | ||
38 | + @Before | ||
39 | + public void beforeTest() throws Exception { | ||
40 | + processBeforeTest("RPC test device", "RPC test gateway", TransportPayloadType.PROTOBUF, null, null); | ||
41 | + } | ||
42 | + | ||
43 | + @After | ||
44 | + public void afterTest() throws Exception { | ||
45 | + super.processAfterTest(); | ||
46 | + } | ||
47 | + | ||
48 | + @Test | ||
49 | + public void testServerMqttOneWayRpc() throws Exception { | ||
50 | + processOneWayRpcTest(); | ||
51 | + } | ||
52 | + | ||
53 | + @Test | ||
54 | + public void testServerMqttTwoWayRpc() throws Exception { | ||
55 | + processTwoWayRpcTest(); | ||
56 | + } | ||
57 | + | ||
58 | + @Test | ||
59 | + public void testGatewayServerMqttOneWayRpc() throws Exception { | ||
60 | + processOneWayRpcTestGateway("Gateway Device OneWay RPC Proto"); | ||
61 | + } | ||
62 | + | ||
63 | + @Test | ||
64 | + public void testGatewayServerMqttTwoWayRpc() throws Exception { | ||
65 | + processTwoWayRpcTestGateway("Gateway Device TwoWay RPC Proto"); | ||
66 | + } | ||
67 | + | ||
68 | + protected void processTwoWayRpcTestGateway(String deviceName) throws Exception { | ||
69 | + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); | ||
70 | + TransportApiProtos.ConnectMsg connectMsgProto = getConnectProto(deviceName); | ||
71 | + byte[] payloadBytes = connectMsgProto.toByteArray(); | ||
72 | + validateTwoWayRpcGateway(deviceName, client, payloadBytes); | ||
73 | + } | ||
74 | + | ||
75 | + protected void processOneWayRpcTestGateway(String deviceName) throws Exception { | ||
76 | + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); | ||
77 | + TransportApiProtos.ConnectMsg connectMsgProto = getConnectProto(deviceName); | ||
78 | + byte[] payloadBytes = connectMsgProto.toByteArray(); | ||
79 | + validateOneWayRpcGatewayResponse(deviceName, client, payloadBytes); | ||
80 | + } | ||
81 | + | ||
82 | + | ||
83 | + private TransportApiProtos.ConnectMsg getConnectProto(String deviceName) { | ||
84 | + TransportApiProtos.ConnectMsg.Builder builder = TransportApiProtos.ConnectMsg.newBuilder(); | ||
85 | + builder.setDeviceName(deviceName); | ||
86 | + builder.setDeviceType(TransportPayloadType.PROTOBUF.name()); | ||
87 | + return builder.build(); | ||
88 | + } | ||
89 | + | ||
90 | + protected MqttMessage processMessageArrived(String requestTopic, MqttMessage mqttMessage) throws MqttException, InvalidProtocolBufferException { | ||
91 | + MqttMessage message = new MqttMessage(); | ||
92 | + if (requestTopic.startsWith(MqttTopics.BASE_DEVICE_API_TOPIC)) { | ||
93 | + TransportProtos.ToDeviceRpcResponseMsg toDeviceRpcResponseMsg = TransportProtos.ToDeviceRpcResponseMsg.newBuilder() | ||
94 | + .setPayload(DEVICE_RESPONSE) | ||
95 | + .setRequestId(0) | ||
96 | + .build(); | ||
97 | + message.setPayload(toDeviceRpcResponseMsg.toByteArray()); | ||
98 | + } else { | ||
99 | + TransportApiProtos.GatewayDeviceRpcRequestMsg msg = TransportApiProtos.GatewayDeviceRpcRequestMsg.parseFrom(mqttMessage.getPayload()); | ||
100 | + String deviceName = msg.getDeviceName(); | ||
101 | + int requestId = msg.getRpcRequestMsg().getRequestId(); | ||
102 | + TransportApiProtos.GatewayRpcResponseMsg gatewayRpcResponseMsg = TransportApiProtos.GatewayRpcResponseMsg.newBuilder() | ||
103 | + .setDeviceName(deviceName) | ||
104 | + .setId(requestId) | ||
105 | + .setData("{\"success\": true}") | ||
106 | + .build(); | ||
107 | + message.setPayload(gatewayRpcResponseMsg.toByteArray()); | ||
108 | + } | ||
109 | + return message; | ||
110 | + } | ||
111 | + | ||
112 | + | ||
113 | + | ||
114 | +} |
@@ -16,11 +16,11 @@ | @@ -16,11 +16,11 @@ | ||
16 | package org.thingsboard.server.mqtt.rpc.nosql; | 16 | package org.thingsboard.server.mqtt.rpc.nosql; |
17 | 17 | ||
18 | import org.thingsboard.server.dao.service.DaoNoSqlTest; | 18 | import org.thingsboard.server.dao.service.DaoNoSqlTest; |
19 | -import org.thingsboard.server.mqtt.rpc.AbstractMqttServerSideRpcIntegrationTest; | 19 | +import org.thingsboard.server.mqtt.rpc.AbstractMqttServerSideRpcDefaultIntegrationTest; |
20 | 20 | ||
21 | /** | 21 | /** |
22 | * Created by Valerii Sosliuk on 8/22/2017. | 22 | * Created by Valerii Sosliuk on 8/22/2017. |
23 | */ | 23 | */ |
24 | @DaoNoSqlTest | 24 | @DaoNoSqlTest |
25 | -public class MqttServerSideRpcNoSqlIntegrationTest extends AbstractMqttServerSideRpcIntegrationTest { | 25 | +public class MqttServerSideRpcNoSqlIntegrationTest extends AbstractMqttServerSideRpcDefaultIntegrationTest { |
26 | } | 26 | } |
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.mqtt.rpc.sql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.rpc.AbstractMqttServerSideRpcJsonIntegrationTest; | ||
20 | + | ||
21 | +@DaoSqlTest | ||
22 | +public class MqttServerSideRpcJsonSqlIntegrationTest extends AbstractMqttServerSideRpcJsonIntegrationTest { | ||
23 | +} |
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.mqtt.rpc.sql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.rpc.AbstractMqttServerSideRpcProtoIntegrationTest; | ||
20 | + | ||
21 | + | ||
22 | +@DaoSqlTest | ||
23 | +public class MqttServerSideRpcProtoSqlIntegrationTest extends AbstractMqttServerSideRpcProtoIntegrationTest { | ||
24 | +} |
@@ -16,11 +16,11 @@ | @@ -16,11 +16,11 @@ | ||
16 | package org.thingsboard.server.mqtt.rpc.sql; | 16 | package org.thingsboard.server.mqtt.rpc.sql; |
17 | 17 | ||
18 | import org.thingsboard.server.dao.service.DaoSqlTest; | 18 | import org.thingsboard.server.dao.service.DaoSqlTest; |
19 | -import org.thingsboard.server.mqtt.rpc.AbstractMqttServerSideRpcIntegrationTest; | 19 | +import org.thingsboard.server.mqtt.rpc.AbstractMqttServerSideRpcDefaultIntegrationTest; |
20 | 20 | ||
21 | /** | 21 | /** |
22 | * Created by Valerii Sosliuk on 8/22/2017. | 22 | * Created by Valerii Sosliuk on 8/22/2017. |
23 | */ | 23 | */ |
24 | @DaoSqlTest | 24 | @DaoSqlTest |
25 | -public class MqttServerSideRpcSqlIntegrationTest extends AbstractMqttServerSideRpcIntegrationTest { | 25 | +public class MqttServerSideRpcSqlIntegrationTest extends AbstractMqttServerSideRpcDefaultIntegrationTest { |
26 | } | 26 | } |
application/src/test/java/org/thingsboard/server/mqtt/telemetry/AbstractMqttTelemetryIntegrationTest.java
deleted
100644 → 0
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.mqtt.telemetry; | ||
17 | - | ||
18 | -import io.netty.handler.codec.mqtt.MqttQoS; | ||
19 | -import lombok.extern.slf4j.Slf4j; | ||
20 | -import org.eclipse.paho.client.mqttv3.*; | ||
21 | -import org.junit.Before; | ||
22 | -import org.junit.Ignore; | ||
23 | -import org.junit.Test; | ||
24 | -import org.springframework.web.util.UriComponentsBuilder; | ||
25 | -import org.thingsboard.server.common.data.Device; | ||
26 | -import org.thingsboard.server.common.data.security.DeviceCredentials; | ||
27 | -import org.thingsboard.server.controller.AbstractControllerTest; | ||
28 | -import org.thingsboard.server.dao.service.DaoNoSqlTest; | ||
29 | - | ||
30 | -import java.net.URI; | ||
31 | -import java.util.*; | ||
32 | -import java.util.concurrent.CountDownLatch; | ||
33 | -import java.util.concurrent.TimeUnit; | ||
34 | - | ||
35 | -import static org.junit.Assert.assertEquals; | ||
36 | -import static org.junit.Assert.assertNotNull; | ||
37 | -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; | ||
38 | - | ||
39 | -/** | ||
40 | - * @author Valerii Sosliuk | ||
41 | - */ | ||
42 | -@Slf4j | ||
43 | -public abstract class AbstractMqttTelemetryIntegrationTest extends AbstractControllerTest { | ||
44 | - | ||
45 | - private static final String MQTT_URL = "tcp://localhost:1883"; | ||
46 | - | ||
47 | - private Device savedDevice; | ||
48 | - private String accessToken; | ||
49 | - | ||
50 | - @Before | ||
51 | - public void beforeTest() throws Exception { | ||
52 | - loginTenantAdmin(); | ||
53 | - | ||
54 | - Device device = new Device(); | ||
55 | - device.setName("Test device"); | ||
56 | - device.setType("default"); | ||
57 | - savedDevice = doPost("/api/device", device, Device.class); | ||
58 | - | ||
59 | - DeviceCredentials deviceCredentials = | ||
60 | - doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class); | ||
61 | - | ||
62 | - assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId()); | ||
63 | - accessToken = deviceCredentials.getCredentialsId(); | ||
64 | - assertNotNull(accessToken); | ||
65 | - } | ||
66 | - | ||
67 | - @Test | ||
68 | - public void testPushMqttRpcData() throws Exception { | ||
69 | - String clientId = MqttAsyncClient.generateClientId(); | ||
70 | - MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId); | ||
71 | - | ||
72 | - MqttConnectOptions options = new MqttConnectOptions(); | ||
73 | - options.setUserName(accessToken); | ||
74 | - client.connect(options); | ||
75 | - Thread.sleep(3000); | ||
76 | - MqttMessage message = new MqttMessage(); | ||
77 | - message.setPayload("{\"key1\":\"value1\", \"key2\":true, \"key3\": 3.0, \"key4\": 4}".getBytes()); | ||
78 | - client.publish("v1/devices/me/telemetry", message); | ||
79 | - | ||
80 | - String deviceId = savedDevice.getId().getId().toString(); | ||
81 | - | ||
82 | - Thread.sleep(2000); | ||
83 | - List<String> actualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + deviceId + "/keys/timeseries", List.class); | ||
84 | - Set<String> actualKeySet = new HashSet<>(actualKeys); | ||
85 | - | ||
86 | - List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4"); | ||
87 | - Set<String> expectedKeySet = new HashSet<>(expectedKeys); | ||
88 | - | ||
89 | - assertEquals(expectedKeySet, actualKeySet); | ||
90 | - | ||
91 | - String getTelemetryValuesUrl = "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/timeseries?keys=" + String.join(",", actualKeySet); | ||
92 | - Map<String, List<Map<String, String>>> values = doGetAsync(getTelemetryValuesUrl, Map.class); | ||
93 | - | ||
94 | - assertEquals("value1", values.get("key1").get(0).get("value")); | ||
95 | - assertEquals("true", values.get("key2").get(0).get("value")); | ||
96 | - assertEquals("3.0", values.get("key3").get(0).get("value")); | ||
97 | - assertEquals("4", values.get("key4").get(0).get("value")); | ||
98 | - } | ||
99 | - | ||
100 | - | ||
101 | -// @Test - Unstable | ||
102 | - public void testMqttQoSLevel() throws Exception { | ||
103 | - String clientId = MqttAsyncClient.generateClientId(); | ||
104 | - MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId); | ||
105 | - | ||
106 | - MqttConnectOptions options = new MqttConnectOptions(); | ||
107 | - options.setUserName(accessToken); | ||
108 | - CountDownLatch latch = new CountDownLatch(1); | ||
109 | - TestMqttCallback callback = new TestMqttCallback(client, latch); | ||
110 | - client.setCallback(callback); | ||
111 | - client.connect(options).waitForCompletion(5000); | ||
112 | - client.subscribe("v1/devices/me/attributes", MqttQoS.AT_MOST_ONCE.value()); | ||
113 | - String payload = "{\"key\":\"uniqueValue\"}"; | ||
114 | -// TODO 3.1: we need to acknowledge subscription only after it is processed by device actor and not when the message is pushed to queue. | ||
115 | -// MqttClient -> SUB REQUEST -> Transport -> Kafka -> Device Actor (subscribed) | ||
116 | -// MqttClient <- SUB_ACK <- Transport | ||
117 | - Thread.sleep(5000); | ||
118 | - doPostAsync("/api/plugins/telemetry/" + savedDevice.getId() + "/SHARED_SCOPE", payload, String.class, status().isOk()); | ||
119 | - latch.await(10, TimeUnit.SECONDS); | ||
120 | - assertEquals(payload, callback.getPayload()); | ||
121 | - assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS()); | ||
122 | - } | ||
123 | - | ||
124 | - private static class TestMqttCallback implements MqttCallback { | ||
125 | - | ||
126 | - private final MqttAsyncClient client; | ||
127 | - private final CountDownLatch latch; | ||
128 | - private volatile Integer qoS; | ||
129 | - private volatile String payload; | ||
130 | - | ||
131 | - String getPayload() { | ||
132 | - return payload; | ||
133 | - } | ||
134 | - | ||
135 | - TestMqttCallback(MqttAsyncClient client, CountDownLatch latch) { | ||
136 | - this.client = client; | ||
137 | - this.latch = latch; | ||
138 | - } | ||
139 | - | ||
140 | - int getQoS() { | ||
141 | - return qoS; | ||
142 | - } | ||
143 | - | ||
144 | - @Override | ||
145 | - public void connectionLost(Throwable throwable) { | ||
146 | - log.error("Client connection lost", throwable); | ||
147 | - } | ||
148 | - | ||
149 | - @Override | ||
150 | - public void messageArrived(String requestTopic, MqttMessage mqttMessage) { | ||
151 | - payload = new String(mqttMessage.getPayload()); | ||
152 | - qoS = mqttMessage.getQos(); | ||
153 | - latch.countDown(); | ||
154 | - } | ||
155 | - | ||
156 | - @Override | ||
157 | - public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) { | ||
158 | - | ||
159 | - } | ||
160 | - } | ||
161 | - | ||
162 | - | ||
163 | -} |
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.mqtt.telemetry.attributes; | ||
17 | + | ||
18 | +import lombok.extern.slf4j.Slf4j; | ||
19 | +import org.eclipse.paho.client.mqttv3.MqttAsyncClient; | ||
20 | +import org.junit.After; | ||
21 | +import org.junit.Before; | ||
22 | +import org.junit.Test; | ||
23 | +import org.thingsboard.server.common.data.Device; | ||
24 | +import org.thingsboard.server.common.data.device.profile.MqttTopics; | ||
25 | +import org.thingsboard.server.common.data.id.DeviceId; | ||
26 | +import org.thingsboard.server.mqtt.AbstractMqttIntegrationTest; | ||
27 | + | ||
28 | +import java.util.Arrays; | ||
29 | +import java.util.HashSet; | ||
30 | +import java.util.LinkedHashMap; | ||
31 | +import java.util.List; | ||
32 | +import java.util.Map; | ||
33 | +import java.util.Set; | ||
34 | + | ||
35 | +import static org.junit.Assert.assertEquals; | ||
36 | +import static org.junit.Assert.assertNotNull; | ||
37 | +import static org.junit.Assert.assertTrue; | ||
38 | + | ||
39 | +@Slf4j | ||
40 | +public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqttIntegrationTest { | ||
41 | + | ||
42 | + protected static final String PAYLOAD_VALUES_STR = "{\"key1\":\"value1\", \"key2\":true, \"key3\": 3.0, \"key4\": 4," + | ||
43 | + " \"key5\": {\"someNumber\": 42, \"someArray\": [1,2,3], \"someNestedObject\": {\"key\": \"value\"}}}"; | ||
44 | + | ||
45 | + @Before | ||
46 | + public void beforeTest() throws Exception { | ||
47 | + processBeforeTest("Test Post Attributes device", "Test Post Attributes gateway", null, null, null); | ||
48 | + } | ||
49 | + | ||
50 | + @After | ||
51 | + public void afterTest() throws Exception { | ||
52 | + processAfterTest(); | ||
53 | + } | ||
54 | + | ||
55 | + @Test | ||
56 | + public void testPushMqttAttributes() throws Exception { | ||
57 | + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); | ||
58 | + processAttributesTest(MqttTopics.DEVICE_ATTRIBUTES_TOPIC, expectedKeys, PAYLOAD_VALUES_STR.getBytes()); | ||
59 | + } | ||
60 | + | ||
61 | + @Test | ||
62 | + public void testPushMqttAttributesGateway() throws Exception { | ||
63 | + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); | ||
64 | + String deviceName1 = "Device A"; | ||
65 | + String deviceName2 = "Device B"; | ||
66 | + String payload = getGatewayAttributesJsonPayload(deviceName1, deviceName2); | ||
67 | + processGatewayAttributesTest(expectedKeys, payload.getBytes(), deviceName1, deviceName2); | ||
68 | + } | ||
69 | + | ||
70 | + protected void processAttributesTest(String topic, List<String> expectedKeys, byte[] payload) throws Exception { | ||
71 | + MqttAsyncClient client = getMqttAsyncClient(accessToken); | ||
72 | + | ||
73 | + publishMqttMsg(client, payload, topic); | ||
74 | + | ||
75 | + DeviceId deviceId = savedDevice.getId(); | ||
76 | + | ||
77 | + long start = System.currentTimeMillis(); | ||
78 | + long end = System.currentTimeMillis() + 5000; | ||
79 | + | ||
80 | + List<String> actualKeys = null; | ||
81 | + while (start <= end) { | ||
82 | + actualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + deviceId + "/keys/attributes/CLIENT_SCOPE", List.class); | ||
83 | + if (actualKeys.size() == expectedKeys.size()) { | ||
84 | + break; | ||
85 | + } | ||
86 | + Thread.sleep(100); | ||
87 | + start += 100; | ||
88 | + } | ||
89 | + assertNotNull(actualKeys); | ||
90 | + | ||
91 | + Set<String> actualKeySet = new HashSet<>(actualKeys); | ||
92 | + | ||
93 | + Set<String> expectedKeySet = new HashSet<>(expectedKeys); | ||
94 | + | ||
95 | + assertEquals(expectedKeySet, actualKeySet); | ||
96 | + | ||
97 | + String getAttributesValuesUrl = getAttributesValuesUrl(deviceId, actualKeySet); | ||
98 | + List<Map<String, Object>> values = doGetAsync(getAttributesValuesUrl, List.class); | ||
99 | + assertAttributesValues(values, expectedKeySet); | ||
100 | + String deleteAttributesUrl = "/api/plugins/telemetry/DEVICE/" + deviceId + "/CLIENT_SCOPE?keys=" + String.join(",", actualKeySet); | ||
101 | + doDelete(deleteAttributesUrl); | ||
102 | + } | ||
103 | + | ||
104 | + protected void processGatewayAttributesTest(List<String> expectedKeys, byte[] payload, String firstDeviceName, String secondDeviceName) throws Exception { | ||
105 | + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); | ||
106 | + | ||
107 | + publishMqttMsg(client, payload, MqttTopics.GATEWAY_ATTRIBUTES_TOPIC); | ||
108 | + | ||
109 | + Device firstDevice = doExecuteWithRetriesAndInterval(() -> doGet("/api/tenant/devices?deviceName=" + firstDeviceName, Device.class), | ||
110 | + 20, | ||
111 | + 100); | ||
112 | + | ||
113 | + assertNotNull(firstDevice); | ||
114 | + | ||
115 | + Device secondDevice = doExecuteWithRetriesAndInterval(() -> doGet("/api/tenant/devices?deviceName=" + secondDeviceName, Device.class), | ||
116 | + 20, | ||
117 | + 100); | ||
118 | + | ||
119 | + assertNotNull(secondDevice); | ||
120 | + | ||
121 | + Thread.sleep(2000); | ||
122 | + | ||
123 | + List<String> firstDeviceActualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + firstDevice.getId() + "/keys/attributes/CLIENT_SCOPE", List.class); | ||
124 | + Set<String> firstDeviceActualKeySet = new HashSet<>(firstDeviceActualKeys); | ||
125 | + | ||
126 | + List<String> secondDeviceActualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + secondDevice.getId() + "/keys/attributes/CLIENT_SCOPE", List.class); | ||
127 | + Set<String> secondDeviceActualKeySet = new HashSet<>(secondDeviceActualKeys); | ||
128 | + | ||
129 | + Set<String> expectedKeySet = new HashSet<>(expectedKeys); | ||
130 | + | ||
131 | + assertEquals(expectedKeySet, firstDeviceActualKeySet); | ||
132 | + assertEquals(expectedKeySet, secondDeviceActualKeySet); | ||
133 | + | ||
134 | + String getAttributesValuesUrlFirstDevice = getAttributesValuesUrl(firstDevice.getId(), firstDeviceActualKeySet); | ||
135 | + String getAttributesValuesUrlSecondDevice = getAttributesValuesUrl(firstDevice.getId(), secondDeviceActualKeySet); | ||
136 | + | ||
137 | + List<Map<String, Object>> firstDeviceValues = doGetAsync(getAttributesValuesUrlFirstDevice, List.class); | ||
138 | + List<Map<String, Object>> secondDeviceValues = doGetAsync(getAttributesValuesUrlSecondDevice, List.class); | ||
139 | + | ||
140 | + assertAttributesValues(firstDeviceValues, expectedKeySet); | ||
141 | + assertAttributesValues(secondDeviceValues, expectedKeySet); | ||
142 | + | ||
143 | + } | ||
144 | + | ||
145 | + protected void assertAttributesValues(List<Map<String, Object>> deviceValues, Set<String> expectedKeySet) { | ||
146 | + for (Map<String, Object> map : deviceValues) { | ||
147 | + String key = (String) map.get("key"); | ||
148 | + Object value = map.get("value"); | ||
149 | + assertTrue(expectedKeySet.contains(key)); | ||
150 | + switch (key) { | ||
151 | + case "key1": | ||
152 | + assertEquals("value1", value); | ||
153 | + break; | ||
154 | + case "key2": | ||
155 | + assertEquals(true, value); | ||
156 | + break; | ||
157 | + case "key3": | ||
158 | + assertEquals(3.0, value); | ||
159 | + break; | ||
160 | + case "key4": | ||
161 | + assertEquals(4, value); | ||
162 | + break; | ||
163 | + case "key5": | ||
164 | + assertNotNull(value); | ||
165 | + assertEquals(3, ((LinkedHashMap) value).size()); | ||
166 | + assertEquals(42, ((LinkedHashMap) value).get("someNumber")); | ||
167 | + assertEquals(Arrays.asList(1, 2, 3), ((LinkedHashMap) value).get("someArray")); | ||
168 | + LinkedHashMap<String, String> someNestedObject = (LinkedHashMap) ((LinkedHashMap) value).get("someNestedObject"); | ||
169 | + assertEquals("value", someNestedObject.get("key")); | ||
170 | + break; | ||
171 | + } | ||
172 | + } | ||
173 | + } | ||
174 | + | ||
175 | + protected String getGatewayAttributesJsonPayload(String deviceA, String deviceB) { | ||
176 | + return "{\"" + deviceA + "\": " + PAYLOAD_VALUES_STR + ", \"" + deviceB + "\": " + PAYLOAD_VALUES_STR + "}"; | ||
177 | + } | ||
178 | + | ||
179 | + private String getAttributesValuesUrl(DeviceId deviceId, Set<String> actualKeySet) { | ||
180 | + return "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/attributes/CLIENT_SCOPE?keys=" + String.join(",", actualKeySet); | ||
181 | + } | ||
182 | +} |
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.mqtt.telemetry.attributes; | ||
17 | + | ||
18 | +import lombok.extern.slf4j.Slf4j; | ||
19 | +import org.junit.After; | ||
20 | +import org.junit.Before; | ||
21 | +import org.junit.Test; | ||
22 | +import org.thingsboard.server.common.data.TransportPayloadType; | ||
23 | + | ||
24 | +import java.util.Arrays; | ||
25 | +import java.util.List; | ||
26 | + | ||
27 | +@Slf4j | ||
28 | +public abstract class AbstractMqttAttributesJsonIntegrationTest extends AbstractMqttAttributesIntegrationTest { | ||
29 | + | ||
30 | + private static final String POST_DATA_ATTRIBUTES_TOPIC = "data/attributes"; | ||
31 | + | ||
32 | + @Before | ||
33 | + public void beforeTest() throws Exception { | ||
34 | + processBeforeTest("Test Post Attributes device", "Test Post Attributes gateway", TransportPayloadType.JSON, null, POST_DATA_ATTRIBUTES_TOPIC); | ||
35 | + } | ||
36 | + | ||
37 | + @After | ||
38 | + public void afterTest() throws Exception { | ||
39 | + processAfterTest(); | ||
40 | + } | ||
41 | + | ||
42 | + @Test | ||
43 | + public void testPushMqttAttributes() throws Exception { | ||
44 | + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); | ||
45 | + processAttributesTest(POST_DATA_ATTRIBUTES_TOPIC, expectedKeys, PAYLOAD_VALUES_STR.getBytes()); | ||
46 | + } | ||
47 | + | ||
48 | + @Test | ||
49 | + public void testPushMqttAttributesGateway() throws Exception { | ||
50 | + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); | ||
51 | + String deviceName1 = "Device A"; | ||
52 | + String deviceName2 = "Device B"; | ||
53 | + String payload = getGatewayAttributesJsonPayload(deviceName1, deviceName2); | ||
54 | + processGatewayAttributesTest(expectedKeys, payload.getBytes(), deviceName1, deviceName2); | ||
55 | + } | ||
56 | +} |
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.mqtt.telemetry.attributes; | ||
17 | + | ||
18 | +import lombok.extern.slf4j.Slf4j; | ||
19 | +import org.junit.After; | ||
20 | +import org.junit.Before; | ||
21 | +import org.junit.Test; | ||
22 | +import org.thingsboard.server.common.data.TransportPayloadType; | ||
23 | +import org.thingsboard.server.gen.transport.TransportApiProtos; | ||
24 | +import org.thingsboard.server.gen.transport.TransportProtos; | ||
25 | + | ||
26 | +import java.util.Arrays; | ||
27 | +import java.util.List; | ||
28 | + | ||
29 | +import static org.junit.Assert.assertEquals; | ||
30 | +import static org.junit.Assert.assertNotNull; | ||
31 | +import static org.junit.Assert.assertTrue; | ||
32 | + | ||
33 | +@Slf4j | ||
34 | +public abstract class AbstractMqttAttributesProtoIntegrationTest extends AbstractMqttAttributesIntegrationTest { | ||
35 | + | ||
36 | + private static final String POST_DATA_ATTRIBUTES_TOPIC = "proto/attributes"; | ||
37 | + | ||
38 | + @Before | ||
39 | + public void beforeTest() throws Exception { | ||
40 | + processBeforeTest("Test Post Attributes device", "Test Post Attributes gateway", TransportPayloadType.PROTOBUF, null, POST_DATA_ATTRIBUTES_TOPIC); | ||
41 | + } | ||
42 | + | ||
43 | + @After | ||
44 | + public void afterTest() throws Exception { | ||
45 | + processAfterTest(); | ||
46 | + } | ||
47 | + | ||
48 | + @Test | ||
49 | + public void testPushMqttAttributes() throws Exception { | ||
50 | + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); | ||
51 | + TransportProtos.PostAttributeMsg msg = getPostAttributeMsg(expectedKeys); | ||
52 | + processAttributesTest(POST_DATA_ATTRIBUTES_TOPIC, expectedKeys, msg.toByteArray()); | ||
53 | + } | ||
54 | + | ||
55 | + @Test | ||
56 | + public void testPushMqttAttributesGateway() throws Exception { | ||
57 | + TransportApiProtos.GatewayAttributesMsg.Builder gatewayAttributesMsgProtoBuilder = TransportApiProtos.GatewayAttributesMsg.newBuilder(); | ||
58 | + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); | ||
59 | + String deviceName1 = "Device A"; | ||
60 | + String deviceName2 = "Device B"; | ||
61 | + TransportApiProtos.AttributesMsg firstDeviceAttributesMsgProto = getDeviceAttributesMsgProto(deviceName1, expectedKeys); | ||
62 | + TransportApiProtos.AttributesMsg secondDeviceAttributesMsgProto = getDeviceAttributesMsgProto(deviceName2, expectedKeys); | ||
63 | + gatewayAttributesMsgProtoBuilder.addAllMsg(Arrays.asList(firstDeviceAttributesMsgProto, secondDeviceAttributesMsgProto)); | ||
64 | + TransportApiProtos.GatewayAttributesMsg gatewayAttributesMsg = gatewayAttributesMsgProtoBuilder.build(); | ||
65 | + processGatewayAttributesTest(expectedKeys, gatewayAttributesMsg.toByteArray(), deviceName1, deviceName2); | ||
66 | + } | ||
67 | + | ||
68 | + private TransportApiProtos.AttributesMsg getDeviceAttributesMsgProto(String deviceName, List<String> expectedKeys) { | ||
69 | + TransportApiProtos.AttributesMsg.Builder deviceAttributesMsgBuilder = TransportApiProtos.AttributesMsg.newBuilder(); | ||
70 | + TransportProtos.PostAttributeMsg msg = getPostAttributeMsg(expectedKeys); | ||
71 | + deviceAttributesMsgBuilder.setDeviceName(deviceName); | ||
72 | + deviceAttributesMsgBuilder.setMsg(msg); | ||
73 | + return deviceAttributesMsgBuilder.build(); | ||
74 | + } | ||
75 | +} |
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.mqtt.telemetry.attributes.nosql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoNoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesIntegrationTest; | ||
20 | + | ||
21 | +@DaoNoSqlTest | ||
22 | +public class MqttAttributesNoSqlIntegrationTest extends AbstractMqttAttributesIntegrationTest { | ||
23 | +} |
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.mqtt.telemetry.attributes.nosql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoNoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesIntegrationTest; | ||
20 | +import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesJsonIntegrationTest; | ||
21 | + | ||
22 | +@DaoNoSqlTest | ||
23 | +public class MqttAttributesNoSqlJsonIntegrationTest extends AbstractMqttAttributesJsonIntegrationTest { | ||
24 | +} |
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.mqtt.telemetry.attributes.nosql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoNoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesIntegrationTest; | ||
20 | +import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesProtoIntegrationTest; | ||
21 | + | ||
22 | +@DaoNoSqlTest | ||
23 | +public class MqttAttributesNoSqlProtoIntegrationTest extends AbstractMqttAttributesProtoIntegrationTest { | ||
24 | +} |
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.mqtt.telemetry.attributes.sql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesIntegrationTest; | ||
20 | + | ||
21 | +@DaoSqlTest | ||
22 | +public class MqttAttributesSqlIntegrationTest extends AbstractMqttAttributesIntegrationTest { | ||
23 | +} |
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.mqtt.telemetry.attributes.sql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesJsonIntegrationTest; | ||
20 | + | ||
21 | +@DaoSqlTest | ||
22 | +public class MqttAttributesSqlJsonIntegrationTest extends AbstractMqttAttributesJsonIntegrationTest { | ||
23 | +} |
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.mqtt.telemetry.attributes.sql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesJsonIntegrationTest; | ||
20 | +import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesProtoIntegrationTest; | ||
21 | + | ||
22 | +@DaoSqlTest | ||
23 | +public class MqttAttributesSqlProtoIntegrationTest extends AbstractMqttAttributesProtoIntegrationTest { | ||
24 | +} |
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.mqtt.telemetry.timeseries; | ||
17 | + | ||
18 | +import io.netty.handler.codec.mqtt.MqttQoS; | ||
19 | +import lombok.extern.slf4j.Slf4j; | ||
20 | +import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; | ||
21 | +import org.eclipse.paho.client.mqttv3.MqttAsyncClient; | ||
22 | +import org.eclipse.paho.client.mqttv3.MqttCallback; | ||
23 | +import org.eclipse.paho.client.mqttv3.MqttConnectOptions; | ||
24 | +import org.eclipse.paho.client.mqttv3.MqttMessage; | ||
25 | +import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; | ||
26 | +import org.junit.After; | ||
27 | +import org.junit.Before; | ||
28 | +import org.junit.Test; | ||
29 | +import org.thingsboard.server.common.data.Device; | ||
30 | +import org.thingsboard.server.common.data.TransportPayloadType; | ||
31 | +import org.thingsboard.server.common.data.device.profile.MqttTopics; | ||
32 | +import org.thingsboard.server.common.data.id.DeviceId; | ||
33 | +import org.thingsboard.server.mqtt.AbstractMqttIntegrationTest; | ||
34 | + | ||
35 | +import java.util.Arrays; | ||
36 | +import java.util.HashSet; | ||
37 | +import java.util.List; | ||
38 | +import java.util.Map; | ||
39 | +import java.util.Set; | ||
40 | +import java.util.concurrent.CountDownLatch; | ||
41 | +import java.util.concurrent.TimeUnit; | ||
42 | + | ||
43 | +import static org.junit.Assert.assertEquals; | ||
44 | +import static org.junit.Assert.assertNotNull; | ||
45 | +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; | ||
46 | + | ||
47 | +@Slf4j | ||
48 | +public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqttIntegrationTest { | ||
49 | + | ||
50 | + protected static final String PAYLOAD_VALUES_STR = "{\"key1\":\"value1\", \"key2\":true, \"key3\": 3.0, \"key4\": 4," + | ||
51 | + " \"key5\": {\"someNumber\": 42, \"someArray\": [1,2,3], \"someNestedObject\": {\"key\": \"value\"}}}"; | ||
52 | + | ||
53 | + @Before | ||
54 | + public void beforeTest() throws Exception { | ||
55 | + processBeforeTest("Test Post Telemetry device", "Test Post Telemetry gateway", null, null, null); | ||
56 | + } | ||
57 | + | ||
58 | + @After | ||
59 | + public void afterTest() throws Exception { | ||
60 | + processAfterTest(); | ||
61 | + } | ||
62 | + | ||
63 | + @Test | ||
64 | + public void testPushMqttTelemetry() throws Exception { | ||
65 | + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); | ||
66 | + processTelemetryTest(MqttTopics.DEVICE_TELEMETRY_TOPIC, expectedKeys, PAYLOAD_VALUES_STR.getBytes(), false); | ||
67 | + } | ||
68 | + | ||
69 | + @Test | ||
70 | + public void testPushMqttTelemetryWithTs() throws Exception { | ||
71 | + String payloadStr = "{\"ts\": 10000, \"values\": " + PAYLOAD_VALUES_STR + "}"; | ||
72 | + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); | ||
73 | + processTelemetryTest(MqttTopics.DEVICE_TELEMETRY_TOPIC, expectedKeys, payloadStr.getBytes(), true); | ||
74 | + } | ||
75 | + | ||
76 | + @Test | ||
77 | + public void testPushMqttTelemetryGateway() throws Exception { | ||
78 | + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); | ||
79 | + String deviceName1 = "Device A"; | ||
80 | + String deviceName2 = "Device B"; | ||
81 | + String payload = getGatewayTelemetryJsonPayload(deviceName1, deviceName2, "10000", "20000"); | ||
82 | + processGatewayTelemetryTest(MqttTopics.GATEWAY_TELEMETRY_TOPIC, expectedKeys, payload.getBytes(), deviceName1, deviceName2); | ||
83 | + } | ||
84 | + | ||
85 | + @Test | ||
86 | + public void testGatewayConnect() throws Exception { | ||
87 | + String payload = "{\"device\":\"Device A\"}"; | ||
88 | + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); | ||
89 | + publishMqttMsg(client, payload.getBytes(), MqttTopics.GATEWAY_CONNECT_TOPIC); | ||
90 | + | ||
91 | + String deviceName = "Device A"; | ||
92 | + | ||
93 | + Device device = doExecuteWithRetriesAndInterval(() -> doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class), | ||
94 | + 20, | ||
95 | + 100); | ||
96 | + | ||
97 | + assertNotNull(device); | ||
98 | + } | ||
99 | + | ||
100 | + protected void processTelemetryTest(String topic, List<String> expectedKeys, byte[] payload, boolean withTs) throws Exception { | ||
101 | + MqttAsyncClient client = getMqttAsyncClient(accessToken); | ||
102 | + publishMqttMsg(client, payload, topic); | ||
103 | + | ||
104 | + String deviceId = savedDevice.getId().getId().toString(); | ||
105 | + | ||
106 | + long start = System.currentTimeMillis(); | ||
107 | + long end = System.currentTimeMillis() + 5000; | ||
108 | + | ||
109 | + List<String> actualKeys = null; | ||
110 | + while (start <= end) { | ||
111 | + actualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + deviceId + "/keys/timeseries", List.class); | ||
112 | + if (actualKeys.size() == expectedKeys.size()) { | ||
113 | + break; | ||
114 | + } | ||
115 | + Thread.sleep(100); | ||
116 | + start += 100; | ||
117 | + } | ||
118 | + assertNotNull(actualKeys); | ||
119 | + | ||
120 | + Set<String> actualKeySet = new HashSet<>(actualKeys); | ||
121 | + Set<String> expectedKeySet = new HashSet<>(expectedKeys); | ||
122 | + | ||
123 | + assertEquals(expectedKeySet, actualKeySet); | ||
124 | + | ||
125 | + String getTelemetryValuesUrl; | ||
126 | + if (withTs) { | ||
127 | + getTelemetryValuesUrl = "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/timeseries?startTs=0&endTs=15000&keys=" + String.join(",", actualKeySet); | ||
128 | + } else { | ||
129 | + getTelemetryValuesUrl = "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/timeseries?keys=" + String.join(",", actualKeySet); | ||
130 | + } | ||
131 | + Map<String, List<Map<String, String>>> values = doGetAsync(getTelemetryValuesUrl, Map.class); | ||
132 | + | ||
133 | + if (withTs) { | ||
134 | + assertTs(values, expectedKeys, 10000, 0); | ||
135 | + } | ||
136 | + assertValues(values, 0); | ||
137 | + } | ||
138 | + | ||
139 | + protected void processGatewayTelemetryTest(String topic, List<String> expectedKeys, byte[] payload, String firstDeviceName, String secondDeviceName) throws Exception { | ||
140 | + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); | ||
141 | + | ||
142 | + publishMqttMsg(client, payload, topic); | ||
143 | + | ||
144 | + Device firstDevice = doExecuteWithRetriesAndInterval(() -> doGet("/api/tenant/devices?deviceName=" + firstDeviceName, Device.class), | ||
145 | + 20, | ||
146 | + 100); | ||
147 | + | ||
148 | + assertNotNull(firstDevice); | ||
149 | + | ||
150 | + Device secondDevice = doExecuteWithRetriesAndInterval(() -> doGet("/api/tenant/devices?deviceName=" + secondDeviceName, Device.class), | ||
151 | + 20, | ||
152 | + 100); | ||
153 | + | ||
154 | + assertNotNull(secondDevice); | ||
155 | + | ||
156 | + Thread.sleep(2000); | ||
157 | + | ||
158 | + List<String> firstDeviceActualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + firstDevice.getId() + "/keys/timeseries", List.class); | ||
159 | + Set<String> firstDeviceActualKeySet = new HashSet<>(firstDeviceActualKeys); | ||
160 | + | ||
161 | + List<String> secondDeviceActualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + secondDevice.getId() + "/keys/timeseries", List.class); | ||
162 | + Set<String> secondDeviceActualKeySet = new HashSet<>(secondDeviceActualKeys); | ||
163 | + | ||
164 | + Set<String> expectedKeySet = new HashSet<>(expectedKeys); | ||
165 | + | ||
166 | + assertEquals(expectedKeySet, firstDeviceActualKeySet); | ||
167 | + assertEquals(expectedKeySet, secondDeviceActualKeySet); | ||
168 | + | ||
169 | + String getTelemetryValuesUrlFirstDevice = getTelemetryValuesUrl(firstDevice.getId(), firstDeviceActualKeySet); | ||
170 | + String getTelemetryValuesUrlSecondDevice = getTelemetryValuesUrl(firstDevice.getId(), secondDeviceActualKeySet); | ||
171 | + | ||
172 | + Map<String, List<Map<String, String>>> firstDeviceValues = doGetAsync(getTelemetryValuesUrlFirstDevice, Map.class); | ||
173 | + Map<String, List<Map<String, String>>> secondDeviceValues = doGetAsync(getTelemetryValuesUrlSecondDevice, Map.class); | ||
174 | + | ||
175 | + assertGatewayDeviceData(firstDeviceValues, expectedKeys); | ||
176 | + assertGatewayDeviceData(secondDeviceValues, expectedKeys); | ||
177 | + } | ||
178 | + | ||
179 | + protected String getGatewayTelemetryJsonPayload(String deviceA, String deviceB, String firstTsValue, String secondTsValue) { | ||
180 | + String payload = "[{\"ts\": " + firstTsValue + ", \"values\": " + PAYLOAD_VALUES_STR + "}, " + | ||
181 | + "{\"ts\": " + secondTsValue + ", \"values\": " + PAYLOAD_VALUES_STR + "}]"; | ||
182 | + return "{\"" + deviceA + "\": " + payload + ", \"" + deviceB + "\": " + payload + "}"; | ||
183 | + } | ||
184 | + | ||
185 | + private String getTelemetryValuesUrl(DeviceId deviceId, Set<String> actualKeySet) { | ||
186 | + return "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/timeseries?startTs=0&endTs=25000&keys=" + String.join(",", actualKeySet); | ||
187 | + } | ||
188 | + | ||
189 | + private void assertGatewayDeviceData(Map<String, List<Map<String, String>>> deviceValues, List<String> expectedKeys) { | ||
190 | + | ||
191 | + assertEquals(2, deviceValues.get(expectedKeys.get(0)).size()); | ||
192 | + assertEquals(2, deviceValues.get(expectedKeys.get(1)).size()); | ||
193 | + assertEquals(2, deviceValues.get(expectedKeys.get(2)).size()); | ||
194 | + assertEquals(2, deviceValues.get(expectedKeys.get(3)).size()); | ||
195 | + assertEquals(2, deviceValues.get(expectedKeys.get(4)).size()); | ||
196 | + | ||
197 | + assertTs(deviceValues, expectedKeys, 20000, 0); | ||
198 | + assertTs(deviceValues, expectedKeys, 10000, 1); | ||
199 | + | ||
200 | + assertValues(deviceValues, 0); | ||
201 | + assertValues(deviceValues, 1); | ||
202 | + | ||
203 | + } | ||
204 | + | ||
205 | + private void assertValues(Map<String, List<Map<String, String>>> deviceValues, int arrayIndex) { | ||
206 | + for (Map.Entry<String, List<Map<String, String>>> entry : deviceValues.entrySet()) { | ||
207 | + String key = entry.getKey(); | ||
208 | + List<Map<String, String>> tsKv = entry.getValue(); | ||
209 | + String value = tsKv.get(arrayIndex).get("value"); | ||
210 | + switch (key) { | ||
211 | + case "key1": | ||
212 | + assertEquals("value1", value); | ||
213 | + break; | ||
214 | + case "key2": | ||
215 | + assertEquals("true", value); | ||
216 | + break; | ||
217 | + case "key3": | ||
218 | + assertEquals("3.0", value); | ||
219 | + break; | ||
220 | + case "key4": | ||
221 | + assertEquals("4", value); | ||
222 | + break; | ||
223 | + case "key5": | ||
224 | + assertEquals("{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}", value); | ||
225 | + break; | ||
226 | + } | ||
227 | + } | ||
228 | + } | ||
229 | + | ||
230 | + private void assertTs(Map<String, List<Map<String, String>>> deviceValues, List<String> expectedKeys, int ts, int arrayIndex) { | ||
231 | + assertEquals(ts, deviceValues.get(expectedKeys.get(0)).get(arrayIndex).get("ts")); | ||
232 | + assertEquals(ts, deviceValues.get(expectedKeys.get(1)).get(arrayIndex).get("ts")); | ||
233 | + assertEquals(ts, deviceValues.get(expectedKeys.get(2)).get(arrayIndex).get("ts")); | ||
234 | + assertEquals(ts, deviceValues.get(expectedKeys.get(3)).get(arrayIndex).get("ts")); | ||
235 | + assertEquals(ts, deviceValues.get(expectedKeys.get(4)).get(arrayIndex).get("ts")); | ||
236 | + } | ||
237 | + | ||
238 | + // @Test - Unstable | ||
239 | + public void testMqttQoSLevel() throws Exception { | ||
240 | + String clientId = MqttAsyncClient.generateClientId(); | ||
241 | + MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId, new MemoryPersistence()); | ||
242 | + | ||
243 | + MqttConnectOptions options = new MqttConnectOptions(); | ||
244 | + options.setUserName(accessToken); | ||
245 | + CountDownLatch latch = new CountDownLatch(1); | ||
246 | + TestMqttCallback callback = new TestMqttCallback(client, latch); | ||
247 | + client.setCallback(callback); | ||
248 | + client.connect(options).waitForCompletion(5000); | ||
249 | + client.subscribe("v1/devices/me/attributes", MqttQoS.AT_MOST_ONCE.value()); | ||
250 | + String payload = "{\"key\":\"uniqueValue\"}"; | ||
251 | +// TODO 3.1: we need to acknowledge subscription only after it is processed by device actor and not when the message is pushed to queue. | ||
252 | +// MqttClient -> SUB REQUEST -> Transport -> Kafka -> Device Actor (subscribed) | ||
253 | +// MqttClient <- SUB_ACK <- Transport | ||
254 | + Thread.sleep(5000); | ||
255 | + doPostAsync("/api/plugins/telemetry/" + savedDevice.getId() + "/SHARED_SCOPE", payload, String.class, status().isOk()); | ||
256 | + latch.await(10, TimeUnit.SECONDS); | ||
257 | + assertEquals(payload, callback.getPayload()); | ||
258 | + assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS()); | ||
259 | + } | ||
260 | + | ||
261 | + private static class TestMqttCallback implements MqttCallback { | ||
262 | + | ||
263 | + private final MqttAsyncClient client; | ||
264 | + private final CountDownLatch latch; | ||
265 | + private volatile Integer qoS; | ||
266 | + private volatile String payload; | ||
267 | + | ||
268 | + String getPayload() { | ||
269 | + return payload; | ||
270 | + } | ||
271 | + | ||
272 | + TestMqttCallback(MqttAsyncClient client, CountDownLatch latch) { | ||
273 | + this.client = client; | ||
274 | + this.latch = latch; | ||
275 | + } | ||
276 | + | ||
277 | + int getQoS() { | ||
278 | + return qoS; | ||
279 | + } | ||
280 | + | ||
281 | + @Override | ||
282 | + public void connectionLost(Throwable throwable) { | ||
283 | + log.error("Client connection lost", throwable); | ||
284 | + } | ||
285 | + | ||
286 | + @Override | ||
287 | + public void messageArrived(String requestTopic, MqttMessage mqttMessage) { | ||
288 | + payload = new String(mqttMessage.getPayload()); | ||
289 | + qoS = mqttMessage.getQos(); | ||
290 | + latch.countDown(); | ||
291 | + } | ||
292 | + | ||
293 | + @Override | ||
294 | + public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) { | ||
295 | + | ||
296 | + } | ||
297 | + } | ||
298 | + | ||
299 | + | ||
300 | +} |
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.mqtt.telemetry.timeseries; | ||
17 | + | ||
18 | +import lombok.extern.slf4j.Slf4j; | ||
19 | +import org.eclipse.paho.client.mqttv3.MqttAsyncClient; | ||
20 | +import org.junit.After; | ||
21 | +import org.junit.Before; | ||
22 | +import org.junit.Test; | ||
23 | +import org.thingsboard.server.common.data.Device; | ||
24 | +import org.thingsboard.server.common.data.TransportPayloadType; | ||
25 | +import org.thingsboard.server.common.data.device.profile.MqttTopics; | ||
26 | + | ||
27 | +import java.util.Arrays; | ||
28 | +import java.util.List; | ||
29 | + | ||
30 | +import static org.junit.Assert.assertEquals; | ||
31 | +import static org.junit.Assert.assertNotNull; | ||
32 | + | ||
33 | +@Slf4j | ||
34 | +public abstract class AbstractMqttTimeseriesJsonIntegrationTest extends AbstractMqttTimeseriesIntegrationTest { | ||
35 | + | ||
36 | + private static final String POST_DATA_TELEMETRY_TOPIC = "data/telemetry"; | ||
37 | + | ||
38 | + @Before | ||
39 | + public void beforeTest() throws Exception { | ||
40 | + processBeforeTest("Test Post Telemetry device json payload", "Test Post Telemetry gateway json payload", TransportPayloadType.JSON, POST_DATA_TELEMETRY_TOPIC, null); | ||
41 | + } | ||
42 | + | ||
43 | + @After | ||
44 | + public void afterTest() throws Exception { | ||
45 | + processAfterTest(); | ||
46 | + } | ||
47 | + | ||
48 | + @Test | ||
49 | + public void testPushMqttTelemetry() throws Exception { | ||
50 | + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); | ||
51 | + processTelemetryTest(POST_DATA_TELEMETRY_TOPIC, expectedKeys, PAYLOAD_VALUES_STR.getBytes(), false); | ||
52 | + } | ||
53 | + | ||
54 | + @Test | ||
55 | + public void testPushMqttTelemetryWithTs() throws Exception { | ||
56 | + String payloadStr = "{\"ts\": 10000, \"values\": " + PAYLOAD_VALUES_STR + "}"; | ||
57 | + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); | ||
58 | + processTelemetryTest(POST_DATA_TELEMETRY_TOPIC, expectedKeys, payloadStr.getBytes(), true); | ||
59 | + } | ||
60 | + | ||
61 | + @Test | ||
62 | + public void testPushMqttTelemetryGateway() throws Exception { | ||
63 | + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); | ||
64 | + String deviceName1 = "Device A"; | ||
65 | + String deviceName2 = "Device B"; | ||
66 | + String payload = getGatewayTelemetryJsonPayload(deviceName1, deviceName2, "10000", "20000"); | ||
67 | + processGatewayTelemetryTest(MqttTopics.GATEWAY_TELEMETRY_TOPIC, expectedKeys, payload.getBytes(), deviceName1, deviceName2); | ||
68 | + } | ||
69 | + | ||
70 | + @Test | ||
71 | + public void testGatewayConnect() throws Exception { | ||
72 | + String payload = "{\"device\":\"Device A\", \"type\": \"" + TransportPayloadType.JSON.name() + "\"}"; | ||
73 | + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); | ||
74 | + publishMqttMsg(client, payload.getBytes(), MqttTopics.GATEWAY_CONNECT_TOPIC); | ||
75 | + | ||
76 | + String deviceName = "Device A"; | ||
77 | + Device device = doExecuteWithRetriesAndInterval(() -> doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class), | ||
78 | + 20, | ||
79 | + 100); | ||
80 | + assertNotNull(device); | ||
81 | + } | ||
82 | +} |
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.mqtt.telemetry.timeseries; | ||
17 | + | ||
18 | +import lombok.extern.slf4j.Slf4j; | ||
19 | +import org.eclipse.paho.client.mqttv3.MqttAsyncClient; | ||
20 | +import org.junit.After; | ||
21 | +import org.junit.Before; | ||
22 | +import org.junit.Test; | ||
23 | +import org.thingsboard.server.common.data.Device; | ||
24 | +import org.thingsboard.server.common.data.TransportPayloadType; | ||
25 | +import org.thingsboard.server.common.data.device.profile.MqttTopics; | ||
26 | +import org.thingsboard.server.gen.transport.TransportApiProtos; | ||
27 | +import org.thingsboard.server.gen.transport.TransportProtos; | ||
28 | + | ||
29 | +import java.util.Arrays; | ||
30 | +import java.util.List; | ||
31 | + | ||
32 | +import static org.junit.Assert.assertEquals; | ||
33 | +import static org.junit.Assert.assertNotNull; | ||
34 | + | ||
35 | +@Slf4j | ||
36 | +public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends AbstractMqttTimeseriesIntegrationTest { | ||
37 | + | ||
38 | + private static final String POST_DATA_TELEMETRY_TOPIC = "proto/telemetry"; | ||
39 | + | ||
40 | + @Before | ||
41 | + public void beforeTest() throws Exception { | ||
42 | + processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, POST_DATA_TELEMETRY_TOPIC, null); | ||
43 | + } | ||
44 | + | ||
45 | + @After | ||
46 | + public void afterTest() throws Exception { | ||
47 | + processAfterTest(); | ||
48 | + } | ||
49 | + | ||
50 | + @Test | ||
51 | + public void testPushMqttTelemetry() throws Exception { | ||
52 | + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); | ||
53 | + TransportProtos.TsKvListProto tsKvListProto = getTsKvListProto(expectedKeys, 0); | ||
54 | + processTelemetryTest(POST_DATA_TELEMETRY_TOPIC, expectedKeys, tsKvListProto.toByteArray(), false); | ||
55 | + } | ||
56 | + | ||
57 | + @Test | ||
58 | + public void testPushMqttTelemetryWithTs() throws Exception { | ||
59 | + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); | ||
60 | + TransportProtos.TsKvListProto tsKvListProto = getTsKvListProto(expectedKeys, 10000); | ||
61 | + processTelemetryTest(POST_DATA_TELEMETRY_TOPIC, expectedKeys, tsKvListProto.toByteArray(), true); | ||
62 | + } | ||
63 | + | ||
64 | + @Test | ||
65 | + public void testPushMqttTelemetryGateway() throws Exception { | ||
66 | + TransportApiProtos.GatewayTelemetryMsg.Builder gatewayTelemetryMsgProtoBuilder = TransportApiProtos.GatewayTelemetryMsg.newBuilder(); | ||
67 | + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); | ||
68 | + String deviceName1 = "Device A"; | ||
69 | + String deviceName2 = "Device B"; | ||
70 | + TransportApiProtos.TelemetryMsg deviceATelemetryMsgProto = getDeviceTelemetryMsgProto(deviceName1, expectedKeys, 10000, 20000); | ||
71 | + TransportApiProtos.TelemetryMsg deviceBTelemetryMsgProto = getDeviceTelemetryMsgProto(deviceName2, expectedKeys, 10000, 20000); | ||
72 | + gatewayTelemetryMsgProtoBuilder.addAllMsg(Arrays.asList(deviceATelemetryMsgProto, deviceBTelemetryMsgProto)); | ||
73 | + TransportApiProtos.GatewayTelemetryMsg gatewayTelemetryMsg = gatewayTelemetryMsgProtoBuilder.build(); | ||
74 | + processGatewayTelemetryTest(MqttTopics.GATEWAY_TELEMETRY_TOPIC, expectedKeys, gatewayTelemetryMsg.toByteArray(), deviceName1, deviceName2); | ||
75 | + } | ||
76 | + | ||
77 | + @Test | ||
78 | + public void testGatewayConnect() throws Exception { | ||
79 | + String deviceName = "Device A"; | ||
80 | + TransportApiProtos.ConnectMsg connectMsgProto = getConnectProto(deviceName); | ||
81 | + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); | ||
82 | + publishMqttMsg(client, connectMsgProto.toByteArray(), MqttTopics.GATEWAY_CONNECT_TOPIC); | ||
83 | + | ||
84 | + Device device = doExecuteWithRetriesAndInterval(() -> doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class), | ||
85 | + 20, | ||
86 | + 100); | ||
87 | + | ||
88 | + assertNotNull(device); | ||
89 | + } | ||
90 | + | ||
91 | + private TransportApiProtos.ConnectMsg getConnectProto(String deviceName) { | ||
92 | + TransportApiProtos.ConnectMsg.Builder builder = TransportApiProtos.ConnectMsg.newBuilder(); | ||
93 | + builder.setDeviceName(deviceName); | ||
94 | + builder.setDeviceType(TransportPayloadType.PROTOBUF.name()); | ||
95 | + return builder.build(); | ||
96 | + } | ||
97 | + | ||
98 | + private TransportApiProtos.TelemetryMsg getDeviceTelemetryMsgProto(String deviceName, List<String> expectedKeys, long firstTs, long secondTs) { | ||
99 | + TransportApiProtos.TelemetryMsg.Builder deviceTelemetryMsgBuilder = TransportApiProtos.TelemetryMsg.newBuilder(); | ||
100 | + TransportProtos.TsKvListProto tsKvListProto1 = getTsKvListProto(expectedKeys, firstTs); | ||
101 | + TransportProtos.TsKvListProto tsKvListProto2 = getTsKvListProto(expectedKeys, secondTs); | ||
102 | + TransportProtos.PostTelemetryMsg.Builder msg = TransportProtos.PostTelemetryMsg.newBuilder(); | ||
103 | + msg.addAllTsKvList(Arrays.asList(tsKvListProto1, tsKvListProto2)); | ||
104 | + deviceTelemetryMsgBuilder.setDeviceName(deviceName); | ||
105 | + deviceTelemetryMsgBuilder.setMsg(msg); | ||
106 | + return deviceTelemetryMsgBuilder.build(); | ||
107 | + } | ||
108 | + | ||
109 | + private TransportProtos.TsKvListProto getTsKvListProto(List<String> expectedKeys, long ts) { | ||
110 | + List<TransportProtos.KeyValueProto> kvProtos = getKvProtos(expectedKeys); | ||
111 | + TransportProtos.TsKvListProto.Builder builder = TransportProtos.TsKvListProto.newBuilder(); | ||
112 | + builder.addAllKv(kvProtos); | ||
113 | + builder.setTs(ts); | ||
114 | + return builder.build(); | ||
115 | + } | ||
116 | +} |
application/src/test/java/org/thingsboard/server/mqtt/telemetry/timeseries/nosql/MqttTimeseriesNoSqlIntegrationTest.java
renamed from
application/src/test/java/org/thingsboard/server/mqtt/telemetry/nosql/MqttTelemetryNoSqlIntegrationTest.java
@@ -13,14 +13,14 @@ | @@ -13,14 +13,14 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | -package org.thingsboard.server.mqtt.telemetry.nosql; | 16 | +package org.thingsboard.server.mqtt.telemetry.timeseries.nosql; |
17 | 17 | ||
18 | import org.thingsboard.server.dao.service.DaoNoSqlTest; | 18 | import org.thingsboard.server.dao.service.DaoNoSqlTest; |
19 | -import org.thingsboard.server.mqtt.telemetry.AbstractMqttTelemetryIntegrationTest; | 19 | +import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesIntegrationTest; |
20 | 20 | ||
21 | /** | 21 | /** |
22 | * Created by Valerii Sosliuk on 8/22/2017. | 22 | * Created by Valerii Sosliuk on 8/22/2017. |
23 | */ | 23 | */ |
24 | @DaoNoSqlTest | 24 | @DaoNoSqlTest |
25 | -public class MqttTelemetryNoSqlIntegrationTest extends AbstractMqttTelemetryIntegrationTest { | 25 | +public class MqttTimeseriesNoSqlIntegrationTest extends AbstractMqttTimeseriesIntegrationTest { |
26 | } | 26 | } |
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.mqtt.telemetry.timeseries.nosql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoNoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesJsonIntegrationTest; | ||
20 | + | ||
21 | +@DaoNoSqlTest | ||
22 | +public class MqttTimeseriesNoSqlJsonIntegrationTest extends AbstractMqttTimeseriesJsonIntegrationTest { | ||
23 | +} |
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.mqtt.telemetry.timeseries.nosql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoNoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesProtoIntegrationTest; | ||
20 | + | ||
21 | +@DaoNoSqlTest | ||
22 | +public class MqttTimeseriesNoSqlProtoIntegrationTest extends AbstractMqttTimeseriesProtoIntegrationTest { | ||
23 | +} |
application/src/test/java/org/thingsboard/server/mqtt/telemetry/timeseries/sql/MqttTimeseriesSqlIntegrationTest.java
renamed from
application/src/test/java/org/thingsboard/server/mqtt/telemetry/sql/MqttTelemetrySqlIntegrationTest.java
@@ -13,15 +13,14 @@ | @@ -13,15 +13,14 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | -package org.thingsboard.server.mqtt.telemetry.sql; | 16 | +package org.thingsboard.server.mqtt.telemetry.timeseries.sql; |
17 | 17 | ||
18 | -import org.thingsboard.server.dao.service.DaoNoSqlTest; | ||
19 | import org.thingsboard.server.dao.service.DaoSqlTest; | 18 | import org.thingsboard.server.dao.service.DaoSqlTest; |
20 | -import org.thingsboard.server.mqtt.telemetry.AbstractMqttTelemetryIntegrationTest; | 19 | +import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesIntegrationTest; |
21 | 20 | ||
22 | /** | 21 | /** |
23 | * Created by Valerii Sosliuk on 8/22/2017. | 22 | * Created by Valerii Sosliuk on 8/22/2017. |
24 | */ | 23 | */ |
25 | @DaoSqlTest | 24 | @DaoSqlTest |
26 | -public class MqttTelemetrySqlIntegrationTest extends AbstractMqttTelemetryIntegrationTest { | 25 | +public class MqttTimeseriesSqlIntegrationTest extends AbstractMqttTimeseriesIntegrationTest { |
27 | } | 26 | } |
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.mqtt.telemetry.timeseries.sql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesIntegrationTest; | ||
20 | +import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesJsonIntegrationTest; | ||
21 | + | ||
22 | +/** | ||
23 | + * Created by Valerii Sosliuk on 8/22/2017. | ||
24 | + */ | ||
25 | +@DaoSqlTest | ||
26 | +public class MqttTimeseriesSqlJsonIntegrationTest extends AbstractMqttTimeseriesJsonIntegrationTest { | ||
27 | +} |
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.mqtt.telemetry.timeseries.sql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesJsonIntegrationTest; | ||
20 | +import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesProtoIntegrationTest; | ||
21 | + | ||
22 | +/** | ||
23 | + * Created by Valerii Sosliuk on 8/22/2017. | ||
24 | + */ | ||
25 | +@DaoSqlTest | ||
26 | +public class MqttTimeseriesSqlProtoIntegrationTest extends AbstractMqttTimeseriesProtoIntegrationTest { | ||
27 | +} |
@@ -32,7 +32,7 @@ public class RuleEngineSqlTestSuite { | @@ -32,7 +32,7 @@ public class RuleEngineSqlTestSuite { | ||
32 | 32 | ||
33 | @ClassRule | 33 | @ClassRule |
34 | public static CustomSqlUnit sqlUnit = new CustomSqlUnit( | 34 | public static CustomSqlUnit sqlUnit = new CustomSqlUnit( |
35 | - Arrays.asList("sql/schema-ts-hsql.sql", "sql/schema-entities-hsql.sql", "sql/system-data.sql"), | 35 | + Arrays.asList("sql/schema-types-hsql.sql", "sql/schema-ts-hsql.sql", "sql/schema-entities-hsql.sql", "sql/system-data.sql"), |
36 | "sql/hsql/drop-all-tables.sql", | 36 | "sql/hsql/drop-all-tables.sql", |
37 | "sql-test.properties"); | 37 | "sql-test.properties"); |
38 | 38 |
@@ -16,7 +16,6 @@ | @@ -16,7 +16,6 @@ | ||
16 | package org.thingsboard.server.rules.flow.sql; | 16 | package org.thingsboard.server.rules.flow.sql; |
17 | 17 | ||
18 | import org.thingsboard.server.dao.service.DaoSqlTest; | 18 | import org.thingsboard.server.dao.service.DaoSqlTest; |
19 | -import org.thingsboard.server.mqtt.rpc.AbstractMqttServerSideRpcIntegrationTest; | ||
20 | import org.thingsboard.server.rules.flow.AbstractRuleEngineFlowIntegrationTest; | 19 | import org.thingsboard.server.rules.flow.AbstractRuleEngineFlowIntegrationTest; |
21 | 20 | ||
22 | /** | 21 | /** |
@@ -33,7 +33,7 @@ public class SystemSqlTestSuite { | @@ -33,7 +33,7 @@ public class SystemSqlTestSuite { | ||
33 | 33 | ||
34 | @ClassRule | 34 | @ClassRule |
35 | public static CustomSqlUnit sqlUnit = new CustomSqlUnit( | 35 | public static CustomSqlUnit sqlUnit = new CustomSqlUnit( |
36 | - Arrays.asList("sql/schema-ts-hsql.sql", "sql/schema-entities-hsql.sql", "sql/system-data.sql"), | 36 | + Arrays.asList("sql/schema-types-hsql.sql", "sql/schema-ts-hsql.sql", "sql/schema-entities-hsql.sql", "sql/system-data.sql"), |
37 | "sql/hsql/drop-all-tables.sql", | 37 | "sql/hsql/drop-all-tables.sql", |
38 | "sql-test.properties"); | 38 | "sql-test.properties"); |
39 | 39 |
@@ -16,6 +16,7 @@ | @@ -16,6 +16,7 @@ | ||
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.exception.ThingsboardException; | ||
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; |
@@ -23,6 +24,8 @@ import org.thingsboard.server.common.data.page.PageData; | @@ -23,6 +24,8 @@ 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; |
24 | import org.thingsboard.server.common.data.relation.EntityRelation; | 25 | import org.thingsboard.server.common.data.relation.EntityRelation; |
25 | import org.thingsboard.server.common.data.rule.RuleChain; | 26 | import org.thingsboard.server.common.data.rule.RuleChain; |
27 | +import org.thingsboard.server.common.data.rule.RuleChainData; | ||
28 | +import org.thingsboard.server.common.data.rule.RuleChainImportResult; | ||
26 | import org.thingsboard.server.common.data.rule.RuleChainMetaData; | 29 | import org.thingsboard.server.common.data.rule.RuleChainMetaData; |
27 | import org.thingsboard.server.common.data.rule.RuleNode; | 30 | import org.thingsboard.server.common.data.rule.RuleNode; |
28 | 31 | ||
@@ -63,4 +66,8 @@ public interface RuleChainService { | @@ -63,4 +66,8 @@ public interface RuleChainService { | ||
63 | 66 | ||
64 | void deleteRuleChainsByTenantId(TenantId tenantId); | 67 | void deleteRuleChainsByTenantId(TenantId tenantId); |
65 | 68 | ||
69 | + RuleChainData exportTenantRuleChains(TenantId tenantId, PageLink pageLink) throws ThingsboardException; | ||
70 | + | ||
71 | + List<RuleChainImportResult> importTenantRuleChains(TenantId tenantId, RuleChainData ruleChainData, boolean overwrite); | ||
72 | + | ||
66 | } | 73 | } |
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.rule; | ||
17 | + | ||
18 | +import org.thingsboard.server.common.data.id.EntityId; | ||
19 | +import org.thingsboard.server.common.data.id.RuleNodeId; | ||
20 | +import org.thingsboard.server.common.data.id.TenantId; | ||
21 | +import org.thingsboard.server.common.data.page.PageData; | ||
22 | +import org.thingsboard.server.common.data.page.PageLink; | ||
23 | +import org.thingsboard.server.common.data.rule.RuleNodeState; | ||
24 | + | ||
25 | +public interface RuleNodeStateService { | ||
26 | + | ||
27 | + PageData<RuleNodeState> findByRuleNodeId(TenantId tenantId, RuleNodeId ruleNodeId, PageLink pageLink); | ||
28 | + | ||
29 | + RuleNodeState findByRuleNodeIdAndEntityId(TenantId tenantId, RuleNodeId ruleNodeId, EntityId entityId); | ||
30 | + | ||
31 | + RuleNodeState save(TenantId tenantId, RuleNodeState ruleNodeState); | ||
32 | + | ||
33 | +} |
@@ -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, TENANT_PROFILE, CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, DEVICE_PROFILE, 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, TENANT_PROFILE, DEVICE_PROFILE |
23 | } | 23 | } |
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 | +public enum TransportPayloadType { | ||
19 | + JSON, | ||
20 | + PROTOBUF | ||
21 | +} |
@@ -15,6 +15,7 @@ | @@ -15,6 +15,7 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.common.data.device.profile; | 16 | package org.thingsboard.server.common.data.device.profile; |
17 | 17 | ||
18 | +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; | ||
18 | import lombok.Data; | 19 | import lombok.Data; |
19 | import org.thingsboard.server.common.data.query.KeyFilter; | 20 | import org.thingsboard.server.common.data.query.KeyFilter; |
20 | 21 | ||
@@ -22,10 +23,10 @@ import java.util.List; | @@ -22,10 +23,10 @@ import java.util.List; | ||
22 | import java.util.concurrent.TimeUnit; | 23 | import java.util.concurrent.TimeUnit; |
23 | 24 | ||
24 | @Data | 25 | @Data |
26 | +@JsonIgnoreProperties(ignoreUnknown = true) | ||
25 | public class AlarmCondition { | 27 | public class AlarmCondition { |
26 | 28 | ||
27 | private List<KeyFilter> condition; | 29 | private List<KeyFilter> condition; |
28 | - private TimeUnit durationUnit; | ||
29 | - private long durationValue; | 30 | + private AlarmConditionSpec spec; |
30 | 31 | ||
31 | } | 32 | } |
common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionSpec.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.device.profile; | ||
17 | + | ||
18 | +import com.fasterxml.jackson.annotation.JsonIgnore; | ||
19 | +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; | ||
20 | +import com.fasterxml.jackson.annotation.JsonSubTypes; | ||
21 | +import com.fasterxml.jackson.annotation.JsonTypeInfo; | ||
22 | + | ||
23 | +@JsonIgnoreProperties(ignoreUnknown = true) | ||
24 | +@JsonTypeInfo( | ||
25 | + use = JsonTypeInfo.Id.NAME, | ||
26 | + include = JsonTypeInfo.As.PROPERTY, | ||
27 | + property = "type") | ||
28 | +@JsonSubTypes({ | ||
29 | + @JsonSubTypes.Type(value = SimpleAlarmConditionSpec.class, name = "SIMPLE"), | ||
30 | + @JsonSubTypes.Type(value = DurationAlarmConditionSpec.class, name = "DURATION"), | ||
31 | + @JsonSubTypes.Type(value = RepeatingAlarmConditionSpec.class, name = "REPEATING")}) | ||
32 | +public interface AlarmConditionSpec { | ||
33 | + | ||
34 | + @JsonIgnore | ||
35 | + AlarmConditionSpecType getType(); | ||
36 | + | ||
37 | +} |
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.device.profile; | ||
17 | + | ||
18 | +public enum AlarmConditionSpecType { | ||
19 | + | ||
20 | + SIMPLE, | ||
21 | + DURATION, | ||
22 | + REPEATING | ||
23 | + | ||
24 | +} |
@@ -21,6 +21,7 @@ import lombok.Data; | @@ -21,6 +21,7 @@ import lombok.Data; | ||
21 | public class AlarmRule { | 21 | public class AlarmRule { |
22 | 22 | ||
23 | private AlarmCondition condition; | 23 | private AlarmCondition condition; |
24 | + private AlarmSchedule schedule; | ||
24 | // Advanced | 25 | // Advanced |
25 | private String alarmDetails; | 26 | private String alarmDetails; |
26 | 27 |