Commit 8c9dda1b4d16b17311e1e91d4d9f0c92edd60e62

Authored by zbeacon
2 parents 1bb1f5be 9fef6b02

Added ability to provision device using protobuf

Showing 72 changed files with 3765 additions and 455 deletions

Too many changes to show.

To preserve performance only 72 of 108 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 {
@@ -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 {
@@ -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 }
@@ -237,6 +237,8 @@ public class DefaultTransportApiService implements TransportApiService { @@ -237,6 +237,8 @@ public class DefaultTransportApiService implements TransportApiService {
237 device.setName(requestMsg.getDeviceName()); 237 device.setName(requestMsg.getDeviceName());
238 device.setType(requestMsg.getDeviceType()); 238 device.setType(requestMsg.getDeviceType());
239 device.setCustomerId(gateway.getCustomerId()); 239 device.setCustomerId(gateway.getCustomerId());
  240 + DeviceProfile deviceProfile = deviceProfileService.findOrCreateDeviceProfile(gateway.getTenantId(), requestMsg.getDeviceType());
  241 + device.setDeviceProfileId(deviceProfile.getId());
240 device = deviceService.saveDevice(device); 242 device = deviceService.saveDevice(device);
241 relationService.saveRelationAsync(TenantId.SYS_TENANT_ID, new EntityRelation(gateway.getId(), device.getId(), "Created")); 243 relationService.saveRelationAsync(TenantId.SYS_TENANT_ID, new EntityRelation(gateway.getId(), device.getId(), "Created"));
242 deviceStateService.onDeviceAdded(device); 244 deviceStateService.onDeviceAdded(device);
@@ -227,6 +227,10 @@ public abstract class AbstractWebTest { @@ -227,6 +227,10 @@ public abstract class AbstractWebTest {
227 login(CUSTOMER_USER_EMAIL, CUSTOMER_USER_PASSWORD); 227 login(CUSTOMER_USER_EMAIL, CUSTOMER_USER_PASSWORD);
228 } 228 }
229 229
  230 + protected void loginUser(String userName, String password) throws Exception {
  231 + login(userName, password);
  232 + }
  233 +
230 private Tenant savedDifferentTenant; 234 private Tenant savedDifferentTenant;
231 235
232 protected void loginDifferentTenant() throws Exception { 236 protected void loginDifferentTenant() throws Exception {
@@ -252,15 +256,27 @@ public abstract class AbstractWebTest { @@ -252,15 +256,27 @@ public abstract class AbstractWebTest {
252 protected User createUserAndLogin(User user, String password) throws Exception { 256 protected User createUserAndLogin(User user, String password) throws Exception {
253 User savedUser = doPost("/api/user", user, User.class); 257 User savedUser = doPost("/api/user", user, User.class);
254 logout(); 258 logout();
  259 + JsonNode activateRequest = getActivateRequest(password);
  260 + JsonNode tokenInfo = readResponse(doPost("/api/noauth/activate", activateRequest).andExpect(status().isOk()), JsonNode.class);
  261 + validateAndSetJwtToken(tokenInfo, user.getEmail());
  262 + return savedUser;
  263 + }
  264 +
  265 + protected User createUser(User user, String password) throws Exception {
  266 + User savedUser = doPost("/api/user", user, User.class);
  267 + JsonNode activateRequest = getActivateRequest(password);
  268 + ResultActions resultActions = doPost("/api/noauth/activate", activateRequest);
  269 + resultActions.andExpect(status().isOk());
  270 + return savedUser;
  271 + }
  272 +
  273 + private JsonNode getActivateRequest(String password) throws Exception {
255 doGet("/api/noauth/activate?activateToken={activateToken}", TestMailService.currentActivateToken) 274 doGet("/api/noauth/activate?activateToken={activateToken}", TestMailService.currentActivateToken)
256 .andExpect(status().isSeeOther()) 275 .andExpect(status().isSeeOther())
257 .andExpect(header().string(HttpHeaders.LOCATION, "/login/createPassword?activateToken=" + TestMailService.currentActivateToken)); 276 .andExpect(header().string(HttpHeaders.LOCATION, "/login/createPassword?activateToken=" + TestMailService.currentActivateToken));
258 - JsonNode activateRequest = new ObjectMapper().createObjectNode() 277 + return new ObjectMapper().createObjectNode()
259 .put("activateToken", TestMailService.currentActivateToken) 278 .put("activateToken", TestMailService.currentActivateToken)
260 .put("password", password); 279 .put("password", password);
261 - JsonNode tokenInfo = readResponse(doPost("/api/noauth/activate", activateRequest).andExpect(status().isOk()), JsonNode.class);  
262 - validateAndSetJwtToken(tokenInfo, user.getEmail());  
263 - return savedUser;  
264 } 280 }
265 281
266 protected void login(String username, String password) throws Exception { 282 protected void login(String username, String password) throws Exception {
@@ -443,6 +459,10 @@ public abstract class AbstractWebTest { @@ -443,6 +459,10 @@ public abstract class AbstractWebTest {
443 return readResponse(doPostAsync(urlTemplate, content, timeout, params).andExpect(resultMatcher), responseClass); 459 return readResponse(doPostAsync(urlTemplate, content, timeout, params).andExpect(resultMatcher), responseClass);
444 } 460 }
445 461
  462 + protected <T> T doPostClaimAsync(String urlTemplate, Object content, Class<T> responseClass, ResultMatcher resultMatcher, String... params) throws Exception {
  463 + return readResponse(doPostAsync(urlTemplate, content, DEFAULT_TIMEOUT, params).andExpect(resultMatcher), responseClass);
  464 + }
  465 +
446 protected <T> T doDelete(String urlTemplate, Class<T> responseClass, String... params) throws Exception { 466 protected <T> T doDelete(String urlTemplate, Class<T> responseClass, String... params) throws Exception {
447 return readResponse(doDelete(urlTemplate, params).andExpect(status().isOk()), responseClass); 467 return readResponse(doDelete(urlTemplate, params).andExpect(status().isOk()), responseClass);
448 } 468 }
@@ -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.junit.Assert;
  25 +import org.springframework.util.StringUtils;
  26 +import org.thingsboard.server.common.data.Device;
  27 +import org.thingsboard.server.common.data.DeviceProfile;
  28 +import org.thingsboard.server.common.data.DeviceProfileType;
  29 +import org.thingsboard.server.common.data.DeviceTransportType;
  30 +import org.thingsboard.server.common.data.Tenant;
  31 +import org.thingsboard.server.common.data.TransportPayloadType;
  32 +import org.thingsboard.server.common.data.User;
  33 +import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration;
  34 +import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
  35 +import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration;
  36 +import org.thingsboard.server.common.data.security.Authority;
  37 +import org.thingsboard.server.common.data.security.DeviceCredentials;
  38 +import org.thingsboard.server.controller.AbstractControllerTest;
  39 +import org.thingsboard.server.gen.transport.TransportProtos;
  40 +
  41 +import java.util.ArrayList;
  42 +import java.util.List;
  43 +import java.util.concurrent.atomic.AtomicInteger;
  44 +
  45 +import static org.junit.Assert.assertEquals;
  46 +import static org.junit.Assert.assertNotNull;
  47 +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
  48 +
  49 +@Slf4j
  50 +public abstract class AbstractMqttIntegrationTest extends AbstractControllerTest {
  51 +
  52 + protected static final String MQTT_URL = "tcp://localhost:1883";
  53 +
  54 + private static final AtomicInteger atomicInteger = new AtomicInteger(2);
  55 +
  56 + protected Tenant savedTenant;
  57 + protected User tenantAdmin;
  58 +
  59 + protected Device savedDevice;
  60 + protected String accessToken;
  61 +
  62 + protected Device savedGateway;
  63 + protected String gatewayAccessToken;
  64 +
  65 + protected void processBeforeTest(String deviceName, String gatewayName, TransportPayloadType payloadType, String telemetryTopic, String attributesTopic) throws Exception {
  66 + loginSysAdmin();
  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 + tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
  81 +
  82 + Device device = new Device();
  83 + device.setName(deviceName);
  84 + device.setType("default");
  85 +
  86 + Device gateway = new Device();
  87 + gateway.setName(gatewayName);
  88 + gateway.setType("default");
  89 + ObjectNode additionalInfo = mapper.createObjectNode();
  90 + additionalInfo.put("gateway", true);
  91 + gateway.setAdditionalInfo(additionalInfo);
  92 +
  93 + if (payloadType != null) {
  94 + DeviceProfile mqttDeviceProfile = createMqttDeviceProfile(payloadType, telemetryTopic, attributesTopic);
  95 + DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", mqttDeviceProfile, DeviceProfile.class);
  96 + device.setType(savedDeviceProfile.getName());
  97 + device.setDeviceProfileId(savedDeviceProfile.getId());
  98 + gateway.setType(savedDeviceProfile.getName());
  99 + gateway.setDeviceProfileId(savedDeviceProfile.getId());
  100 + }
  101 +
  102 + savedDevice = doPost("/api/device", device, Device.class);
  103 +
  104 + DeviceCredentials deviceCredentials =
  105 + doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
  106 +
  107 + savedGateway = doPost("/api/device", gateway, Device.class);
  108 +
  109 + DeviceCredentials gatewayCredentials =
  110 + doGet("/api/device/" + savedGateway.getId().getId().toString() + "/credentials", DeviceCredentials.class);
  111 +
  112 + assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId());
  113 + accessToken = deviceCredentials.getCredentialsId();
  114 + assertNotNull(accessToken);
  115 +
  116 + assertEquals(savedGateway.getId(), gatewayCredentials.getDeviceId());
  117 + gatewayAccessToken = gatewayCredentials.getCredentialsId();
  118 + assertNotNull(gatewayAccessToken);
  119 +
  120 + }
  121 +
  122 + protected void processAfterTest() throws Exception {
  123 + loginSysAdmin();
  124 + if (savedTenant != null) {
  125 + doDelete("/api/tenant/" + savedTenant.getId().getId().toString()).andExpect(status().isOk());
  126 + }
  127 + }
  128 +
  129 + protected MqttAsyncClient getMqttAsyncClient(String accessToken) throws MqttException {
  130 + String clientId = MqttAsyncClient.generateClientId();
  131 + MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId);
  132 +
  133 + MqttConnectOptions options = new MqttConnectOptions();
  134 + options.setUserName(accessToken);
  135 + client.connect(options).waitForCompletion();
  136 + return client;
  137 + }
  138 +
  139 + protected void publishMqttMsg(MqttAsyncClient client, byte[] payload, String topic) throws MqttException {
  140 + MqttMessage message = new MqttMessage();
  141 + message.setPayload(payload);
  142 + client.publish(topic, message);
  143 + }
  144 +
  145 + protected List<TransportProtos.KeyValueProto> getKvProtos(List<String> expectedKeys) {
  146 + List<TransportProtos.KeyValueProto> keyValueProtos = new ArrayList<>();
  147 + TransportProtos.KeyValueProto strKeyValueProto = getKeyValueProto(expectedKeys.get(0), "value1", TransportProtos.KeyValueType.STRING_V);
  148 + TransportProtos.KeyValueProto boolKeyValueProto = getKeyValueProto(expectedKeys.get(1), "true", TransportProtos.KeyValueType.BOOLEAN_V);
  149 + TransportProtos.KeyValueProto dblKeyValueProto = getKeyValueProto(expectedKeys.get(2), "3.0", TransportProtos.KeyValueType.DOUBLE_V);
  150 + TransportProtos.KeyValueProto longKeyValueProto = getKeyValueProto(expectedKeys.get(3), "4", TransportProtos.KeyValueType.LONG_V);
  151 + TransportProtos.KeyValueProto jsonKeyValueProto = getKeyValueProto(expectedKeys.get(4), "{\"someNumber\": 42, \"someArray\": [1,2,3], \"someNestedObject\": {\"key\": \"value\"}}", TransportProtos.KeyValueType.JSON_V);
  152 + keyValueProtos.add(strKeyValueProto);
  153 + keyValueProtos.add(boolKeyValueProto);
  154 + keyValueProtos.add(dblKeyValueProto);
  155 + keyValueProtos.add(longKeyValueProto);
  156 + keyValueProtos.add(jsonKeyValueProto);
  157 + return keyValueProtos;
  158 + }
  159 +
  160 + protected TransportProtos.KeyValueProto getKeyValueProto(String key, String strValue, TransportProtos.KeyValueType type) {
  161 + TransportProtos.KeyValueProto.Builder keyValueProtoBuilder = TransportProtos.KeyValueProto.newBuilder();
  162 + keyValueProtoBuilder.setKey(key);
  163 + keyValueProtoBuilder.setType(type);
  164 + switch (type) {
  165 + case BOOLEAN_V:
  166 + keyValueProtoBuilder.setBoolV(Boolean.parseBoolean(strValue));
  167 + break;
  168 + case LONG_V:
  169 + keyValueProtoBuilder.setLongV(Long.parseLong(strValue));
  170 + break;
  171 + case DOUBLE_V:
  172 + keyValueProtoBuilder.setDoubleV(Double.parseDouble(strValue));
  173 + break;
  174 + case STRING_V:
  175 + keyValueProtoBuilder.setStringV(strValue);
  176 + break;
  177 + case JSON_V:
  178 + keyValueProtoBuilder.setJsonV(strValue);
  179 + break;
  180 + }
  181 + return keyValueProtoBuilder.build();
  182 + }
  183 +
  184 + protected DeviceProfile createMqttDeviceProfile(TransportPayloadType transportPayloadType, String telemetryTopic, String attributesTopic) {
  185 + DeviceProfile deviceProfile = new DeviceProfile();
  186 + deviceProfile.setName(transportPayloadType.name());
  187 + deviceProfile.setType(DeviceProfileType.DEFAULT);
  188 + deviceProfile.setTransportType(DeviceTransportType.MQTT);
  189 + deviceProfile.setDescription(transportPayloadType.name() + " Test");
  190 + DeviceProfileData deviceProfileData = new DeviceProfileData();
  191 + DefaultDeviceProfileConfiguration configuration = new DefaultDeviceProfileConfiguration();
  192 + MqttDeviceProfileTransportConfiguration transportConfiguration = new MqttDeviceProfileTransportConfiguration();
  193 + transportConfiguration.setTransportPayloadType(transportPayloadType);
  194 + if (!StringUtils.isEmpty(telemetryTopic)) {
  195 + transportConfiguration.setDeviceTelemetryTopic(telemetryTopic);
  196 + }
  197 + if (!StringUtils.isEmpty(attributesTopic)) {
  198 + transportConfiguration.setDeviceAttributesTopic(attributesTopic);
  199 + }
  200 + deviceProfileData.setTransportConfiguration(transportConfiguration);
  201 + deviceProfileData.setConfiguration(configuration);
  202 + deviceProfile.setProfileData(deviceProfileData);
  203 + deviceProfile.setDefault(false);
  204 + deviceProfile.setDefaultRuleChainId(null);
  205 + return deviceProfile;
  206 + }
  207 +
  208 + protected TransportProtos.PostAttributeMsg getPostAttributeMsg(List<String> expectedKeys) {
  209 + List<TransportProtos.KeyValueProto> kvProtos = getKvProtos(expectedKeys);
  210 + TransportProtos.PostAttributeMsg.Builder builder = TransportProtos.PostAttributeMsg.newBuilder();
  211 + builder.addAllKv(kvProtos);
  212 + return builder.build();
  213 + }
  214 +
  215 +}
@@ -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 + Thread.sleep(1000);
  88 +
  89 + Device savedDevice = doGet("/api/tenant/devices?deviceName=" + "Gateway Device Request Attributes", Device.class);
  90 + assertNotNull(savedDevice);
  91 + doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk());
  92 +
  93 + Thread.sleep(1000);
  94 +
  95 + client.subscribe(MqttTopics.GATEWAY_ATTRIBUTES_RESPONSE_TOPIC, MqttQoS.AT_LEAST_ONCE.value());
  96 +
  97 + TestMqttCallback clientAttributesCallback = getTestMqttCallback();
  98 + client.setCallback(clientAttributesCallback);
  99 + validateClientResponseGateway(client, clientAttributesCallback);
  100 +
  101 + TestMqttCallback sharedAttributesCallback = getTestMqttCallback();
  102 + client.setCallback(sharedAttributesCallback);
  103 + validateSharedResponseGateway(client, sharedAttributesCallback);
  104 + }
  105 +
  106 + protected void postAttributesAndSubscribeToTopic(Device savedDevice, MqttAsyncClient client) throws Exception {
  107 + doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk());
  108 + client.publish(MqttTopics.DEVICE_ATTRIBUTES_TOPIC, new MqttMessage(POST_ATTRIBUTES_PAYLOAD.getBytes()));
  109 + client.subscribe(MqttTopics.DEVICE_ATTRIBUTES_RESPONSES_TOPIC, MqttQoS.AT_MOST_ONCE.value());
  110 + }
  111 +
  112 + protected void postGatewayDeviceClientAttributes(MqttAsyncClient client) throws Exception {
  113 + 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\"}}}}";
  114 + client.publish(MqttTopics.GATEWAY_ATTRIBUTES_TOPIC, new MqttMessage(postClientAttributes.getBytes()));
  115 + }
  116 +
  117 + protected void validateResponse(MqttAsyncClient client, CountDownLatch latch, TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException {
  118 + String keys = "attribute1,attribute2,attribute3,attribute4,attribute5";
  119 + String payloadStr = "{\"clientKeys\":\"" + keys + "\", \"sharedKeys\":\"" + keys + "\"}";
  120 + MqttMessage mqttMessage = new MqttMessage();
  121 + mqttMessage.setPayload(payloadStr.getBytes());
  122 + client.publish(MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX + "1", mqttMessage);
  123 + latch.await(3, TimeUnit.SECONDS);
  124 + assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS());
  125 + 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\"}}}}";
  126 + assertEquals(JacksonUtil.toJsonNode(expectedRequestPayload), JacksonUtil.toJsonNode(new String(callback.getPayloadBytes(), StandardCharsets.UTF_8)));
  127 + }
  128 +
  129 + protected void validateClientResponseGateway(MqttAsyncClient client, TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException {
  130 + String payloadStr = "{\"id\": 1, \"device\": \"" + "Gateway Device Request Attributes" + "\", \"client\": true, \"keys\": [\"attribute1\", \"attribute2\", \"attribute3\", \"attribute4\", \"attribute5\"]}";
  131 + MqttMessage mqttMessage = new MqttMessage();
  132 + mqttMessage.setPayload(payloadStr.getBytes());
  133 + client.publish(MqttTopics.GATEWAY_ATTRIBUTES_REQUEST_TOPIC, mqttMessage);
  134 + callback.getLatch().await(3, TimeUnit.SECONDS);
  135 + assertEquals(MqttQoS.AT_LEAST_ONCE.value(), callback.getQoS());
  136 + 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\"}}}}";
  137 + assertEquals(JacksonUtil.toJsonNode(expectedRequestPayload), JacksonUtil.toJsonNode(new String(callback.getPayloadBytes(), StandardCharsets.UTF_8)));
  138 + }
  139 +
  140 + protected void validateSharedResponseGateway(MqttAsyncClient client, TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException {
  141 + String payloadStr = "{\"id\": 1, \"device\": \"" + "Gateway Device Request Attributes" + "\", \"client\": false, \"keys\": [\"attribute1\", \"attribute2\", \"attribute3\", \"attribute4\", \"attribute5\"]}";
  142 + MqttMessage mqttMessage = new MqttMessage();
  143 + mqttMessage.setPayload(payloadStr.getBytes());
  144 + client.publish(MqttTopics.GATEWAY_ATTRIBUTES_REQUEST_TOPIC, mqttMessage);
  145 + callback.getLatch().await(3, TimeUnit.SECONDS);
  146 + assertEquals(MqttQoS.AT_LEAST_ONCE.value(), callback.getQoS());
  147 + 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\"}}}}";
  148 + assertEquals(JacksonUtil.toJsonNode(expectedRequestPayload), JacksonUtil.toJsonNode(new String(callback.getPayloadBytes(), StandardCharsets.UTF_8)));
  149 + }
  150 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.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(2000);
  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 + Thread.sleep(1000);
  131 +
  132 + Device savedDevice = doGet("/api/tenant/devices?deviceName=" + "Gateway Device Subscribe to attribute updates", Device.class);
  133 + assertNotNull(savedDevice);
  134 +
  135 + client.subscribe(MqttTopics.GATEWAY_ATTRIBUTES_TOPIC, MqttQoS.AT_MOST_ONCE.value());
  136 +
  137 + Thread.sleep(2000);
  138 +
  139 + doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk());
  140 + onUpdateCallback.getLatch().await(3, TimeUnit.SECONDS);
  141 +
  142 + validateGatewayUpdateAttributesResponse(onUpdateCallback);
  143 +
  144 + TestMqttCallback onDeleteCallback = getTestMqttCallback();
  145 + client.setCallback(onDeleteCallback);
  146 +
  147 + doDelete("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/SHARED_SCOPE?keys=attribute5", String.class);
  148 + onDeleteCallback.getLatch().await(3, TimeUnit.SECONDS);
  149 +
  150 + validateGatewayDeleteAttributesResponse(onDeleteCallback);
  151 +
  152 + }
  153 +
  154 + protected void validateGatewayUpdateAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException {
  155 + assertNotNull(callback.getPayloadBytes());
  156 + String s = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8);
  157 + assertEquals(getResponseGatewayAttributesUpdatedPayload(), s);
  158 + }
  159 +
  160 + protected void validateGatewayDeleteAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException {
  161 + assertNotNull(callback.getPayloadBytes());
  162 + String s = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8);
  163 + assertEquals(s, getResponseGatewayAttributesDeletedPayload());
  164 + }
  165 +
  166 + protected byte[] getConnectPayloadBytes() {
  167 + String connectPayload = "{\"device\": \"Gateway Device Subscribe to attribute updates\", \"type\": \"" + TransportPayloadType.JSON.name() + "\"}";
  168 + return connectPayload.getBytes();
  169 + }
  170 +}
  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 +}
  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.Test;
  24 +import org.thingsboard.server.common.data.ClaimRequest;
  25 +import org.thingsboard.server.common.data.Customer;
  26 +import org.thingsboard.server.common.data.Device;
  27 +import org.thingsboard.server.common.data.User;
  28 +import org.thingsboard.server.common.data.device.profile.MqttTopics;
  29 +import org.thingsboard.server.common.data.security.Authority;
  30 +import org.thingsboard.server.dao.device.claim.ClaimResponse;
  31 +import org.thingsboard.server.dao.device.claim.ClaimResult;
  32 +import org.thingsboard.server.mqtt.AbstractMqttIntegrationTest;
  33 +
  34 +import static org.junit.Assert.assertEquals;
  35 +import static org.junit.Assert.assertNotNull;
  36 +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
  37 +
  38 +@Slf4j
  39 +public abstract class AbstractMqttClaimDeviceTest extends AbstractMqttIntegrationTest {
  40 +
  41 + protected static final String CUSTOMER_USER_PASSWORD = "customerUser123!";
  42 +
  43 + protected User customerAdmin;
  44 + protected Customer savedCustomer;
  45 +
  46 + @Before
  47 + public void beforeTest() throws Exception {
  48 + super.processBeforeTest("Test Claim device", "Test Claim gateway", null, null, null);
  49 + createCustomerAndUser();
  50 + }
  51 +
  52 + protected void createCustomerAndUser() throws Exception {
  53 + Customer customer = new Customer();
  54 + customer.setTenantId(savedTenant.getId());
  55 + customer.setTitle("Test Claiming Customer");
  56 + savedCustomer = doPost("/api/customer", customer, Customer.class);
  57 + assertNotNull(savedCustomer);
  58 + assertEquals(savedTenant.getId(), savedCustomer.getTenantId());
  59 +
  60 + User user = new User();
  61 + user.setAuthority(Authority.CUSTOMER_USER);
  62 + user.setTenantId(savedTenant.getId());
  63 + user.setCustomerId(savedCustomer.getId());
  64 + user.setEmail("customer@thingsboard.org");
  65 +
  66 + customerAdmin = createUser(user, CUSTOMER_USER_PASSWORD);
  67 + assertNotNull(customerAdmin);
  68 + assertEquals(customerAdmin.getCustomerId(), savedCustomer.getId());
  69 + }
  70 +
  71 + @After
  72 + public void afterTest() throws Exception {
  73 + super.processAfterTest();
  74 + }
  75 +
  76 + @Test
  77 + public void testClaimingDevice() throws Exception {
  78 + processTestClaimingDevice(false);
  79 + }
  80 +
  81 + @Test
  82 + public void testClaimingDeviceWithoutSecretAndDuration() throws Exception {
  83 + processTestClaimingDevice(true);
  84 + }
  85 +
  86 + @Test
  87 + public void testGatewayClaimingDevice() throws Exception {
  88 + processTestGatewayClaimingDevice("Test claiming gateway device", false);
  89 + }
  90 +
  91 + @Test
  92 + public void testGatewayClaimingDeviceWithoutSecretAndDuration() throws Exception {
  93 + processTestGatewayClaimingDevice("Test claiming gateway device empty payload", true);
  94 + }
  95 +
  96 +
  97 + protected void processTestClaimingDevice(boolean emptyPayload) throws Exception {
  98 + MqttAsyncClient client = getMqttAsyncClient(accessToken);
  99 + byte[] payloadBytes;
  100 + byte[] failurePayloadBytes;
  101 + if (emptyPayload) {
  102 + payloadBytes = "{}".getBytes();
  103 + failurePayloadBytes = "{\"durationMs\":1}".getBytes();
  104 + } else {
  105 + payloadBytes = "{\"secretKey\":\"value\", \"durationMs\":60000}".getBytes();
  106 + failurePayloadBytes = "{\"secretKey\":\"value\", \"durationMs\":1}".getBytes();
  107 + }
  108 + validateClaimResponse(emptyPayload, client, payloadBytes, failurePayloadBytes);
  109 + }
  110 +
  111 + protected void validateClaimResponse(boolean emptyPayload, MqttAsyncClient client, byte[] payloadBytes, byte[] failurePayloadBytes) throws Exception {
  112 + client.publish(MqttTopics.DEVICE_CLAIM_TOPIC, new MqttMessage(failurePayloadBytes));
  113 +
  114 + Thread.sleep(2000);
  115 +
  116 + loginUser(customerAdmin.getName(), CUSTOMER_USER_PASSWORD);
  117 + ClaimRequest claimRequest;
  118 + if (!emptyPayload) {
  119 + claimRequest = new ClaimRequest("value");
  120 + } else {
  121 + claimRequest = new ClaimRequest(null);
  122 + }
  123 +
  124 + ClaimResponse claimResponse = doPostClaimAsync("/api/customer/device/" + savedDevice.getName() + "/claim", claimRequest, ClaimResponse.class, status().isBadRequest());
  125 + assertEquals(claimResponse, ClaimResponse.FAILURE);
  126 +
  127 + client.publish(MqttTopics.DEVICE_CLAIM_TOPIC, new MqttMessage(payloadBytes));
  128 +
  129 + Thread.sleep(2000);
  130 +
  131 + ClaimResult claimResult = doPostClaimAsync("/api/customer/device/" + savedDevice.getName() + "/claim", claimRequest, ClaimResult.class, status().isOk());
  132 + assertEquals(claimResult.getResponse(), ClaimResponse.SUCCESS);
  133 + Device claimedDevice = claimResult.getDevice();
  134 + assertNotNull(claimedDevice);
  135 + assertNotNull(claimedDevice.getCustomerId());
  136 + assertEquals(customerAdmin.getCustomerId(), claimedDevice.getCustomerId());
  137 +
  138 + claimResponse = doPostClaimAsync("/api/customer/device/" + savedDevice.getName() + "/claim", claimRequest, ClaimResponse.class, status().isBadRequest());
  139 + assertEquals(claimResponse, ClaimResponse.CLAIMED);
  140 + }
  141 +
  142 + protected void validateGatewayClaimResponse(String deviceName, boolean emptyPayload, MqttAsyncClient client, byte[] failurePayloadBytes, byte[] payloadBytes) throws Exception {
  143 + client.publish(MqttTopics.GATEWAY_CLAIM_TOPIC, new MqttMessage(failurePayloadBytes));
  144 +
  145 + Thread.sleep(2000);
  146 +
  147 + Device savedDevice = doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class);
  148 + assertNotNull(savedDevice);
  149 +
  150 + loginUser(customerAdmin.getName(), CUSTOMER_USER_PASSWORD);
  151 + ClaimRequest claimRequest;
  152 + if (!emptyPayload) {
  153 + claimRequest = new ClaimRequest("value");
  154 + } else {
  155 + claimRequest = new ClaimRequest(null);
  156 + }
  157 +
  158 + ClaimResponse claimResponse = doPostClaimAsync("/api/customer/device/" + deviceName + "/claim", claimRequest, ClaimResponse.class, status().isBadRequest());
  159 + assertEquals(claimResponse, ClaimResponse.FAILURE);
  160 +
  161 + client.publish(MqttTopics.GATEWAY_CLAIM_TOPIC, new MqttMessage(payloadBytes));
  162 +
  163 + Thread.sleep(2000);
  164 +
  165 + ClaimResult claimResult = doPostClaimAsync("/api/customer/device/" + deviceName + "/claim", claimRequest, ClaimResult.class, status().isOk());
  166 + assertEquals(claimResult.getResponse(), ClaimResponse.SUCCESS);
  167 + Device claimedDevice = claimResult.getDevice();
  168 + assertNotNull(claimedDevice);
  169 + assertNotNull(claimedDevice.getCustomerId());
  170 + assertEquals(customerAdmin.getCustomerId(), claimedDevice.getCustomerId());
  171 +
  172 + claimResponse = doPostClaimAsync("/api/customer/device/" + deviceName + "/claim", claimRequest, ClaimResponse.class, status().isBadRequest());
  173 + assertEquals(claimResponse, ClaimResponse.CLAIMED);
  174 + }
  175 +
  176 + protected void processTestGatewayClaimingDevice(String deviceName, boolean emptyPayload) throws Exception {
  177 + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
  178 + byte[] failurePayloadBytes;
  179 + byte[] payloadBytes;
  180 + String failurePayload;
  181 + String payload;
  182 + if (emptyPayload) {
  183 + failurePayload = "{\"" + deviceName + "\": " + "{\"durationMs\":1}" + "}";
  184 + payload = "{\"" + deviceName + "\": " + "{}" + "}";
  185 + } else {
  186 + failurePayload = "{\"" + deviceName + "\": " + "{\"secretKey\":\"value\", \"durationMs\":1}" + "}";
  187 + payload = "{\"" + deviceName + "\": " + "{\"secretKey\":\"value\", \"durationMs\":60000}" + "}";
  188 + }
  189 + payloadBytes = payload.getBytes();
  190 + failurePayloadBytes = failurePayload.getBytes();
  191 + validateGatewayClaimResponse(deviceName, emptyPayload, client, failurePayloadBytes, payloadBytes);
  192 + }
  193 +
  194 +}
  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.Test;
  22 +import org.thingsboard.server.common.data.TransportPayloadType;
  23 +
  24 +@Slf4j
  25 +public abstract class AbstractMqttClaimJsonDeviceTest extends AbstractMqttClaimDeviceTest {
  26 +
  27 + @Before
  28 + public void beforeTest() throws Exception {
  29 + super.processBeforeTest("Test Claim device", "Test Claim gateway", TransportPayloadType.JSON, null, null);
  30 + createCustomerAndUser();
  31 + }
  32 +
  33 + @After
  34 + public void afterTest() throws Exception {
  35 + super.afterTest();
  36 + }
  37 +
  38 + @Test
  39 + public void testClaimingDevice() throws Exception {
  40 + processTestClaimingDevice(false);
  41 + }
  42 +
  43 + @Test
  44 + public void testClaimingDeviceWithoutSecretAndDuration() throws Exception {
  45 + processTestClaimingDevice(true);
  46 + }
  47 +
  48 + @Test
  49 + public void testGatewayClaimingDevice() throws Exception {
  50 + processTestGatewayClaimingDevice("Test claiming gateway device Json", false);
  51 + }
  52 +
  53 + @Test
  54 + public void testGatewayClaimingDeviceWithoutSecretAndDuration() throws Exception {
  55 + processTestGatewayClaimingDevice("Test claiming gateway device empty payload Json", true);
  56 + }
  57 +}
  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.Test;
  23 +import org.thingsboard.server.common.data.TransportPayloadType;
  24 +import org.thingsboard.server.gen.transport.TransportApiProtos;
  25 +
  26 +@Slf4j
  27 +public abstract class AbstractMqttClaimProtoDeviceTest extends AbstractMqttClaimDeviceTest {
  28 +
  29 + @Before
  30 + public void beforeTest() throws Exception {
  31 + processBeforeTest("Test Claim device", "Test Claim gateway", TransportPayloadType.PROTOBUF, null, null);
  32 + createCustomerAndUser();
  33 + }
  34 +
  35 + @After
  36 + public void afterTest() throws Exception { super.afterTest(); }
  37 +
  38 + @Test
  39 + public void testClaimingDevice() throws Exception {
  40 + processTestClaimingDevice(false);
  41 + }
  42 +
  43 + @Test
  44 + public void testClaimingDeviceWithoutSecretAndDuration() throws Exception {
  45 + processTestClaimingDevice(true);
  46 + }
  47 +
  48 + @Test
  49 + public void testGatewayClaimingDevice() throws Exception {
  50 + processTestGatewayClaimingDevice("Test claiming gateway device Proto", false);
  51 + }
  52 +
  53 + @Test
  54 + public void testGatewayClaimingDeviceWithoutSecretAndDuration() throws Exception {
  55 + processTestGatewayClaimingDevice("Test claiming gateway device empty payload Proto", true);
  56 + }
  57 +
  58 + protected void processTestClaimingDevice(boolean emptyPayload) throws Exception {
  59 + MqttAsyncClient client = getMqttAsyncClient(accessToken);
  60 + byte[] payloadBytes;
  61 + if (emptyPayload) {
  62 + payloadBytes = getClaimDevice(0, emptyPayload).toByteArray();
  63 + } else {
  64 + payloadBytes = getClaimDevice(60000, emptyPayload).toByteArray();
  65 + }
  66 + byte[] failurePayloadBytes = getClaimDevice(1, emptyPayload).toByteArray();
  67 + validateClaimResponse(emptyPayload, client, payloadBytes, failurePayloadBytes);
  68 + }
  69 +
  70 + protected void processTestGatewayClaimingDevice(String deviceName, boolean emptyPayload) throws Exception {
  71 + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
  72 + byte[] failurePayloadBytes;
  73 + byte[] payloadBytes;
  74 + if (emptyPayload) {
  75 + payloadBytes = getGatewayClaimMsg(deviceName, 0, emptyPayload).toByteArray();
  76 + } else {
  77 + payloadBytes = getGatewayClaimMsg(deviceName, 60000, emptyPayload).toByteArray();
  78 + }
  79 + failurePayloadBytes = getGatewayClaimMsg(deviceName, 1, emptyPayload).toByteArray();
  80 +
  81 + validateGatewayClaimResponse(deviceName, emptyPayload, client, failurePayloadBytes, payloadBytes);
  82 + }
  83 +
  84 + private TransportApiProtos.GatewayClaimMsg getGatewayClaimMsg(String deviceName, long duration, boolean emptyPayload) {
  85 + TransportApiProtos.GatewayClaimMsg.Builder gatewayClaimMsgBuilder = TransportApiProtos.GatewayClaimMsg.newBuilder();
  86 + TransportApiProtos.ClaimDeviceMsg.Builder claimDeviceMsgBuilder = TransportApiProtos.ClaimDeviceMsg.newBuilder();
  87 + TransportApiProtos.ClaimDevice.Builder claimDeviceBuilder = TransportApiProtos.ClaimDevice.newBuilder();
  88 + if (!emptyPayload) {
  89 + claimDeviceBuilder.setSecretKey("value");
  90 + }
  91 + if (duration > 0) {
  92 + claimDeviceBuilder.setDurationMs(duration);
  93 + }
  94 + TransportApiProtos.ClaimDevice claimDevice = claimDeviceBuilder.build();
  95 + claimDeviceMsgBuilder.setClaimRequest(claimDevice);
  96 + claimDeviceMsgBuilder.setDeviceName(deviceName);
  97 + TransportApiProtos.ClaimDeviceMsg claimDeviceMsg = claimDeviceMsgBuilder.build();
  98 + gatewayClaimMsgBuilder.addMsg(claimDeviceMsg);
  99 + return gatewayClaimMsgBuilder.build();
  100 + }
  101 +
  102 + private TransportApiProtos.ClaimDevice getClaimDevice(long duration, boolean emptyPayload) {
  103 + TransportApiProtos.ClaimDevice.Builder claimDeviceBuilder = TransportApiProtos.ClaimDevice.newBuilder();
  104 + if (!emptyPayload) {
  105 + claimDeviceBuilder.setSecretKey("value");
  106 + }
  107 + if (duration > 0) {
  108 + claimDeviceBuilder.setSecretKey("value");
  109 + claimDeviceBuilder.setDurationMs(duration);
  110 + }
  111 + return claimDeviceBuilder.build();
  112 + }
  113 +
  114 +
  115 +}
  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 +}
  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 +}
  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 +}
  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,74 +64,88 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @@ -47,74 +64,88 @@ 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; 71 + protected Long asyncContextTimeoutToUseRpcPlugin;
58 72
59 - private static final AtomicInteger atomicInteger = new AtomicInteger(2); 73 + protected void processBeforeTest(String deviceName, String gatewayName, TransportPayloadType payloadType, String telemetryTopic, String attributesTopic) throws Exception {
  74 + super.processBeforeTest(deviceName, gatewayName, payloadType, telemetryTopic, attributesTopic);
  75 + asyncContextTimeoutToUseRpcPlugin = 10000L;
  76 + }
60 77
  78 + protected void processOneWayRpcTest() throws Exception {
  79 + MqttAsyncClient client = getMqttAsyncClient(accessToken);
61 80
62 - @Before  
63 - public void beforeTest() throws Exception {  
64 - loginSysAdmin(); 81 + CountDownLatch latch = new CountDownLatch(1);
  82 + TestMqttCallback callback = new TestMqttCallback(client, latch);
  83 + client.setCallback(callback);
65 84
66 - asyncContextTimeoutToUseRpcPlugin = 10000L; 85 + client.subscribe(MqttTopics.DEVICE_RPC_REQUESTS_SUB_TOPIC, MqttQoS.AT_MOST_ONCE.value());
67 86
68 - Tenant tenant = new Tenant();  
69 - tenant.setTitle("My tenant");  
70 - savedTenant = doPost("/api/tenant", tenant, Tenant.class);  
71 - Assert.assertNotNull(savedTenant); 87 + Thread.sleep(2000);
72 88
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"); 89 + String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}";
  90 + String deviceId = savedDevice.getId().getId().toString();
  91 + String result = doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isOk());
  92 + Assert.assertTrue(StringUtils.isEmpty(result));
  93 + latch.await(3, TimeUnit.SECONDS);
  94 + assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS());
  95 + }
79 96
80 - createUserAndLogin(tenantAdmin, "testPassword1"); 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);
81 } 102 }
82 103
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 - } 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(2000);
  113 +
  114 + String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"26\",\"value\": 1}}";
  115 + String deviceId = savedDevice.getId().getId().toString();
  116 +
  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);
89 } 121 }
90 122
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); 123 + protected void processTwoWayRpcTestGateway(String deviceName) throws Exception {
  124 + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
  125 +
  126 + String payload = "{\"device\":\"" + deviceName + "\"}";
  127 + byte[] payloadBytes = payload.getBytes();
101 128
102 - String clientId = MqttAsyncClient.generateClientId();  
103 - MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId); 129 + validateTwoWayRpcGateway(deviceName, client, payloadBytes);
  130 + }
104 131
105 - MqttConnectOptions options = new MqttConnectOptions();  
106 - options.setUserName(accessToken);  
107 - client.connect(options).waitForCompletion(); 132 + protected void validateOneWayRpcGatewayResponse(String deviceName, MqttAsyncClient client, byte[] payloadBytes) throws Exception {
  133 + publishMqttMsg(client, payloadBytes, MqttTopics.GATEWAY_CONNECT_TOPIC);
  134 +
  135 + Thread.sleep(2000);
  136 +
  137 + Device savedDevice = getDeviceByName(deviceName);
  138 + assertNotNull(savedDevice);
108 139
109 CountDownLatch latch = new CountDownLatch(1); 140 CountDownLatch latch = new CountDownLatch(1);
110 TestMqttCallback callback = new TestMqttCallback(client, latch); 141 TestMqttCallback callback = new TestMqttCallback(client, latch);
111 client.setCallback(callback); 142 client.setCallback(callback);
112 143
113 - client.subscribe("v1/devices/me/rpc/request/+", MqttQoS.AT_MOST_ONCE.value()); 144 + client.subscribe(MqttTopics.GATEWAY_RPC_TOPIC, MqttQoS.AT_MOST_ONCE.value());
114 145
115 Thread.sleep(2000); 146 Thread.sleep(2000);
116 147
117 - String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}"; 148 + String setGpioRequest = "{\"method\": \"toggle_gpio\", \"params\": {\"pin\":1}}";
118 String deviceId = savedDevice.getId().getId().toString(); 149 String deviceId = savedDevice.getId().getId().toString();
119 String result = doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isOk()); 150 String result = doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isOk());
120 Assert.assertTrue(StringUtils.isEmpty(result)); 151 Assert.assertTrue(StringUtils.isEmpty(result));
@@ -122,100 +153,49 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC @@ -122,100 +153,49 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC
122 assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS()); 153 assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS());
123 } 154 }
124 155
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}";  
137 - String deviceId = savedDevice.getId().getId().toString(); 156 + protected void validateTwoWayRpcGateway(String deviceName, MqttAsyncClient client, byte[] payloadBytes) throws Exception {
  157 + publishMqttMsg(client, payloadBytes, MqttTopics.GATEWAY_CONNECT_TOPIC);
138 158
139 - doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().is(409),  
140 - asyncContextTimeoutToUseRpcPlugin);  
141 - } 159 + Thread.sleep(2000);
142 160
143 - @Test  
144 - public void testServerMqttOneWayRpcDeviceDoesNotExist() throws Exception {  
145 - String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"25\",\"value\": 1}}";  
146 - String nonExistentDeviceId = Uuids.timeBased().toString(); 161 + Device savedDevice = getDeviceByName(deviceName);
  162 + assertNotNull(savedDevice);
147 163
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);  
151 - } 164 + CountDownLatch latch = new CountDownLatch(1);
  165 + TestMqttCallback callback = new TestMqttCallback(client, latch);
  166 + client.setCallback(callback);
152 167
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);  
163 -  
164 - String clientId = MqttAsyncClient.generateClientId();  
165 - MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId);  
166 -  
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))); 168 + client.subscribe(MqttTopics.GATEWAY_RPC_TOPIC, MqttQoS.AT_MOST_ONCE.value());
172 169
173 Thread.sleep(2000); 170 Thread.sleep(2000);
174 171
175 - String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"26\",\"value\": 1}}"; 172 + String setGpioRequest = "{\"method\": \"toggle_gpio\", \"params\": {\"pin\":1}}";
176 String deviceId = savedDevice.getId().getId().toString(); 173 String deviceId = savedDevice.getId().getId().toString();
177 -  
178 String result = doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk()); 174 String result = doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
179 - Assert.assertEquals("{\"value1\":\"A\",\"value2\":\"B\"}", result);  
180 - }  
181 -  
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();  
195 -  
196 - doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().is(409),  
197 - asyncContextTimeoutToUseRpcPlugin);  
198 - }  
199 -  
200 - @Test  
201 - public void testServerMqttTwoWayRpcDeviceDoesNotExist() throws Exception {  
202 - String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"28\",\"value\": 1}}";  
203 - String nonExistentDeviceId = Uuids.timeBased().toString();  
204 -  
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); 175 + latch.await(3, TimeUnit.SECONDS);
  176 + String expected = "{\"success\":true}";
  177 + assertEquals(expected, result);
  178 + assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS());
208 } 179 }
209 180
210 - private Device getSavedDevice(Device device) throws Exception {  
211 - return doPost("/api/device", device, Device.class); 181 + private Device getDeviceByName(String deviceName) throws Exception {
  182 + return doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class);
212 } 183 }
213 184
214 - private DeviceCredentials getDeviceCredentials(Device savedDevice) throws Exception {  
215 - return doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class); 185 + protected MqttMessage processMessageArrived(String requestTopic, MqttMessage mqttMessage) throws MqttException, InvalidProtocolBufferException {
  186 + MqttMessage message = new MqttMessage();
  187 + if (requestTopic.startsWith(MqttTopics.BASE_DEVICE_API_TOPIC)) {
  188 + message.setPayload(DEVICE_RESPONSE.getBytes(StandardCharset.UTF_8));
  189 + } else {
  190 + JsonNode requestMsgNode = JacksonUtil.toJsonNode(new String(mqttMessage.getPayload(), StandardCharset.UTF_8));
  191 + String deviceName = requestMsgNode.get("device").asText();
  192 + int requestId = requestMsgNode.get("data").get("id").asInt();
  193 + message.setPayload(("{\"device\": \"" + deviceName + "\", \"id\": " + requestId + ", \"data\": {\"success\": true}}").getBytes(StandardCharset.UTF_8));
  194 + }
  195 + return message;
216 } 196 }
217 197
218 - private static class TestMqttCallback implements MqttCallback { 198 + private class TestMqttCallback implements MqttCallback {
219 199
220 private final MqttAsyncClient client; 200 private final MqttAsyncClient client;
221 private final CountDownLatch latch; 201 private final CountDownLatch latch;
@@ -237,11 +217,9 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC @@ -237,11 +217,9 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC
237 @Override 217 @Override
238 public void messageArrived(String requestTopic, MqttMessage mqttMessage) throws Exception { 218 public void messageArrived(String requestTopic, MqttMessage mqttMessage) throws Exception {
239 log.info("Message Arrived: " + Arrays.toString(mqttMessage.getPayload())); 219 log.info("Message Arrived: " + Arrays.toString(mqttMessage.getPayload()));
240 - MqttMessage message = new MqttMessage();  
241 String responseTopic = requestTopic.replace("request", "response"); 220 String responseTopic = requestTopic.replace("request", "response");
242 - message.setPayload("{\"value1\":\"A\", \"value2\":\"B\"}".getBytes("UTF-8"));  
243 qoS = mqttMessage.getQos(); 221 qoS = mqttMessage.getQos();
244 - client.publish(responseTopic, message); 222 + client.publish(responseTopic, processMessageArrived(requestTopic, mqttMessage));
245 latch.countDown(); 223 latch.countDown();
246 } 224 }
247 225
  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 }
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() + 2000;
  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 + Thread.sleep(2000);
  110 +
  111 + Device firstDevice = doGet("/api/tenant/devices?deviceName=" + firstDeviceName, Device.class);
  112 + assertNotNull(firstDevice);
  113 + Device secondDevice = doGet("/api/tenant/devices?deviceName=" + secondDeviceName, Device.class);
  114 + assertNotNull(secondDevice);
  115 +
  116 + List<String> firstDeviceActualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + firstDevice.getId() + "/keys/attributes/CLIENT_SCOPE", List.class);
  117 + Set<String> firstDeviceActualKeySet = new HashSet<>(firstDeviceActualKeys);
  118 +
  119 + List<String> secondDeviceActualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + secondDevice.getId() + "/keys/attributes/CLIENT_SCOPE", List.class);
  120 + Set<String> secondDeviceActualKeySet = new HashSet<>(secondDeviceActualKeys);
  121 +
  122 + Set<String> expectedKeySet = new HashSet<>(expectedKeys);
  123 +
  124 + assertEquals(expectedKeySet, firstDeviceActualKeySet);
  125 + assertEquals(expectedKeySet, secondDeviceActualKeySet);
  126 +
  127 + String getAttributesValuesUrlFirstDevice = getAttributesValuesUrl(firstDevice.getId(), firstDeviceActualKeySet);
  128 + String getAttributesValuesUrlSecondDevice = getAttributesValuesUrl(firstDevice.getId(), secondDeviceActualKeySet);
  129 +
  130 + List<Map<String, Object>> firstDeviceValues = doGetAsync(getAttributesValuesUrlFirstDevice, List.class);
  131 + List<Map<String, Object>> secondDeviceValues = doGetAsync(getAttributesValuesUrlSecondDevice, List.class);
  132 +
  133 + assertAttributesValues(firstDeviceValues, expectedKeySet);
  134 + assertAttributesValues(secondDeviceValues, expectedKeySet);
  135 +
  136 + }
  137 +
  138 + protected void assertAttributesValues(List<Map<String, Object>> deviceValues, Set<String> expectedKeySet) {
  139 + for (Map<String, Object> map : deviceValues) {
  140 + String key = (String) map.get("key");
  141 + Object value = map.get("value");
  142 + assertTrue(expectedKeySet.contains(key));
  143 + switch (key) {
  144 + case "key1":
  145 + assertEquals("value1", value);
  146 + break;
  147 + case "key2":
  148 + assertEquals(true, value);
  149 + break;
  150 + case "key3":
  151 + assertEquals(3.0, value);
  152 + break;
  153 + case "key4":
  154 + assertEquals(4, value);
  155 + break;
  156 + case "key5":
  157 + assertNotNull(value);
  158 + assertEquals(3, ((LinkedHashMap) value).size());
  159 + assertEquals(42, ((LinkedHashMap) value).get("someNumber"));
  160 + assertEquals(Arrays.asList(1, 2, 3), ((LinkedHashMap) value).get("someArray"));
  161 + LinkedHashMap<String, String> someNestedObject = (LinkedHashMap) ((LinkedHashMap) value).get("someNestedObject");
  162 + assertEquals("value", someNestedObject.get("key"));
  163 + break;
  164 + }
  165 + }
  166 + }
  167 +
  168 + protected String getGatewayAttributesJsonPayload(String deviceA, String deviceB) {
  169 + return "{\"" + deviceA + "\": " + PAYLOAD_VALUES_STR + ", \"" + deviceB + "\": " + PAYLOAD_VALUES_STR + "}";
  170 + }
  171 +
  172 + private String getAttributesValuesUrl(DeviceId deviceId, Set<String> actualKeySet) {
  173 + return "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/attributes/CLIENT_SCOPE?keys=" + String.join(",", actualKeySet);
  174 + }
  175 +}
  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.junit.After;
  26 +import org.junit.Before;
  27 +import org.junit.Test;
  28 +import org.thingsboard.server.common.data.Device;
  29 +import org.thingsboard.server.common.data.TransportPayloadType;
  30 +import org.thingsboard.server.common.data.device.profile.MqttTopics;
  31 +import org.thingsboard.server.common.data.id.DeviceId;
  32 +import org.thingsboard.server.mqtt.AbstractMqttIntegrationTest;
  33 +
  34 +import java.util.Arrays;
  35 +import java.util.HashSet;
  36 +import java.util.List;
  37 +import java.util.Map;
  38 +import java.util.Set;
  39 +import java.util.concurrent.CountDownLatch;
  40 +import java.util.concurrent.TimeUnit;
  41 +
  42 +import static org.junit.Assert.assertEquals;
  43 +import static org.junit.Assert.assertNotNull;
  44 +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
  45 +
  46 +@Slf4j
  47 +public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqttIntegrationTest {
  48 +
  49 + protected static final String PAYLOAD_VALUES_STR = "{\"key1\":\"value1\", \"key2\":true, \"key3\": 3.0, \"key4\": 4," +
  50 + " \"key5\": {\"someNumber\": 42, \"someArray\": [1,2,3], \"someNestedObject\": {\"key\": \"value\"}}}";
  51 +
  52 + @Before
  53 + public void beforeTest() throws Exception {
  54 + processBeforeTest("Test Post Telemetry device", "Test Post Telemetry gateway", null, null, null);
  55 + }
  56 +
  57 + @After
  58 + public void afterTest() throws Exception {
  59 + processAfterTest();
  60 + }
  61 +
  62 + @Test
  63 + public void testPushMqttTelemetry() throws Exception {
  64 + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");
  65 + processTelemetryTest(MqttTopics.DEVICE_TELEMETRY_TOPIC, expectedKeys, PAYLOAD_VALUES_STR.getBytes(), false);
  66 + }
  67 +
  68 + @Test
  69 + public void testPushMqttTelemetryWithTs() throws Exception {
  70 + String payloadStr = "{\"ts\": 10000, \"values\": " + PAYLOAD_VALUES_STR + "}";
  71 + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");
  72 + processTelemetryTest(MqttTopics.DEVICE_TELEMETRY_TOPIC, expectedKeys, payloadStr.getBytes(), true);
  73 + }
  74 +
  75 + @Test
  76 + public void testPushMqttTelemetryGateway() throws Exception {
  77 + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");
  78 + String deviceName1 = "Device A";
  79 + String deviceName2 = "Device B";
  80 + String payload = getGatewayTelemetryJsonPayload(deviceName1, deviceName2, "10000", "20000");
  81 + processGatewayTelemetryTest(MqttTopics.GATEWAY_TELEMETRY_TOPIC, expectedKeys, payload.getBytes(), deviceName1, deviceName2);
  82 + }
  83 +
  84 + @Test
  85 + public void testGatewayConnect() throws Exception {
  86 + String payload = "{\"device\":\"Device A\"}";
  87 + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
  88 + publishMqttMsg(client, payload.getBytes(), MqttTopics.GATEWAY_CONNECT_TOPIC);
  89 +
  90 + Thread.sleep(2000);
  91 +
  92 + String deviceName = "Device A";
  93 + Device device = doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class);
  94 + assertNotNull(device);
  95 + }
  96 +
  97 + protected void processTelemetryTest(String topic, List<String> expectedKeys, byte[] payload, boolean withTs) throws Exception {
  98 + MqttAsyncClient client = getMqttAsyncClient(accessToken);
  99 + publishMqttMsg(client, payload, topic);
  100 +
  101 + String deviceId = savedDevice.getId().getId().toString();
  102 +
  103 + long start = System.currentTimeMillis();
  104 + long end = System.currentTimeMillis() + 2000;
  105 +
  106 + List<String> actualKeys = null;
  107 + while (start <= end) {
  108 + actualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + deviceId + "/keys/timeseries", List.class);
  109 + if (actualKeys.size() == expectedKeys.size()) {
  110 + break;
  111 + }
  112 + Thread.sleep(100);
  113 + start += 100;
  114 + }
  115 + assertNotNull(actualKeys);
  116 +
  117 + Set<String> actualKeySet = new HashSet<>(actualKeys);
  118 + Set<String> expectedKeySet = new HashSet<>(expectedKeys);
  119 +
  120 + assertEquals(expectedKeySet, actualKeySet);
  121 +
  122 + String getTelemetryValuesUrl;
  123 + if (withTs) {
  124 + getTelemetryValuesUrl = "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/timeseries?startTs=0&endTs=15000&keys=" + String.join(",", actualKeySet);
  125 + } else {
  126 + getTelemetryValuesUrl = "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/timeseries?keys=" + String.join(",", actualKeySet);
  127 + }
  128 + Map<String, List<Map<String, String>>> values = doGetAsync(getTelemetryValuesUrl, Map.class);
  129 +
  130 + if (withTs) {
  131 + assertTs(values, expectedKeys, 10000, 0);
  132 + }
  133 + assertValues(values, 0);
  134 + }
  135 +
  136 + protected void processGatewayTelemetryTest(String topic, List<String> expectedKeys, byte[] payload, String firstDeviceName, String secondDeviceName) throws Exception {
  137 + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
  138 +
  139 + publishMqttMsg(client, payload, topic);
  140 +
  141 + Thread.sleep(2000);
  142 +
  143 + Device firstDevice = doGet("/api/tenant/devices?deviceName=" + firstDeviceName, Device.class);
  144 + assertNotNull(firstDevice);
  145 + Device secondDevice = doGet("/api/tenant/devices?deviceName=" + secondDeviceName, Device.class);
  146 + assertNotNull(secondDevice);
  147 +
  148 + List<String> firstDeviceActualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + firstDevice.getId() + "/keys/timeseries", List.class);
  149 + Set<String> firstDeviceActualKeySet = new HashSet<>(firstDeviceActualKeys);
  150 +
  151 + List<String> secondDeviceActualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + secondDevice.getId() + "/keys/timeseries", List.class);
  152 + Set<String> secondDeviceActualKeySet = new HashSet<>(secondDeviceActualKeys);
  153 +
  154 + Set<String> expectedKeySet = new HashSet<>(expectedKeys);
  155 +
  156 + assertEquals(expectedKeySet, firstDeviceActualKeySet);
  157 + assertEquals(expectedKeySet, secondDeviceActualKeySet);
  158 +
  159 + String getTelemetryValuesUrlFirstDevice = getTelemetryValuesUrl(firstDevice.getId(), firstDeviceActualKeySet);
  160 + String getTelemetryValuesUrlSecondDevice = getTelemetryValuesUrl(firstDevice.getId(), secondDeviceActualKeySet);
  161 +
  162 + Map<String, List<Map<String, String>>> firstDeviceValues = doGetAsync(getTelemetryValuesUrlFirstDevice, Map.class);
  163 + Map<String, List<Map<String, String>>> secondDeviceValues = doGetAsync(getTelemetryValuesUrlSecondDevice, Map.class);
  164 +
  165 + assertGatewayDeviceData(firstDeviceValues, expectedKeys);
  166 + assertGatewayDeviceData(secondDeviceValues, expectedKeys);
  167 + }
  168 +
  169 + protected String getGatewayTelemetryJsonPayload(String deviceA, String deviceB, String firstTsValue, String secondTsValue) {
  170 + String payload = "[{\"ts\": " + firstTsValue + ", \"values\": " + PAYLOAD_VALUES_STR + "}, " +
  171 + "{\"ts\": " + secondTsValue + ", \"values\": " + PAYLOAD_VALUES_STR + "}]";
  172 + return "{\"" + deviceA + "\": " + payload + ", \"" + deviceB + "\": " + payload + "}";
  173 + }
  174 +
  175 + private String getTelemetryValuesUrl(DeviceId deviceId, Set<String> actualKeySet) {
  176 + return "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/timeseries?startTs=0&endTs=25000&keys=" + String.join(",", actualKeySet);
  177 + }
  178 +
  179 + private void assertGatewayDeviceData(Map<String, List<Map<String, String>>> deviceValues, List<String> expectedKeys) {
  180 +
  181 + assertEquals(2, deviceValues.get(expectedKeys.get(0)).size());
  182 + assertEquals(2, deviceValues.get(expectedKeys.get(1)).size());
  183 + assertEquals(2, deviceValues.get(expectedKeys.get(2)).size());
  184 + assertEquals(2, deviceValues.get(expectedKeys.get(3)).size());
  185 + assertEquals(2, deviceValues.get(expectedKeys.get(4)).size());
  186 +
  187 + assertTs(deviceValues, expectedKeys, 20000, 0);
  188 + assertTs(deviceValues, expectedKeys, 10000, 1);
  189 +
  190 + assertValues(deviceValues, 0);
  191 + assertValues(deviceValues, 1);
  192 +
  193 + }
  194 +
  195 + private void assertValues(Map<String, List<Map<String, String>>> deviceValues, int arrayIndex) {
  196 + for (Map.Entry<String, List<Map<String, String>>> entry : deviceValues.entrySet()) {
  197 + String key = entry.getKey();
  198 + List<Map<String, String>> tsKv = entry.getValue();
  199 + String value = tsKv.get(arrayIndex).get("value");
  200 + switch (key) {
  201 + case "key1":
  202 + assertEquals("value1", value);
  203 + break;
  204 + case "key2":
  205 + assertEquals("true", value);
  206 + break;
  207 + case "key3":
  208 + assertEquals("3.0", value);
  209 + break;
  210 + case "key4":
  211 + assertEquals("4", value);
  212 + break;
  213 + case "key5":
  214 + assertEquals("{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}", value);
  215 + break;
  216 + }
  217 + }
  218 + }
  219 +
  220 + private void assertTs(Map<String, List<Map<String, String>>> deviceValues, List<String> expectedKeys, int ts, int arrayIndex) {
  221 + assertEquals(ts, deviceValues.get(expectedKeys.get(0)).get(arrayIndex).get("ts"));
  222 + assertEquals(ts, deviceValues.get(expectedKeys.get(1)).get(arrayIndex).get("ts"));
  223 + assertEquals(ts, deviceValues.get(expectedKeys.get(2)).get(arrayIndex).get("ts"));
  224 + assertEquals(ts, deviceValues.get(expectedKeys.get(3)).get(arrayIndex).get("ts"));
  225 + assertEquals(ts, deviceValues.get(expectedKeys.get(4)).get(arrayIndex).get("ts"));
  226 + }
  227 +
  228 + // @Test - Unstable
  229 + public void testMqttQoSLevel() throws Exception {
  230 + String clientId = MqttAsyncClient.generateClientId();
  231 + MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId);
  232 +
  233 + MqttConnectOptions options = new MqttConnectOptions();
  234 + options.setUserName(accessToken);
  235 + CountDownLatch latch = new CountDownLatch(1);
  236 + TestMqttCallback callback = new TestMqttCallback(client, latch);
  237 + client.setCallback(callback);
  238 + client.connect(options).waitForCompletion(5000);
  239 + client.subscribe("v1/devices/me/attributes", MqttQoS.AT_MOST_ONCE.value());
  240 + String payload = "{\"key\":\"uniqueValue\"}";
  241 +// 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.
  242 +// MqttClient -> SUB REQUEST -> Transport -> Kafka -> Device Actor (subscribed)
  243 +// MqttClient <- SUB_ACK <- Transport
  244 + Thread.sleep(5000);
  245 + doPostAsync("/api/plugins/telemetry/" + savedDevice.getId() + "/SHARED_SCOPE", payload, String.class, status().isOk());
  246 + latch.await(10, TimeUnit.SECONDS);
  247 + assertEquals(payload, callback.getPayload());
  248 + assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS());
  249 + }
  250 +
  251 + private static class TestMqttCallback implements MqttCallback {
  252 +
  253 + private final MqttAsyncClient client;
  254 + private final CountDownLatch latch;
  255 + private volatile Integer qoS;
  256 + private volatile String payload;
  257 +
  258 + String getPayload() {
  259 + return payload;
  260 + }
  261 +
  262 + TestMqttCallback(MqttAsyncClient client, CountDownLatch latch) {
  263 + this.client = client;
  264 + this.latch = latch;
  265 + }
  266 +
  267 + int getQoS() {
  268 + return qoS;
  269 + }
  270 +
  271 + @Override
  272 + public void connectionLost(Throwable throwable) {
  273 + log.error("Client connection lost", throwable);
  274 + }
  275 +
  276 + @Override
  277 + public void messageArrived(String requestTopic, MqttMessage mqttMessage) {
  278 + payload = new String(mqttMessage.getPayload());
  279 + qoS = mqttMessage.getQos();
  280 + latch.countDown();
  281 + }
  282 +
  283 + @Override
  284 + public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
  285 +
  286 + }
  287 + }
  288 +
  289 +
  290 +}
  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 + Thread.sleep(2000);
  77 +
  78 + String deviceName = "Device A";
  79 + Device device = doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class);
  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 + Thread.sleep(2000);
  85 +
  86 + Device device = doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class);
  87 + assertNotNull(device);
  88 + }
  89 +
  90 + private TransportApiProtos.ConnectMsg getConnectProto(String deviceName) {
  91 + TransportApiProtos.ConnectMsg.Builder builder = TransportApiProtos.ConnectMsg.newBuilder();
  92 + builder.setDeviceName(deviceName);
  93 + builder.setDeviceType(TransportPayloadType.PROTOBUF.name());
  94 + return builder.build();
  95 + }
  96 +
  97 + private TransportApiProtos.TelemetryMsg getDeviceTelemetryMsgProto(String deviceName, List<String> expectedKeys, long firstTs, long secondTs) {
  98 + TransportApiProtos.TelemetryMsg.Builder deviceTelemetryMsgBuilder = TransportApiProtos.TelemetryMsg.newBuilder();
  99 + TransportProtos.TsKvListProto tsKvListProto1 = getTsKvListProto(expectedKeys, firstTs);
  100 + TransportProtos.TsKvListProto tsKvListProto2 = getTsKvListProto(expectedKeys, secondTs);
  101 + TransportProtos.PostTelemetryMsg.Builder msg = TransportProtos.PostTelemetryMsg.newBuilder();
  102 + msg.addAllTsKvList(Arrays.asList(tsKvListProto1, tsKvListProto2));
  103 + deviceTelemetryMsgBuilder.setDeviceName(deviceName);
  104 + deviceTelemetryMsgBuilder.setMsg(msg);
  105 + return deviceTelemetryMsgBuilder.build();
  106 + }
  107 +
  108 + private TransportProtos.TsKvListProto getTsKvListProto(List<String> expectedKeys, long ts) {
  109 + List<TransportProtos.KeyValueProto> kvProtos = getKvProtos(expectedKeys);
  110 + TransportProtos.TsKvListProto.Builder builder = TransportProtos.TsKvListProto.newBuilder();
  111 + builder.addAllKv(kvProtos);
  112 + builder.setTs(ts);
  113 + return builder.build();
  114 + }
  115 +}
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
  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 +}
@@ -16,11 +16,14 @@ @@ -16,11 +16,14 @@
16 package org.thingsboard.server.common.data.device.profile; 16 package org.thingsboard.server.common.data.device.profile;
17 17
18 import lombok.Data; 18 import lombok.Data;
  19 +import org.thingsboard.server.common.data.TransportPayloadType;
19 import org.thingsboard.server.common.data.DeviceTransportType; 20 import org.thingsboard.server.common.data.DeviceTransportType;
20 21
21 @Data 22 @Data
22 public class MqttDeviceProfileTransportConfiguration implements DeviceProfileTransportConfiguration { 23 public class MqttDeviceProfileTransportConfiguration implements DeviceProfileTransportConfiguration {
23 24
  25 + private TransportPayloadType transportPayloadType = TransportPayloadType.JSON;
  26 +
24 private String deviceTelemetryTopic = MqttTopics.DEVICE_TELEMETRY_TOPIC; 27 private String deviceTelemetryTopic = MqttTopics.DEVICE_TELEMETRY_TOPIC;
25 private String deviceAttributesTopic = MqttTopics.DEVICE_ATTRIBUTES_TOPIC; 28 private String deviceAttributesTopic = MqttTopics.DEVICE_ATTRIBUTES_TOPIC;
26 29
@@ -20,32 +20,57 @@ package org.thingsboard.server.common.data.device.profile; @@ -20,32 +20,57 @@ package org.thingsboard.server.common.data.device.profile;
20 */ 20 */
21 public class MqttTopics { 21 public class MqttTopics {
22 22
  23 + private static final String REQUEST = "/request";
  24 + private static final String RESPONSE = "/response";
  25 + private static final String RPC = "/rpc";
  26 + private static final String CONNECT = "/connect";
  27 + private static final String DISCONNECT = "/disconnect";
  28 + private static final String TELEMETRY = "/telemetry";
  29 + private static final String ATTRIBUTES = "/attributes";
  30 + private static final String CLAIM = "/claim";
  31 + private static final String SUB_TOPIC = "+";
  32 + private static final String PROVISION = "/provision";
  33 +
  34 + private static final String ATTRIBUTES_RESPONSE = ATTRIBUTES + RESPONSE;
  35 + private static final String ATTRIBUTES_REQUEST = ATTRIBUTES + REQUEST;
  36 +
  37 + private static final String DEVICE_RPC_RESPONSE = RPC + RESPONSE;
  38 + private static final String DEVICE_RPC_REQUEST = RPC + REQUEST;
  39 +
  40 + private static final String DEVICE_ATTRIBUTES_RESPONSE = ATTRIBUTES_RESPONSE + "/";
  41 + private static final String DEVICE_ATTRIBUTES_REQUEST = ATTRIBUTES_REQUEST + "/";
  42 +
  43 + private static final String PROVISION_RESPONSE = PROVISION + RESPONSE;
  44 + // V1_JSON topics
  45 +
23 public static final String BASE_DEVICE_API_TOPIC = "v1/devices/me"; 46 public static final String BASE_DEVICE_API_TOPIC = "v1/devices/me";
24 - public static final String DEVICE_RPC_RESPONSE_TOPIC = BASE_DEVICE_API_TOPIC + "/rpc/response/";  
25 - public static final String DEVICE_RPC_RESPONSE_SUB_TOPIC = DEVICE_RPC_RESPONSE_TOPIC + "+";  
26 - public static final String DEVICE_RPC_REQUESTS_TOPIC = BASE_DEVICE_API_TOPIC + "/rpc/request/";  
27 - public static final String DEVICE_RPC_REQUESTS_SUB_TOPIC = DEVICE_RPC_REQUESTS_TOPIC + "+";  
28 - public static final String DEVICE_ATTRIBUTES_RESPONSE_TOPIC_PREFIX = BASE_DEVICE_API_TOPIC + "/attributes/response/";  
29 - public static final String DEVICE_ATTRIBUTES_RESPONSES_TOPIC = DEVICE_ATTRIBUTES_RESPONSE_TOPIC_PREFIX + "+";  
30 - public static final String DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX = BASE_DEVICE_API_TOPIC + "/attributes/request/";  
31 - public static final String DEVICE_TELEMETRY_TOPIC = BASE_DEVICE_API_TOPIC + "/telemetry";  
32 - public static final String DEVICE_CLAIM_TOPIC = BASE_DEVICE_API_TOPIC + "/claim";  
33 - public static final String DEVICE_ATTRIBUTES_TOPIC = BASE_DEVICE_API_TOPIC + "/attributes";  
34 - public static final String DEVICE_PROVISION_REQUEST_TOPIC = BASE_DEVICE_API_TOPIC + "/provision";  
35 - public static final String DEVICE_PROVISION_RESPONSE_TOPIC = BASE_DEVICE_API_TOPIC + "/provision/response";  
36 47
37 - public static final String BASE_GATEWAY_API_TOPIC = "v1/gateway";  
38 - public static final String GATEWAY_CONNECT_TOPIC = BASE_GATEWAY_API_TOPIC + "/connect";  
39 - public static final String GATEWAY_DISCONNECT_TOPIC = BASE_GATEWAY_API_TOPIC + "/disconnect";  
40 - public static final String GATEWAY_ATTRIBUTES_TOPIC = BASE_GATEWAY_API_TOPIC + "/attributes";  
41 - public static final String GATEWAY_TELEMETRY_TOPIC = BASE_GATEWAY_API_TOPIC + "/telemetry";  
42 - public static final String GATEWAY_CLAIM_TOPIC = BASE_GATEWAY_API_TOPIC + "/claim";  
43 - public static final String GATEWAY_RPC_TOPIC = BASE_GATEWAY_API_TOPIC + "/rpc";  
44 - public static final String GATEWAY_ATTRIBUTES_REQUEST_TOPIC = BASE_GATEWAY_API_TOPIC + "/attributes/request";  
45 - public static final String GATEWAY_ATTRIBUTES_RESPONSE_TOPIC = BASE_GATEWAY_API_TOPIC + "/attributes/response";  
46 - public static final String GATEWAY_PROVISION_REQUEST_TOPIC = BASE_GATEWAY_API_TOPIC + "/provision";  
47 - public static final String GATEWAY_PROVISION_RESPONSE_TOPIC = BASE_GATEWAY_API_TOPIC + "/provision/response"; 48 + public static final String DEVICE_RPC_RESPONSE_TOPIC = BASE_DEVICE_API_TOPIC + DEVICE_RPC_RESPONSE;
  49 + public static final String DEVICE_RPC_RESPONSE_SUB_TOPIC = DEVICE_RPC_RESPONSE_TOPIC + SUB_TOPIC;
  50 + public static final String DEVICE_RPC_REQUESTS_TOPIC = BASE_DEVICE_API_TOPIC + DEVICE_RPC_REQUEST;
  51 + public static final String DEVICE_RPC_REQUESTS_SUB_TOPIC = DEVICE_RPC_REQUESTS_TOPIC + SUB_TOPIC;
  52 + public static final String DEVICE_ATTRIBUTES_RESPONSE_TOPIC_PREFIX = BASE_DEVICE_API_TOPIC + DEVICE_ATTRIBUTES_RESPONSE;
  53 + public static final String DEVICE_ATTRIBUTES_RESPONSES_TOPIC = DEVICE_ATTRIBUTES_RESPONSE_TOPIC_PREFIX + SUB_TOPIC;
  54 + public static final String DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX = BASE_DEVICE_API_TOPIC + DEVICE_ATTRIBUTES_REQUEST;
  55 + public static final String DEVICE_TELEMETRY_TOPIC = BASE_DEVICE_API_TOPIC + TELEMETRY;
  56 + public static final String DEVICE_CLAIM_TOPIC = BASE_DEVICE_API_TOPIC + CLAIM;
  57 + public static final String DEVICE_ATTRIBUTES_TOPIC = BASE_DEVICE_API_TOPIC + ATTRIBUTES;
  58 + public static final String DEVICE_PROVISION_REQUEST_TOPIC = BASE_DEVICE_API_TOPIC + PROVISION;
  59 + public static final String DEVICE_PROVISION_RESPONSE_TOPIC = BASE_DEVICE_API_TOPIC + PROVISION_RESPONSE;
48 60
  61 + // V1_JSON gateway topics
  62 +
  63 + public static final String BASE_GATEWAY_API_TOPIC = "v1/gateway";
  64 + public static final String GATEWAY_CONNECT_TOPIC = BASE_GATEWAY_API_TOPIC + CONNECT;
  65 + public static final String GATEWAY_DISCONNECT_TOPIC = BASE_GATEWAY_API_TOPIC + DISCONNECT;
  66 + public static final String GATEWAY_ATTRIBUTES_TOPIC = BASE_GATEWAY_API_TOPIC + ATTRIBUTES;
  67 + public static final String GATEWAY_TELEMETRY_TOPIC = BASE_GATEWAY_API_TOPIC + TELEMETRY;
  68 + public static final String GATEWAY_CLAIM_TOPIC = BASE_GATEWAY_API_TOPIC + CLAIM;
  69 + public static final String GATEWAY_RPC_TOPIC = BASE_GATEWAY_API_TOPIC + RPC;
  70 + public static final String GATEWAY_ATTRIBUTES_REQUEST_TOPIC = BASE_GATEWAY_API_TOPIC + ATTRIBUTES_REQUEST;
  71 + public static final String GATEWAY_ATTRIBUTES_RESPONSE_TOPIC = BASE_GATEWAY_API_TOPIC + ATTRIBUTES_RESPONSE;
  72 + public static final String GATEWAY_PROVISION_REQUEST_TOPIC = BASE_GATEWAY_API_TOPIC + PROVISION;
  73 + public static final String GATEWAY_PROVISION_RESPONSE_TOPIC = BASE_GATEWAY_API_TOPIC + PROVISION_RESPONSE;
49 74
50 private MqttTopics() { 75 private MqttTopics() {
51 } 76 }
@@ -20,6 +20,7 @@ import lombok.Data; @@ -20,6 +20,7 @@ import lombok.Data;
20 import lombok.NoArgsConstructor; 20 import lombok.NoArgsConstructor;
21 21
22 import java.io.Serializable; 22 import java.io.Serializable;
  23 +import java.util.Collections;
23 import java.util.HashMap; 24 import java.util.HashMap;
24 import java.util.Map; 25 import java.util.Map;
25 import java.util.concurrent.ConcurrentHashMap; 26 import java.util.concurrent.ConcurrentHashMap;
@@ -31,6 +32,8 @@ import java.util.concurrent.ConcurrentHashMap; @@ -31,6 +32,8 @@ import java.util.concurrent.ConcurrentHashMap;
31 @NoArgsConstructor 32 @NoArgsConstructor
32 public final class TbMsgMetaData implements Serializable { 33 public final class TbMsgMetaData implements Serializable {
33 34
  35 + public static final TbMsgMetaData EMPTY = new TbMsgMetaData(Collections.emptyMap());
  36 +
34 private final Map<String, String> data = new ConcurrentHashMap<>(); 37 private final Map<String, String> data = new ConcurrentHashMap<>();
35 38
36 public TbMsgMetaData(Map<String, String> data) { 39 public TbMsgMetaData(Map<String, String> data) {
@@ -24,7 +24,8 @@ import org.springframework.beans.factory.annotation.Value; @@ -24,7 +24,8 @@ import org.springframework.beans.factory.annotation.Value;
24 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; 24 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
25 import org.springframework.stereotype.Component; 25 import org.springframework.stereotype.Component;
26 import org.thingsboard.server.common.transport.TransportContext; 26 import org.thingsboard.server.common.transport.TransportContext;
27 -import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor; 27 +import org.thingsboard.server.transport.mqtt.adaptors.JsonMqttAdaptor;
  28 +import org.thingsboard.server.transport.mqtt.adaptors.ProtoMqttAdaptor;
28 29
29 /** 30 /**
30 * Created by ashvayka on 04.10.18. 31 * Created by ashvayka on 04.10.18.
@@ -40,7 +41,11 @@ public class MqttTransportContext extends TransportContext { @@ -40,7 +41,11 @@ public class MqttTransportContext extends TransportContext {
40 41
41 @Getter 42 @Getter
42 @Autowired 43 @Autowired
43 - private MqttTransportAdaptor adaptor; 44 + private JsonMqttAdaptor jsonMqttAdaptor;
  45 +
  46 + @Getter
  47 + @Autowired
  48 + private ProtoMqttAdaptor protoMqttAdaptor;
44 49
45 @Getter 50 @Getter
46 @Value("${transport.mqtt.netty.max_payload_size}") 51 @Value("${transport.mqtt.netty.max_payload_size}")
@@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
16 package org.thingsboard.server.transport.mqtt; 16 package org.thingsboard.server.transport.mqtt;
17 17
18 import com.fasterxml.jackson.databind.JsonNode; 18 import com.fasterxml.jackson.databind.JsonNode;
  19 +import com.google.gson.JsonSyntaxException;
19 import io.netty.channel.ChannelHandlerContext; 20 import io.netty.channel.ChannelHandlerContext;
20 import io.netty.channel.ChannelInboundHandlerAdapter; 21 import io.netty.channel.ChannelInboundHandlerAdapter;
21 import io.netty.handler.codec.mqtt.MqttConnAckMessage; 22 import io.netty.handler.codec.mqtt.MqttConnAckMessage;
@@ -34,7 +35,6 @@ import io.netty.handler.codec.mqtt.MqttSubscribeMessage; @@ -34,7 +35,6 @@ import io.netty.handler.codec.mqtt.MqttSubscribeMessage;
34 import io.netty.handler.codec.mqtt.MqttTopicSubscription; 35 import io.netty.handler.codec.mqtt.MqttTopicSubscription;
35 import io.netty.handler.codec.mqtt.MqttUnsubscribeMessage; 36 import io.netty.handler.codec.mqtt.MqttUnsubscribeMessage;
36 import io.netty.handler.ssl.SslHandler; 37 import io.netty.handler.ssl.SslHandler;
37 -import io.netty.util.CharsetUtil;  
38 import io.netty.util.ReferenceCountUtil; 38 import io.netty.util.ReferenceCountUtil;
39 import io.netty.util.concurrent.Future; 39 import io.netty.util.concurrent.Future;
40 import io.netty.util.concurrent.GenericFutureListener; 40 import io.netty.util.concurrent.GenericFutureListener;
@@ -53,7 +53,6 @@ import org.thingsboard.server.common.transport.auth.TransportDeviceInfo; @@ -53,7 +53,6 @@ import org.thingsboard.server.common.transport.auth.TransportDeviceInfo;
53 import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; 53 import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
54 import org.thingsboard.server.common.transport.service.DefaultTransportService; 54 import org.thingsboard.server.common.transport.service.DefaultTransportService;
55 import org.thingsboard.server.gen.transport.TransportProtos; 55 import org.thingsboard.server.gen.transport.TransportProtos;
56 -import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto;  
57 import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsg; 56 import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsg;
58 import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent; 57 import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent;
59 import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg; 58 import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg;
@@ -95,7 +94,6 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -95,7 +94,6 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
95 94
96 private final UUID sessionId; 95 private final UUID sessionId;
97 private final MqttTransportContext context; 96 private final MqttTransportContext context;
98 - private final MqttTransportAdaptor adaptor;  
99 private final TransportService transportService; 97 private final TransportService transportService;
100 private final SslHandler sslHandler; 98 private final SslHandler sslHandler;
101 private final ConcurrentMap<MqttTopicMatcher, Integer> mqttQoSMap; 99 private final ConcurrentMap<MqttTopicMatcher, Integer> mqttQoSMap;
@@ -108,10 +106,9 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -108,10 +106,9 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
108 this.sessionId = UUID.randomUUID(); 106 this.sessionId = UUID.randomUUID();
109 this.context = context; 107 this.context = context;
110 this.transportService = context.getTransportService(); 108 this.transportService = context.getTransportService();
111 - this.adaptor = context.getAdaptor();  
112 this.sslHandler = sslHandler; 109 this.sslHandler = sslHandler;
113 this.mqttQoSMap = new ConcurrentHashMap<>(); 110 this.mqttQoSMap = new ConcurrentHashMap<>();
114 - this.deviceSessionCtx = new DeviceSessionCtx(sessionId, mqttQoSMap); 111 + this.deviceSessionCtx = new DeviceSessionCtx(sessionId, mqttQoSMap, context);
115 } 112 }
116 113
117 @Override 114 @Override
@@ -153,9 +150,19 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -153,9 +150,19 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
153 int msgId = mqttMsg.variableHeader().packetId(); 150 int msgId = mqttMsg.variableHeader().packetId();
154 try { 151 try {
155 if (topicName.equals(MqttTopics.DEVICE_PROVISION_REQUEST_TOPIC)) { 152 if (topicName.equals(MqttTopics.DEVICE_PROVISION_REQUEST_TOPIC)) {
156 - TransportProtos.ProvisionDeviceRequestMsg provisionRequestMsg = adaptor.convertToProvisionRequestMsg(deviceSessionCtx, mqttMsg);  
157 - transportService.process(provisionRequestMsg, new DeviceProvisionCallback(ctx, msgId, provisionRequestMsg));  
158 - log.trace("[{}][{}] Processing publish msg [{}][{}]!", sessionId, deviceSessionCtx.getDeviceId(), topicName, msgId); 153 + try {
  154 + TransportProtos.ProvisionDeviceRequestMsg provisionRequestMsg = deviceSessionCtx.getContext().getJsonMqttAdaptor().convertToProvisionRequestMsg(deviceSessionCtx, mqttMsg);
  155 + transportService.process(provisionRequestMsg, new DeviceProvisionCallback(ctx, msgId, provisionRequestMsg));
  156 + log.trace("[{}][{}] Processing provision publish msg [{}][{}]!", sessionId, deviceSessionCtx.getDeviceId(), topicName, msgId);
  157 + } catch (Exception e) {
  158 + if (e.getCause().toString().contains("JsonSyntaxException")) {
  159 + TransportProtos.ProvisionDeviceRequestMsg provisionRequestMsg = deviceSessionCtx.getContext().getProtoMqttAdaptor().convertToProvisionRequestMsg(deviceSessionCtx, mqttMsg);
  160 + transportService.process(provisionRequestMsg, new DeviceProvisionCallback(ctx, msgId, provisionRequestMsg));
  161 + log.trace("[{}][{}] Processing provision publish msg [{}][{}]!", sessionId, deviceSessionCtx.getDeviceId(), topicName, msgId);
  162 + } else {
  163 + throw e;
  164 + }
  165 + }
159 } else { 166 } else {
160 throw new RuntimeException("Unsupported topic for provisioning requests!"); 167 throw new RuntimeException("Unsupported topic for provisioning requests!");
161 } 168 }
@@ -250,23 +257,24 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -250,23 +257,24 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
250 257
251 private void processDevicePublish(ChannelHandlerContext ctx, MqttPublishMessage mqttMsg, String topicName, int msgId) { 258 private void processDevicePublish(ChannelHandlerContext ctx, MqttPublishMessage mqttMsg, String topicName, int msgId) {
252 try { 259 try {
  260 + MqttTransportAdaptor payloadAdaptor = deviceSessionCtx.getPayloadAdaptor();
253 if (deviceSessionCtx.isDeviceTelemetryTopic(topicName)) { 261 if (deviceSessionCtx.isDeviceTelemetryTopic(topicName)) {
254 - TransportProtos.PostTelemetryMsg postTelemetryMsg = adaptor.convertToPostTelemetry(deviceSessionCtx, mqttMsg); 262 + TransportProtos.PostTelemetryMsg postTelemetryMsg = payloadAdaptor.convertToPostTelemetry(deviceSessionCtx, mqttMsg);
255 transportService.process(deviceSessionCtx.getSessionInfo(), postTelemetryMsg, getPubAckCallback(ctx, msgId, postTelemetryMsg)); 263 transportService.process(deviceSessionCtx.getSessionInfo(), postTelemetryMsg, getPubAckCallback(ctx, msgId, postTelemetryMsg));
256 } else if (deviceSessionCtx.isDeviceAttributesTopic(topicName)) { 264 } else if (deviceSessionCtx.isDeviceAttributesTopic(topicName)) {
257 - TransportProtos.PostAttributeMsg postAttributeMsg = adaptor.convertToPostAttributes(deviceSessionCtx, mqttMsg); 265 + TransportProtos.PostAttributeMsg postAttributeMsg = payloadAdaptor.convertToPostAttributes(deviceSessionCtx, mqttMsg);
258 transportService.process(deviceSessionCtx.getSessionInfo(), postAttributeMsg, getPubAckCallback(ctx, msgId, postAttributeMsg)); 266 transportService.process(deviceSessionCtx.getSessionInfo(), postAttributeMsg, getPubAckCallback(ctx, msgId, postAttributeMsg));
259 } else if (topicName.startsWith(MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX)) { 267 } else if (topicName.startsWith(MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX)) {
260 - TransportProtos.GetAttributeRequestMsg getAttributeMsg = adaptor.convertToGetAttributes(deviceSessionCtx, mqttMsg); 268 + TransportProtos.GetAttributeRequestMsg getAttributeMsg = payloadAdaptor.convertToGetAttributes(deviceSessionCtx, mqttMsg);
261 transportService.process(deviceSessionCtx.getSessionInfo(), getAttributeMsg, getPubAckCallback(ctx, msgId, getAttributeMsg)); 269 transportService.process(deviceSessionCtx.getSessionInfo(), getAttributeMsg, getPubAckCallback(ctx, msgId, getAttributeMsg));
262 } else if (topicName.startsWith(MqttTopics.DEVICE_RPC_RESPONSE_TOPIC)) { 270 } else if (topicName.startsWith(MqttTopics.DEVICE_RPC_RESPONSE_TOPIC)) {
263 - TransportProtos.ToDeviceRpcResponseMsg rpcResponseMsg = adaptor.convertToDeviceRpcResponse(deviceSessionCtx, mqttMsg); 271 + TransportProtos.ToDeviceRpcResponseMsg rpcResponseMsg = payloadAdaptor.convertToDeviceRpcResponse(deviceSessionCtx, mqttMsg);
264 transportService.process(deviceSessionCtx.getSessionInfo(), rpcResponseMsg, getPubAckCallback(ctx, msgId, rpcResponseMsg)); 272 transportService.process(deviceSessionCtx.getSessionInfo(), rpcResponseMsg, getPubAckCallback(ctx, msgId, rpcResponseMsg));
265 } else if (topicName.startsWith(MqttTopics.DEVICE_RPC_REQUESTS_TOPIC)) { 273 } else if (topicName.startsWith(MqttTopics.DEVICE_RPC_REQUESTS_TOPIC)) {
266 - TransportProtos.ToServerRpcRequestMsg rpcRequestMsg = adaptor.convertToServerRpcRequest(deviceSessionCtx, mqttMsg); 274 + TransportProtos.ToServerRpcRequestMsg rpcRequestMsg = payloadAdaptor.convertToServerRpcRequest(deviceSessionCtx, mqttMsg);
267 transportService.process(deviceSessionCtx.getSessionInfo(), rpcRequestMsg, getPubAckCallback(ctx, msgId, rpcRequestMsg)); 275 transportService.process(deviceSessionCtx.getSessionInfo(), rpcRequestMsg, getPubAckCallback(ctx, msgId, rpcRequestMsg));
268 } else if (topicName.equals(MqttTopics.DEVICE_CLAIM_TOPIC)) { 276 } else if (topicName.equals(MqttTopics.DEVICE_CLAIM_TOPIC)) {
269 - TransportProtos.ClaimDeviceMsg claimDeviceMsg = adaptor.convertToClaimDevice(deviceSessionCtx, mqttMsg); 277 + TransportProtos.ClaimDeviceMsg claimDeviceMsg = payloadAdaptor.convertToClaimDevice(deviceSessionCtx, mqttMsg);
270 transportService.process(deviceSessionCtx.getSessionInfo(), claimDeviceMsg, getPubAckCallback(ctx, msgId, claimDeviceMsg)); 278 transportService.process(deviceSessionCtx.getSessionInfo(), claimDeviceMsg, getPubAckCallback(ctx, msgId, claimDeviceMsg));
271 } else { 279 } else {
272 transportService.reportActivity(deviceSessionCtx.getSessionInfo()); 280 transportService.reportActivity(deviceSessionCtx.getSessionInfo());
@@ -279,7 +287,6 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -279,7 +287,6 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
279 } 287 }
280 288
281 289
282 -  
283 private <T> TransportServiceCallback<Void> getPubAckCallback(final ChannelHandlerContext ctx, final int msgId, final T msg) { 290 private <T> TransportServiceCallback<Void> getPubAckCallback(final ChannelHandlerContext ctx, final int msgId, final T msg) {
284 return new TransportServiceCallback<Void>() { 291 return new TransportServiceCallback<Void>() {
285 @Override 292 @Override
@@ -316,7 +323,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -316,7 +323,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
316 ctx.writeAndFlush(createMqttPubAckMsg(msgId)); 323 ctx.writeAndFlush(createMqttPubAckMsg(msgId));
317 } 324 }
318 try { 325 try {
319 - adaptor.convertToPublish(deviceSessionCtx, provisionResponseMsg).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush); 326 + deviceSessionCtx.getPayloadAdaptor().convertToPublish(deviceSessionCtx, provisionResponseMsg).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush);
320 } catch (Exception e) { 327 } catch (Exception e) {
321 log.trace("[{}] Failed to convert device attributes response to MQTT msg", sessionId, e); 328 log.trace("[{}] Failed to convert device attributes response to MQTT msg", sessionId, e);
322 } 329 }
@@ -354,10 +361,10 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -354,10 +361,10 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
354 break; 361 break;
355 } 362 }
356 case MqttTopics.DEVICE_RPC_RESPONSE_SUB_TOPIC: 363 case MqttTopics.DEVICE_RPC_RESPONSE_SUB_TOPIC:
  364 + case MqttTopics.DEVICE_ATTRIBUTES_RESPONSES_TOPIC:
357 case MqttTopics.GATEWAY_ATTRIBUTES_TOPIC: 365 case MqttTopics.GATEWAY_ATTRIBUTES_TOPIC:
358 case MqttTopics.GATEWAY_RPC_TOPIC: 366 case MqttTopics.GATEWAY_RPC_TOPIC:
359 case MqttTopics.GATEWAY_ATTRIBUTES_RESPONSE_TOPIC: 367 case MqttTopics.GATEWAY_ATTRIBUTES_RESPONSE_TOPIC:
360 - case MqttTopics.DEVICE_ATTRIBUTES_RESPONSES_TOPIC:  
361 case MqttTopics.GATEWAY_PROVISION_RESPONSE_TOPIC: 368 case MqttTopics.GATEWAY_PROVISION_RESPONSE_TOPIC:
362 case MqttTopics.DEVICE_PROVISION_RESPONSE_TOPIC: 369 case MqttTopics.DEVICE_PROVISION_RESPONSE_TOPIC:
363 registerSubQoS(topic, grantedQoSList, reqQoS); 370 registerSubQoS(topic, grantedQoSList, reqQoS);
@@ -565,7 +572,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -565,7 +572,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
565 if (infoNode != null) { 572 if (infoNode != null) {
566 JsonNode gatewayNode = infoNode.get("gateway"); 573 JsonNode gatewayNode = infoNode.get("gateway");
567 if (gatewayNode != null && gatewayNode.asBoolean()) { 574 if (gatewayNode != null && gatewayNode.asBoolean()) {
568 - gatewaySessionHandler = new GatewaySessionHandler(context, deviceSessionCtx, sessionId); 575 + gatewaySessionHandler = new GatewaySessionHandler(deviceSessionCtx, sessionId);
569 } 576 }
570 } 577 }
571 } catch (IOException e) { 578 } catch (IOException e) {
@@ -619,7 +626,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -619,7 +626,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
619 @Override 626 @Override
620 public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg response) { 627 public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg response) {
621 try { 628 try {
622 - adaptor.convertToPublish(deviceSessionCtx, response).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush); 629 + deviceSessionCtx.getPayloadAdaptor().convertToPublish(deviceSessionCtx, response).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush);
623 } catch (Exception e) { 630 } catch (Exception e) {
624 log.trace("[{}] Failed to convert device attributes response to MQTT msg", sessionId, e); 631 log.trace("[{}] Failed to convert device attributes response to MQTT msg", sessionId, e);
625 } 632 }
@@ -628,7 +635,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -628,7 +635,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
628 @Override 635 @Override
629 public void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg notification) { 636 public void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg notification) {
630 try { 637 try {
631 - adaptor.convertToPublish(deviceSessionCtx, notification).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush); 638 + deviceSessionCtx.getPayloadAdaptor().convertToPublish(deviceSessionCtx, notification).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush);
632 } catch (Exception e) { 639 } catch (Exception e) {
633 log.trace("[{}] Failed to convert device attributes update to MQTT msg", sessionId, e); 640 log.trace("[{}] Failed to convert device attributes update to MQTT msg", sessionId, e);
634 } 641 }
@@ -644,17 +651,17 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -644,17 +651,17 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
644 public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg rpcRequest) { 651 public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg rpcRequest) {
645 log.trace("[{}] Received RPC command to device", sessionId); 652 log.trace("[{}] Received RPC command to device", sessionId);
646 try { 653 try {
647 - adaptor.convertToPublish(deviceSessionCtx, rpcRequest).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush); 654 + deviceSessionCtx.getPayloadAdaptor().convertToPublish(deviceSessionCtx, rpcRequest).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush);
648 } catch (Exception e) { 655 } catch (Exception e) {
649 - log.trace("[{}] Failed to convert device RPC commandto MQTT msg", sessionId, e); 656 + log.trace("[{}] Failed to convert device RPC command to MQTT msg", sessionId, e);
650 } 657 }
651 } 658 }
652 659
653 @Override 660 @Override
654 public void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg rpcResponse) { 661 public void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg rpcResponse) {
655 - log.trace("[{}] Received RPC command to device", sessionId); 662 + log.trace("[{}] Received RPC command to server", sessionId);
656 try { 663 try {
657 - adaptor.convertToPublish(deviceSessionCtx, rpcResponse).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush); 664 + deviceSessionCtx.getPayloadAdaptor().convertToPublish(deviceSessionCtx, rpcResponse).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush);
658 } catch (Exception e) { 665 } catch (Exception e) {
659 log.trace("[{}] Failed to convert device RPC command to MQTT msg", sessionId, e); 666 log.trace("[{}] Failed to convert device RPC command to MQTT msg", sessionId, e);
660 } 667 }
@@ -38,6 +38,7 @@ import org.thingsboard.server.common.data.device.profile.MqttTopics; @@ -38,6 +38,7 @@ import org.thingsboard.server.common.data.device.profile.MqttTopics;
38 import org.thingsboard.server.transport.mqtt.session.MqttDeviceAwareSessionContext; 38 import org.thingsboard.server.transport.mqtt.session.MqttDeviceAwareSessionContext;
39 39
40 import java.nio.charset.Charset; 40 import java.nio.charset.Charset;
  41 +import java.nio.charset.StandardCharsets;
41 import java.util.Arrays; 42 import java.util.Arrays;
42 import java.util.HashSet; 43 import java.util.HashSet;
43 import java.util.Optional; 44 import java.util.Optional;
@@ -47,12 +48,13 @@ import java.util.UUID; @@ -47,12 +48,13 @@ import java.util.UUID;
47 /** 48 /**
48 * @author Andrew Shvayka 49 * @author Andrew Shvayka
49 */ 50 */
50 -@Component("JsonMqttAdaptor") 51 +@Component
51 @Slf4j 52 @Slf4j
52 public class JsonMqttAdaptor implements MqttTransportAdaptor { 53 public class JsonMqttAdaptor implements MqttTransportAdaptor {
53 54
  55 + protected static final Charset UTF8 = StandardCharsets.UTF_8;
  56 +
54 private static final Gson GSON = new Gson(); 57 private static final Gson GSON = new Gson();
55 - private static final Charset UTF8 = Charset.forName("UTF-8");  
56 private static final ByteBufAllocator ALLOCATOR = new UnpooledByteBufAllocator(false); 58 private static final ByteBufAllocator ALLOCATOR = new UnpooledByteBufAllocator(false);
57 59
58 @Override 60 @Override
@@ -76,106 +78,68 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { @@ -76,106 +78,68 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor {
76 } 78 }
77 79
78 @Override 80 @Override
79 - public TransportProtos.GetAttributeRequestMsg convertToGetAttributes(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException {  
80 - String topicName = inbound.variableHeader().topicName();  
81 - try {  
82 - TransportProtos.GetAttributeRequestMsg.Builder result = TransportProtos.GetAttributeRequestMsg.newBuilder();  
83 - result.setRequestId(Integer.valueOf(topicName.substring(MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX.length())));  
84 - String payload = inbound.payload().toString(UTF8);  
85 - JsonElement requestBody = new JsonParser().parse(payload);  
86 - Set<String> clientKeys = toStringSet(requestBody, "clientKeys");  
87 - Set<String> sharedKeys = toStringSet(requestBody, "sharedKeys");  
88 - if (clientKeys != null) {  
89 - result.addAllClientAttributeNames(clientKeys);  
90 - }  
91 - if (sharedKeys != null) {  
92 - result.addAllSharedAttributeNames(sharedKeys);  
93 - }  
94 - return result.build();  
95 - } catch (RuntimeException e) {  
96 - log.warn("Failed to decode get attributes request", e);  
97 - throw new AdaptorException(e);  
98 - }  
99 - }  
100 -  
101 - @Override  
102 - public TransportProtos.ToDeviceRpcResponseMsg convertToDeviceRpcResponse(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException {  
103 - String topicName = inbound.variableHeader().topicName(); 81 + public TransportProtos.ClaimDeviceMsg convertToClaimDevice(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException {
  82 + String payload = validatePayload(ctx.getSessionId(), inbound.payload(), true);
104 try { 83 try {
105 - Integer requestId = Integer.valueOf(topicName.substring(MqttTopics.DEVICE_RPC_RESPONSE_TOPIC.length()));  
106 - String payload = inbound.payload().toString(UTF8);  
107 - return TransportProtos.ToDeviceRpcResponseMsg.newBuilder().setRequestId(requestId).setPayload(payload).build();  
108 - } catch (RuntimeException e) {  
109 - log.warn("Failed to decode get attributes request", e);  
110 - throw new AdaptorException(e); 84 + return JsonConverter.convertToClaimDeviceProto(ctx.getDeviceId(), payload);
  85 + } catch (IllegalStateException | JsonSyntaxException ex) {
  86 + throw new AdaptorException(ex);
111 } 87 }
112 } 88 }
113 89
114 @Override 90 @Override
115 - public TransportProtos.ToServerRpcRequestMsg convertToServerRpcRequest(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException {  
116 - String topicName = inbound.variableHeader().topicName(); 91 + public TransportProtos.ProvisionDeviceRequestMsg convertToProvisionRequestMsg(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException {
117 String payload = validatePayload(ctx.getSessionId(), inbound.payload(), false); 92 String payload = validatePayload(ctx.getSessionId(), inbound.payload(), false);
118 try { 93 try {
119 - Integer requestId = Integer.valueOf(topicName.substring(MqttTopics.DEVICE_RPC_REQUESTS_TOPIC.length()));  
120 - return JsonConverter.convertToServerRpcRequest(new JsonParser().parse(payload), requestId); 94 + return JsonConverter.convertToProvisionRequestMsg(payload);
121 } catch (IllegalStateException | JsonSyntaxException ex) { 95 } catch (IllegalStateException | JsonSyntaxException ex) {
122 throw new AdaptorException(ex); 96 throw new AdaptorException(ex);
123 } 97 }
124 } 98 }
125 99
126 @Override 100 @Override
127 - public TransportProtos.ClaimDeviceMsg convertToClaimDevice(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException {  
128 - String payload = validatePayload(ctx.getSessionId(), inbound.payload(), true);  
129 - try {  
130 - return JsonConverter.convertToClaimDeviceProto(ctx.getDeviceId(), payload);  
131 - } catch (IllegalStateException | JsonSyntaxException ex) {  
132 - throw new AdaptorException(ex);  
133 - } 101 + public TransportProtos.GetAttributeRequestMsg convertToGetAttributes(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException {
  102 + return processGetAttributeRequestMsg(inbound, MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX);
  103 + }
  104 +
  105 + @Override
  106 + public TransportProtos.ToDeviceRpcResponseMsg convertToDeviceRpcResponse(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException {
  107 + return processToDeviceRpcResponseMsg(inbound, MqttTopics.DEVICE_RPC_RESPONSE_TOPIC);
  108 + }
  109 +
  110 + @Override
  111 + public TransportProtos.ToServerRpcRequestMsg convertToServerRpcRequest(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException {
  112 + return processToServerRpcRequestMsg(ctx, inbound, MqttTopics.DEVICE_RPC_REQUESTS_TOPIC);
134 } 113 }
135 114
136 @Override 115 @Override
137 public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException { 116 public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException {
138 - if (!StringUtils.isEmpty(responseMsg.getError())) {  
139 - throw new AdaptorException(responseMsg.getError());  
140 - } else {  
141 - Integer requestId = responseMsg.getRequestId();  
142 - if (requestId >= 0) {  
143 - return Optional.of(createMqttPublishMsg(ctx,  
144 - MqttTopics.DEVICE_ATTRIBUTES_RESPONSE_TOPIC_PREFIX + requestId,  
145 - JsonConverter.toJson(responseMsg)));  
146 - }  
147 - return Optional.empty();  
148 - } 117 + return processConvertFromAttributeResponseMsg(ctx, responseMsg, MqttTopics.DEVICE_ATTRIBUTES_RESPONSE_TOPIC_PREFIX);
149 } 118 }
150 119
151 @Override 120 @Override
152 public Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException { 121 public Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException {
153 - if (!StringUtils.isEmpty(responseMsg.getError())) {  
154 - throw new AdaptorException(responseMsg.getError());  
155 - } else {  
156 - JsonObject result = JsonConverter.getJsonObjectForGateway(deviceName, responseMsg);  
157 - return Optional.of(createMqttPublishMsg(ctx, MqttTopics.GATEWAY_ATTRIBUTES_RESPONSE_TOPIC, result));  
158 - } 122 + return processConvertFromGatewayAttributeResponseMsg(ctx, deviceName, responseMsg, MqttTopics.GATEWAY_ATTRIBUTES_RESPONSE_TOPIC);
159 } 123 }
160 124
161 @Override 125 @Override
162 - public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.AttributeUpdateNotificationMsg notificationMsg) throws AdaptorException { 126 + public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.AttributeUpdateNotificationMsg notificationMsg) {
163 return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_ATTRIBUTES_TOPIC, JsonConverter.toJson(notificationMsg))); 127 return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_ATTRIBUTES_TOPIC, JsonConverter.toJson(notificationMsg)));
164 } 128 }
165 129
166 @Override 130 @Override
167 - public Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.AttributeUpdateNotificationMsg notificationMsg) throws AdaptorException { 131 + public Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.AttributeUpdateNotificationMsg notificationMsg) {
168 JsonObject result = JsonConverter.getJsonObjectForGateway(deviceName, notificationMsg); 132 JsonObject result = JsonConverter.getJsonObjectForGateway(deviceName, notificationMsg);
169 return Optional.of(createMqttPublishMsg(ctx, MqttTopics.GATEWAY_ATTRIBUTES_TOPIC, result)); 133 return Optional.of(createMqttPublishMsg(ctx, MqttTopics.GATEWAY_ATTRIBUTES_TOPIC, result));
170 } 134 }
171 135
172 @Override 136 @Override
173 - public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) throws AdaptorException { 137 + public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) {
174 return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_RPC_REQUESTS_TOPIC + rpcRequest.getRequestId(), JsonConverter.toJson(rpcRequest, false))); 138 return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_RPC_REQUESTS_TOPIC + rpcRequest.getRequestId(), JsonConverter.toJson(rpcRequest, false)));
175 } 139 }
176 140
177 @Override 141 @Override
178 - public Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) throws AdaptorException { 142 + public Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) {
179 return Optional.of(createMqttPublishMsg(ctx, MqttTopics.GATEWAY_RPC_TOPIC, JsonConverter.toGatewayJson(deviceName, rpcRequest))); 143 return Optional.of(createMqttPublishMsg(ctx, MqttTopics.GATEWAY_RPC_TOPIC, JsonConverter.toGatewayJson(deviceName, rpcRequest)));
180 } 144 }
181 145
@@ -190,23 +154,91 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { @@ -190,23 +154,91 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor {
190 } 154 }
191 155
192 @Override 156 @Override
193 - public TransportProtos.ProvisionDeviceRequestMsg convertToProvisionRequestMsg(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { 157 + public Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.ProvisionDeviceResponseMsg responseMsg) {
  158 + return Optional.of(createMqttPublishMsg(ctx,
  159 + MqttTopics.GATEWAY_PROVISION_RESPONSE_TOPIC,
  160 + JsonConverter.toGatewayJson(deviceName, responseMsg)));
  161 + }
  162 +
  163 + public static JsonElement validateJsonPayload(UUID sessionId, ByteBuf payloadData) throws AdaptorException {
  164 + String payload = validatePayload(sessionId, payloadData, false);
  165 + try {
  166 + return new JsonParser().parse(payload);
  167 + } catch (JsonSyntaxException ex) {
  168 + log.warn("Payload is in incorrect format: {}", payload);
  169 + throw new AdaptorException(ex);
  170 + }
  171 + }
  172 +
  173 + protected TransportProtos.GetAttributeRequestMsg processGetAttributeRequestMsg(MqttPublishMessage inbound, String topic) throws AdaptorException {
  174 + String topicName = inbound.variableHeader().topicName();
  175 + try {
  176 + TransportProtos.GetAttributeRequestMsg.Builder result = TransportProtos.GetAttributeRequestMsg.newBuilder();
  177 + result.setRequestId(getRequestId(topicName, topic));
  178 + String payload = inbound.payload().toString(UTF8);
  179 + JsonElement requestBody = new JsonParser().parse(payload);
  180 + Set<String> clientKeys = toStringSet(requestBody, "clientKeys");
  181 + Set<String> sharedKeys = toStringSet(requestBody, "sharedKeys");
  182 + if (clientKeys != null) {
  183 + result.addAllClientAttributeNames(clientKeys);
  184 + }
  185 + if (sharedKeys != null) {
  186 + result.addAllSharedAttributeNames(sharedKeys);
  187 + }
  188 + return result.build();
  189 + } catch (RuntimeException e) {
  190 + log.warn("Failed to decode get attributes request", e);
  191 + throw new AdaptorException(e);
  192 + }
  193 + }
  194 +
  195 + protected TransportProtos.ToDeviceRpcResponseMsg processToDeviceRpcResponseMsg(MqttPublishMessage inbound, String topic) throws AdaptorException {
  196 + String topicName = inbound.variableHeader().topicName();
  197 + try {
  198 + int requestId = getRequestId(topicName, topic);
  199 + String payload = inbound.payload().toString(UTF8);
  200 + return TransportProtos.ToDeviceRpcResponseMsg.newBuilder().setRequestId(requestId).setPayload(payload).build();
  201 + } catch (RuntimeException e) {
  202 + log.warn("Failed to decode Rpc response", e);
  203 + throw new AdaptorException(e);
  204 + }
  205 + }
  206 +
  207 + protected TransportProtos.ToServerRpcRequestMsg processToServerRpcRequestMsg(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound, String topic) throws AdaptorException {
  208 + String topicName = inbound.variableHeader().topicName();
194 String payload = validatePayload(ctx.getSessionId(), inbound.payload(), false); 209 String payload = validatePayload(ctx.getSessionId(), inbound.payload(), false);
195 try { 210 try {
196 - return JsonConverter.convertToProvisionRequestMsg(payload); 211 + int requestId = getRequestId(topicName, topic);
  212 + return JsonConverter.convertToServerRpcRequest(new JsonParser().parse(payload), requestId);
197 } catch (IllegalStateException | JsonSyntaxException ex) { 213 } catch (IllegalStateException | JsonSyntaxException ex) {
198 throw new AdaptorException(ex); 214 throw new AdaptorException(ex);
199 } 215 }
200 } 216 }
201 217
202 - @Override  
203 - public Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.ProvisionDeviceResponseMsg responseMsg, int requestId) {  
204 - return Optional.of(createMqttPublishMsg(ctx,  
205 - MqttTopics.GATEWAY_PROVISION_REQUEST_TOPIC,  
206 - JsonConverter.toJson(responseMsg, requestId))); 218 + protected Optional<MqttMessage> processConvertFromAttributeResponseMsg(MqttDeviceAwareSessionContext ctx, TransportProtos.GetAttributeResponseMsg responseMsg, String topic) throws AdaptorException {
  219 + if (!StringUtils.isEmpty(responseMsg.getError())) {
  220 + throw new AdaptorException(responseMsg.getError());
  221 + } else {
  222 + int requestId = responseMsg.getRequestId();
  223 + if (requestId >= 0) {
  224 + return Optional.of(createMqttPublishMsg(ctx,
  225 + topic + requestId,
  226 + JsonConverter.toJson(responseMsg)));
  227 + }
  228 + return Optional.empty();
  229 + }
  230 + }
  231 +
  232 + protected Optional<MqttMessage> processConvertFromGatewayAttributeResponseMsg(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.GetAttributeResponseMsg responseMsg, String topic) throws AdaptorException {
  233 + if (!StringUtils.isEmpty(responseMsg.getError())) {
  234 + throw new AdaptorException(responseMsg.getError());
  235 + } else {
  236 + JsonObject result = JsonConverter.getJsonObjectForGateway(deviceName, responseMsg);
  237 + return Optional.of(createMqttPublishMsg(ctx, topic, result));
  238 + }
207 } 239 }
208 240
209 - private MqttPublishMessage createMqttPublishMsg(MqttDeviceAwareSessionContext ctx, String topic, JsonElement json) { 241 + protected MqttPublishMessage createMqttPublishMsg(MqttDeviceAwareSessionContext ctx, String topic, JsonElement json) {
210 MqttFixedHeader mqttFixedHeader = 242 MqttFixedHeader mqttFixedHeader =
211 new MqttFixedHeader(MqttMessageType.PUBLISH, false, ctx.getQoSForTopic(topic), false, 0); 243 new MqttFixedHeader(MqttMessageType.PUBLISH, false, ctx.getQoSForTopic(topic), false, 0);
212 MqttPublishVariableHeader header = new MqttPublishVariableHeader(topic, ctx.nextMsgId()); 244 MqttPublishVariableHeader header = new MqttPublishVariableHeader(topic, ctx.nextMsgId());
@@ -224,16 +256,6 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { @@ -224,16 +256,6 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor {
224 } 256 }
225 } 257 }
226 258
227 - public static JsonElement validateJsonPayload(UUID sessionId, ByteBuf payloadData) throws AdaptorException {  
228 - String payload = validatePayload(sessionId, payloadData, false);  
229 - try {  
230 - return new JsonParser().parse(payload);  
231 - } catch (JsonSyntaxException ex) {  
232 - log.warn("Payload is in incorrect format: {}", payload);  
233 - throw new AdaptorException(ex);  
234 - }  
235 - }  
236 -  
237 private static String validatePayload(UUID sessionId, ByteBuf payloadData, boolean isEmptyPayloadAllowed) throws AdaptorException { 259 private static String validatePayload(UUID sessionId, ByteBuf payloadData, boolean isEmptyPayloadAllowed) throws AdaptorException {
238 String payload = payloadData.toString(UTF8); 260 String payload = payloadData.toString(UTF8);
239 if (payload == null) { 261 if (payload == null) {
@@ -245,4 +267,8 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { @@ -245,4 +267,8 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor {
245 return payload; 267 return payload;
246 } 268 }
247 269
  270 + private int getRequestId(String topicName, String topic) {
  271 + return Integer.parseInt(topicName.substring(topic.length()));
  272 + }
  273 +
248 } 274 }
@@ -69,6 +69,6 @@ public interface MqttTransportAdaptor { @@ -69,6 +69,6 @@ public interface MqttTransportAdaptor {
69 69
70 Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, ProvisionDeviceResponseMsg provisionResponse) throws AdaptorException; 70 Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, ProvisionDeviceResponseMsg provisionResponse) throws AdaptorException;
71 71
72 - Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, ProvisionDeviceResponseMsg provisionResponse, int requestId) throws AdaptorException; 72 + Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, ProvisionDeviceResponseMsg provisionResponse) throws AdaptorException;
73 73
74 } 74 }
  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.transport.mqtt.adaptors;
  17 +
  18 +import com.google.protobuf.InvalidProtocolBufferException;
  19 +import io.netty.buffer.ByteBuf;
  20 +import io.netty.buffer.ByteBufAllocator;
  21 +import io.netty.buffer.UnpooledByteBufAllocator;
  22 +import io.netty.handler.codec.mqtt.MqttFixedHeader;
  23 +import io.netty.handler.codec.mqtt.MqttMessage;
  24 +import io.netty.handler.codec.mqtt.MqttMessageType;
  25 +import io.netty.handler.codec.mqtt.MqttPublishMessage;
  26 +import io.netty.handler.codec.mqtt.MqttPublishVariableHeader;
  27 +import lombok.extern.slf4j.Slf4j;
  28 +import org.springframework.stereotype.Component;
  29 +import org.springframework.util.StringUtils;
  30 +import org.thingsboard.server.common.data.device.profile.MqttTopics;
  31 +import org.thingsboard.server.common.transport.adaptor.AdaptorException;
  32 +import org.thingsboard.server.common.transport.adaptor.ProtoConverter;
  33 +import org.thingsboard.server.gen.transport.TransportApiProtos;
  34 +import org.thingsboard.server.gen.transport.TransportProtos;
  35 +import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsg;
  36 +import org.thingsboard.server.transport.mqtt.session.MqttDeviceAwareSessionContext;
  37 +
  38 +import java.util.Optional;
  39 +
  40 +@Component
  41 +@Slf4j
  42 +public class ProtoMqttAdaptor implements MqttTransportAdaptor {
  43 +
  44 + private static final ByteBufAllocator ALLOCATOR = new UnpooledByteBufAllocator(false);
  45 +
  46 + @Override
  47 + public TransportProtos.PostTelemetryMsg convertToPostTelemetry(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException {
  48 + byte[] bytes = toBytes(inbound.payload());
  49 + try {
  50 + return ProtoConverter.convertToTelemetryProto(bytes);
  51 + } catch (InvalidProtocolBufferException | IllegalArgumentException e) {
  52 + throw new AdaptorException(e);
  53 + }
  54 + }
  55 +
  56 + @Override
  57 + public TransportProtos.PostAttributeMsg convertToPostAttributes(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException {
  58 + byte[] bytes = toBytes(inbound.payload());
  59 + try {
  60 + return ProtoConverter.validatePostAttributeMsg(bytes);
  61 + } catch (InvalidProtocolBufferException | IllegalArgumentException e) {
  62 + throw new AdaptorException(e);
  63 + }
  64 + }
  65 +
  66 + @Override
  67 + public TransportProtos.ClaimDeviceMsg convertToClaimDevice(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException {
  68 + byte[] bytes = toBytes(inbound.payload());
  69 + try {
  70 + return ProtoConverter.convertToClaimDeviceProto(ctx.getDeviceId(), bytes);
  71 + } catch (InvalidProtocolBufferException e) {
  72 + throw new AdaptorException(e);
  73 + }
  74 + }
  75 +
  76 + @Override
  77 + public TransportProtos.GetAttributeRequestMsg convertToGetAttributes(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException {
  78 + byte[] bytes = toBytes(inbound.payload());
  79 + String topicName = inbound.variableHeader().topicName();
  80 + int requestId = getRequestId(topicName, MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX);
  81 + try {
  82 + return ProtoConverter.convertToGetAttributeRequestMessage(bytes, requestId);
  83 + } catch (InvalidProtocolBufferException e) {
  84 + log.warn("Failed to decode get attributes request", e);
  85 + throw new AdaptorException(e);
  86 + }
  87 + }
  88 +
  89 + @Override
  90 + public TransportProtos.ToDeviceRpcResponseMsg convertToDeviceRpcResponse(MqttDeviceAwareSessionContext ctx, MqttPublishMessage mqttMsg) throws AdaptorException {
  91 + byte[] bytes = toBytes(mqttMsg.payload());
  92 + try {
  93 + return TransportProtos.ToDeviceRpcResponseMsg.parseFrom(bytes);
  94 + } catch (RuntimeException | InvalidProtocolBufferException e) {
  95 + log.warn("Failed to decode Rpc response", e);
  96 + throw new AdaptorException(e);
  97 + }
  98 + }
  99 +
  100 + @Override
  101 + public TransportProtos.ToServerRpcRequestMsg convertToServerRpcRequest(MqttDeviceAwareSessionContext ctx, MqttPublishMessage mqttMsg) throws AdaptorException {
  102 + byte[] bytes = toBytes(mqttMsg.payload());
  103 + String topicName = mqttMsg.variableHeader().topicName();
  104 + try {
  105 + int requestId = getRequestId(topicName, MqttTopics.DEVICE_RPC_REQUESTS_TOPIC);
  106 + return ProtoConverter.convertToServerRpcRequest(bytes, requestId);
  107 + } catch (InvalidProtocolBufferException e) {
  108 + throw new AdaptorException(e);
  109 + }
  110 + }
  111 +
  112 + @Override
  113 + public TransportProtos.ProvisionDeviceRequestMsg convertToProvisionRequestMsg(MqttDeviceAwareSessionContext ctx, MqttPublishMessage mqttMsg) throws AdaptorException {
  114 + byte[] bytes = toBytes(mqttMsg.payload());
  115 + String topicName = mqttMsg.variableHeader().topicName();
  116 + try {
  117 + return ProtoConverter.convertToProvisionRequestMsg(bytes);
  118 + } catch (InvalidProtocolBufferException ex) {
  119 + throw new AdaptorException(ex);
  120 + }
  121 + }
  122 +
  123 + @Override
  124 + public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException {
  125 + if (!StringUtils.isEmpty(responseMsg.getError())) {
  126 + throw new AdaptorException(responseMsg.getError());
  127 + } else {
  128 + int requestId = responseMsg.getRequestId();
  129 + if (requestId >= 0) {
  130 + return Optional.of(createMqttPublishMsg(ctx,
  131 + MqttTopics.DEVICE_ATTRIBUTES_RESPONSE_TOPIC_PREFIX + requestId,
  132 + responseMsg.toByteArray()));
  133 + }
  134 + return Optional.empty();
  135 + }
  136 + }
  137 +
  138 +
  139 + @Override
  140 + public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) {
  141 + return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_RPC_REQUESTS_TOPIC + rpcRequest.getRequestId(), rpcRequest.toByteArray()));
  142 + }
  143 +
  144 + @Override
  145 + public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.ToServerRpcResponseMsg rpcResponse) {
  146 + return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_RPC_RESPONSE_TOPIC + rpcResponse.getRequestId(), rpcResponse.toByteArray()));
  147 + }
  148 +
  149 + @Override
  150 + public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.AttributeUpdateNotificationMsg notificationMsg) {
  151 + return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_ATTRIBUTES_TOPIC, notificationMsg.toByteArray()));
  152 + }
  153 +
  154 + @Override
  155 + public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.ProvisionDeviceResponseMsg provisionResponse) {
  156 + return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_PROVISION_RESPONSE_TOPIC, provisionResponse.toByteArray()));
  157 + }
  158 +
  159 + @Override
  160 + public Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException {
  161 + if (!StringUtils.isEmpty(responseMsg.getError())) {
  162 + throw new AdaptorException(responseMsg.getError());
  163 + } else {
  164 + TransportApiProtos.GatewayAttributeResponseMsg.Builder responseMsgBuilder = TransportApiProtos.GatewayAttributeResponseMsg.newBuilder();
  165 + responseMsgBuilder.setDeviceName(deviceName);
  166 + responseMsgBuilder.setResponseMsg(responseMsg);
  167 + byte[] payloadBytes = responseMsgBuilder.build().toByteArray();
  168 + return Optional.of(createMqttPublishMsg(ctx, MqttTopics.GATEWAY_ATTRIBUTES_RESPONSE_TOPIC, payloadBytes));
  169 + }
  170 + }
  171 +
  172 + @Override
  173 + public Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.AttributeUpdateNotificationMsg notificationMsg) {
  174 + TransportApiProtos.GatewayAttributeUpdateNotificationMsg.Builder builder = TransportApiProtos.GatewayAttributeUpdateNotificationMsg.newBuilder();
  175 + builder.setDeviceName(deviceName);
  176 + builder.setNotificationMsg(notificationMsg);
  177 + byte[] payloadBytes = builder.build().toByteArray();
  178 + return Optional.of(createMqttPublishMsg(ctx, MqttTopics.GATEWAY_ATTRIBUTES_TOPIC, payloadBytes));
  179 + }
  180 +
  181 + @Override
  182 + public Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) {
  183 + TransportApiProtos.GatewayDeviceRpcRequestMsg.Builder builder = TransportApiProtos.GatewayDeviceRpcRequestMsg.newBuilder();
  184 + builder.setDeviceName(deviceName);
  185 + builder.setRpcRequestMsg(rpcRequest);
  186 + byte[] payloadBytes = builder.build().toByteArray();
  187 + return Optional.of(createMqttPublishMsg(ctx, MqttTopics.GATEWAY_RPC_TOPIC, payloadBytes));
  188 + }
  189 +
  190 + @Override
  191 + public Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.ProvisionDeviceResponseMsg responseMsg) throws AdaptorException {
  192 + TransportApiProtos.GatewayProvisionResponseMsg.Builder builder = TransportApiProtos.GatewayProvisionResponseMsg.newBuilder();
  193 + builder.setDeviceName(deviceName);
  194 + builder.setProvisionDeviceResponseMsg(responseMsg);
  195 + byte[] payloadBytes = builder.build().toByteArray();
  196 + return Optional.of(createMqttPublishMsg(ctx, MqttTopics.GATEWAY_PROVISION_RESPONSE_TOPIC, payloadBytes));
  197 + }
  198 +
  199 + public static byte[] toBytes(ByteBuf inbound) {
  200 + byte[] bytes = new byte[inbound.readableBytes()];
  201 + int readerIndex = inbound.readerIndex();
  202 + inbound.getBytes(readerIndex, bytes);
  203 + return bytes;
  204 + }
  205 +
  206 + private MqttPublishMessage createMqttPublishMsg(MqttDeviceAwareSessionContext ctx, String topic, byte[] payloadBytes) {
  207 + MqttFixedHeader mqttFixedHeader =
  208 + new MqttFixedHeader(MqttMessageType.PUBLISH, false, ctx.getQoSForTopic(topic), false, 0);
  209 + MqttPublishVariableHeader header = new MqttPublishVariableHeader(topic, ctx.nextMsgId());
  210 + ByteBuf payload = ALLOCATOR.buffer();
  211 + payload.writeBytes(payloadBytes);
  212 + return new MqttPublishMessage(mqttFixedHeader, header, payload);
  213 + }
  214 +
  215 + private int getRequestId(String topicName, String topic) {
  216 + return Integer.parseInt(topicName.substring(topic.length()));
  217 + }
  218 +
  219 +}