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 43 "name": "Save Client Attributes",
44 44 "debugMode": false,
45 45 "configuration": {
46   - "scope": "CLIENT_SCOPE"
  46 + "scope": "CLIENT_SCOPE",
  47 + "notifyDevice": "false"
47 48 }
48 49 },
49 50 {
... ...
... ... @@ -31,7 +31,8 @@
31 31 "name": "Save Client Attributes",
32 32 "debugMode": false,
33 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 226
227 227 @Override
228 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 234 onLocalTelemetrySubUpdate(entityId,
230 235 s -> {
231 236 if (TbSubscriptionType.ATTRIBUTES.equals(s.getType())) {
... ... @@ -254,7 +259,7 @@ public class DefaultSubscriptionManagerService implements SubscriptionManagerSer
254 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 263 clusterService.pushMsgToCore(DeviceAttributesEventNotificationMsg.onUpdate(tenantId,
259 264 new DeviceId(entityId.getId()), DataConstants.SHARED_SCOPE, new ArrayList<>(attributes))
260 265 , null);
... ...
... ... @@ -17,13 +17,12 @@ package org.thingsboard.server.service.subscription;
17 17
18 18 import org.springframework.context.ApplicationListener;
19 19 import org.thingsboard.server.common.data.alarm.Alarm;
20   -import org.thingsboard.server.common.data.id.AlarmId;
21 20 import org.thingsboard.server.common.data.id.EntityId;
22 21 import org.thingsboard.server.common.data.id.TenantId;
23 22 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
24 23 import org.thingsboard.server.common.data.kv.TsKvEntry;
25   -import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
26 24 import org.thingsboard.server.common.msg.queue.TbCallback;
  25 +import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
27 26
28 27 import java.util.List;
29 28
... ... @@ -37,9 +36,13 @@ public interface SubscriptionManagerService extends ApplicationListener<Partitio
37 36
38 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 41 void onAttributesDelete(TenantId tenantId, EntityId entityId, String scope, List<String> keys, TbCallback empty);
41 42
42 43 void onAlarmUpdate(TenantId tenantId, EntityId entityId, Alarm alarm, TbCallback callback);
43 44
44 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 171
172 172 @Override
173 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 179 ListenableFuture<List<Void>> saveFuture = attrService.save(tenantId, entityId, scope, attributes);
175 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 184 @Override
... ... @@ -236,11 +241,11 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer
236 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 245 TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, entityId);
241 246 if (currentPartitions.contains(tpi)) {
242 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 249 } else {
245 250 log.warn("Possible misconfiguration because subscriptionManagerService is null!");
246 251 }
... ...
... ... @@ -237,6 +237,8 @@ public class DefaultTransportApiService implements TransportApiService {
237 237 device.setName(requestMsg.getDeviceName());
238 238 device.setType(requestMsg.getDeviceType());
239 239 device.setCustomerId(gateway.getCustomerId());
  240 + DeviceProfile deviceProfile = deviceProfileService.findOrCreateDeviceProfile(gateway.getTenantId(), requestMsg.getDeviceType());
  241 + device.setDeviceProfileId(deviceProfile.getId());
240 242 device = deviceService.saveDevice(device);
241 243 relationService.saveRelationAsync(TenantId.SYS_TENANT_ID, new EntityRelation(gateway.getId(), device.getId(), "Created"));
242 244 deviceStateService.onDeviceAdded(device);
... ...
... ... @@ -227,6 +227,10 @@ public abstract class AbstractWebTest {
227 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 234 private Tenant savedDifferentTenant;
231 235
232 236 protected void loginDifferentTenant() throws Exception {
... ... @@ -252,15 +256,27 @@ public abstract class AbstractWebTest {
252 256 protected User createUserAndLogin(User user, String password) throws Exception {
253 257 User savedUser = doPost("/api/user", user, User.class);
254 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 274 doGet("/api/noauth/activate?activateToken={activateToken}", TestMailService.currentActivateToken)
256 275 .andExpect(status().isSeeOther())
257 276 .andExpect(header().string(HttpHeaders.LOCATION, "/login/createPassword?activateToken=" + TestMailService.currentActivateToken));
258   - JsonNode activateRequest = new ObjectMapper().createObjectNode()
  277 + return new ObjectMapper().createObjectNode()
259 278 .put("activateToken", TestMailService.currentActivateToken)
260 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 282 protected void login(String username, String password) throws Exception {
... ... @@ -443,6 +459,10 @@ public abstract class AbstractWebTest {
443 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 466 protected <T> T doDelete(String urlTemplate, Class<T> responseClass, String... params) throws Exception {
447 467 return readResponse(doDelete(urlTemplate, params).andExpect(status().isOk()), responseClass);
448 468 }
... ...
... ... @@ -34,7 +34,7 @@ public class ControllerSqlTestSuite {
34 34
35 35 @ClassRule
36 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 38 "sql/hsql/drop-all-tables.sql",
39 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 33
34 34 @ClassRule
35 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 37 "sql/hsql/drop-all-tables.sql",
38 38 "nosql-test.properties");
39 39
... ...
... ... @@ -27,13 +27,17 @@ import java.util.Arrays;
27 27 @RunWith(ClasspathSuite.class)
28 28 @ClasspathSuite.ClassnameFilters({
29 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 36 public class MqttSqlTestSuite {
33 37
34 38 @ClassRule
35 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 41 "sql/hsql/drop-all-tables.sql",
38 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 16 package org.thingsboard.server.mqtt.rpc;
17 17
18 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 23 import io.netty.handler.codec.mqtt.MqttQoS;
20 24 import lombok.extern.slf4j.Slf4j;
21 25 import org.apache.commons.lang3.StringUtils;
... ... @@ -23,15 +27,28 @@ import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
23 27 import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
24 28 import org.eclipse.paho.client.mqttv3.MqttCallback;
25 29 import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
  30 +import org.eclipse.paho.client.mqttv3.MqttException;
26 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 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 40 import org.thingsboard.server.common.data.Tenant;
  41 +import org.thingsboard.server.common.data.TransportPayloadType;
30 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 47 import org.thingsboard.server.common.data.security.Authority;
32 48 import org.thingsboard.server.common.data.security.DeviceCredentials;
33 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 52 import org.thingsboard.server.service.security.AccessValidator;
36 53
37 54 import java.util.Arrays;
... ... @@ -47,74 +64,88 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
47 64 * @author Valerii Sosliuk
48 65 */
49 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 140 CountDownLatch latch = new CountDownLatch(1);
110 141 TestMqttCallback callback = new TestMqttCallback(client, latch);
111 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 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 149 String deviceId = savedDevice.getId().getId().toString();
119 150 String result = doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isOk());
120 151 Assert.assertTrue(StringUtils.isEmpty(result));
... ... @@ -122,100 +153,49 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC
122 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 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 173 String deviceId = savedDevice.getId().getId().toString();
177   -
178 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 200 private final MqttAsyncClient client;
221 201 private final CountDownLatch latch;
... ... @@ -237,11 +217,9 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC
237 217 @Override
238 218 public void messageArrived(String requestTopic, MqttMessage mqttMessage) throws Exception {
239 219 log.info("Message Arrived: " + Arrays.toString(mqttMessage.getPayload()));
240   - MqttMessage message = new MqttMessage();
241 220 String responseTopic = requestTopic.replace("request", "response");
242   - message.setPayload("{\"value1\":\"A\", \"value2\":\"B\"}".getBytes("UTF-8"));
243 221 qoS = mqttMessage.getQos();
244   - client.publish(responseTopic, message);
  222 + client.publish(responseTopic, processMessageArrived(requestTopic, mqttMessage));
245 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 16 package org.thingsboard.server.mqtt.rpc.nosql;
17 17
18 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 22 * Created by Valerii Sosliuk on 8/22/2017.
23 23 */
24 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 16 package org.thingsboard.server.mqtt.rpc.sql;
17 17
18 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 22 * Created by Valerii Sosliuk on 8/22/2017.
23 23 */
24 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 13 * See the License for the specific language governing permissions and
14 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 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 22 * Created by Valerii Sosliuk on 8/22/2017.
23 23 */
24 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 13 * See the License for the specific language governing permissions and
14 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 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 22 * Created by Valerii Sosliuk on 8/22/2017.
24 23 */
25 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 32
33 33 @ClassRule
34 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 36 "sql/hsql/drop-all-tables.sql",
37 37 "sql-test.properties");
38 38
... ...
... ... @@ -16,7 +16,6 @@
16 16 package org.thingsboard.server.rules.flow.sql;
17 17
18 18 import org.thingsboard.server.dao.service.DaoSqlTest;
19   -import org.thingsboard.server.mqtt.rpc.AbstractMqttServerSideRpcIntegrationTest;
20 19 import org.thingsboard.server.rules.flow.AbstractRuleEngineFlowIntegrationTest;
21 20
22 21 /**
... ...
... ... @@ -33,7 +33,7 @@ public class SystemSqlTestSuite {
33 33
34 34 @ClassRule
35 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 37 "sql/hsql/drop-all-tables.sql",
38 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 16 package org.thingsboard.server.common.data.device.profile;
17 17
18 18 import lombok.Data;
  19 +import org.thingsboard.server.common.data.TransportPayloadType;
19 20 import org.thingsboard.server.common.data.DeviceTransportType;
20 21
21 22 @Data
22 23 public class MqttDeviceProfileTransportConfiguration implements DeviceProfileTransportConfiguration {
23 24
  25 + private TransportPayloadType transportPayloadType = TransportPayloadType.JSON;
  26 +
24 27 private String deviceTelemetryTopic = MqttTopics.DEVICE_TELEMETRY_TOPIC;
25 28 private String deviceAttributesTopic = MqttTopics.DEVICE_ATTRIBUTES_TOPIC;
26 29
... ...
... ... @@ -20,32 +20,57 @@ package org.thingsboard.server.common.data.device.profile;
20 20 */
21 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 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 75 private MqttTopics() {
51 76 }
... ...
... ... @@ -20,6 +20,7 @@ import lombok.Data;
20 20 import lombok.NoArgsConstructor;
21 21
22 22 import java.io.Serializable;
  23 +import java.util.Collections;
23 24 import java.util.HashMap;
24 25 import java.util.Map;
25 26 import java.util.concurrent.ConcurrentHashMap;
... ... @@ -31,6 +32,8 @@ import java.util.concurrent.ConcurrentHashMap;
31 32 @NoArgsConstructor
32 33 public final class TbMsgMetaData implements Serializable {
33 34
  35 + public static final TbMsgMetaData EMPTY = new TbMsgMetaData(Collections.emptyMap());
  36 +
34 37 private final Map<String, String> data = new ConcurrentHashMap<>();
35 38
36 39 public TbMsgMetaData(Map<String, String> data) {
... ...
... ... @@ -24,7 +24,8 @@ import org.springframework.beans.factory.annotation.Value;
24 24 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
25 25 import org.springframework.stereotype.Component;
26 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 31 * Created by ashvayka on 04.10.18.
... ... @@ -40,7 +41,11 @@ public class MqttTransportContext extends TransportContext {
40 41
41 42 @Getter
42 43 @Autowired
43   - private MqttTransportAdaptor adaptor;
  44 + private JsonMqttAdaptor jsonMqttAdaptor;
  45 +
  46 + @Getter
  47 + @Autowired
  48 + private ProtoMqttAdaptor protoMqttAdaptor;
44 49
45 50 @Getter
46 51 @Value("${transport.mqtt.netty.max_payload_size}")
... ...
... ... @@ -16,6 +16,7 @@
16 16 package org.thingsboard.server.transport.mqtt;
17 17
18 18 import com.fasterxml.jackson.databind.JsonNode;
  19 +import com.google.gson.JsonSyntaxException;
19 20 import io.netty.channel.ChannelHandlerContext;
20 21 import io.netty.channel.ChannelInboundHandlerAdapter;
21 22 import io.netty.handler.codec.mqtt.MqttConnAckMessage;
... ... @@ -34,7 +35,6 @@ import io.netty.handler.codec.mqtt.MqttSubscribeMessage;
34 35 import io.netty.handler.codec.mqtt.MqttTopicSubscription;
35 36 import io.netty.handler.codec.mqtt.MqttUnsubscribeMessage;
36 37 import io.netty.handler.ssl.SslHandler;
37   -import io.netty.util.CharsetUtil;
38 38 import io.netty.util.ReferenceCountUtil;
39 39 import io.netty.util.concurrent.Future;
40 40 import io.netty.util.concurrent.GenericFutureListener;
... ... @@ -53,7 +53,6 @@ import org.thingsboard.server.common.transport.auth.TransportDeviceInfo;
53 53 import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
54 54 import org.thingsboard.server.common.transport.service.DefaultTransportService;
55 55 import org.thingsboard.server.gen.transport.TransportProtos;
56   -import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto;
57 56 import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsg;
58 57 import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent;
59 58 import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg;
... ... @@ -95,7 +94,6 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
95 94
96 95 private final UUID sessionId;
97 96 private final MqttTransportContext context;
98   - private final MqttTransportAdaptor adaptor;
99 97 private final TransportService transportService;
100 98 private final SslHandler sslHandler;
101 99 private final ConcurrentMap<MqttTopicMatcher, Integer> mqttQoSMap;
... ... @@ -108,10 +106,9 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
108 106 this.sessionId = UUID.randomUUID();
109 107 this.context = context;
110 108 this.transportService = context.getTransportService();
111   - this.adaptor = context.getAdaptor();
112 109 this.sslHandler = sslHandler;
113 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 114 @Override
... ... @@ -153,9 +150,19 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
153 150 int msgId = mqttMsg.variableHeader().packetId();
154 151 try {
155 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 166 } else {
160 167 throw new RuntimeException("Unsupported topic for provisioning requests!");
161 168 }
... ... @@ -250,23 +257,24 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
250 257
251 258 private void processDevicePublish(ChannelHandlerContext ctx, MqttPublishMessage mqttMsg, String topicName, int msgId) {
252 259 try {
  260 + MqttTransportAdaptor payloadAdaptor = deviceSessionCtx.getPayloadAdaptor();
253 261 if (deviceSessionCtx.isDeviceTelemetryTopic(topicName)) {
254   - TransportProtos.PostTelemetryMsg postTelemetryMsg = adaptor.convertToPostTelemetry(deviceSessionCtx, mqttMsg);
  262 + TransportProtos.PostTelemetryMsg postTelemetryMsg = payloadAdaptor.convertToPostTelemetry(deviceSessionCtx, mqttMsg);
255 263 transportService.process(deviceSessionCtx.getSessionInfo(), postTelemetryMsg, getPubAckCallback(ctx, msgId, postTelemetryMsg));
256 264 } else if (deviceSessionCtx.isDeviceAttributesTopic(topicName)) {
257   - TransportProtos.PostAttributeMsg postAttributeMsg = adaptor.convertToPostAttributes(deviceSessionCtx, mqttMsg);
  265 + TransportProtos.PostAttributeMsg postAttributeMsg = payloadAdaptor.convertToPostAttributes(deviceSessionCtx, mqttMsg);
258 266 transportService.process(deviceSessionCtx.getSessionInfo(), postAttributeMsg, getPubAckCallback(ctx, msgId, postAttributeMsg));
259 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 269 transportService.process(deviceSessionCtx.getSessionInfo(), getAttributeMsg, getPubAckCallback(ctx, msgId, getAttributeMsg));
262 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 272 transportService.process(deviceSessionCtx.getSessionInfo(), rpcResponseMsg, getPubAckCallback(ctx, msgId, rpcResponseMsg));
265 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 275 transportService.process(deviceSessionCtx.getSessionInfo(), rpcRequestMsg, getPubAckCallback(ctx, msgId, rpcRequestMsg));
268 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 278 transportService.process(deviceSessionCtx.getSessionInfo(), claimDeviceMsg, getPubAckCallback(ctx, msgId, claimDeviceMsg));
271 279 } else {
272 280 transportService.reportActivity(deviceSessionCtx.getSessionInfo());
... ... @@ -279,7 +287,6 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
279 287 }
280 288
281 289
282   -
283 290 private <T> TransportServiceCallback<Void> getPubAckCallback(final ChannelHandlerContext ctx, final int msgId, final T msg) {
284 291 return new TransportServiceCallback<Void>() {
285 292 @Override
... ... @@ -316,7 +323,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
316 323 ctx.writeAndFlush(createMqttPubAckMsg(msgId));
317 324 }
318 325 try {
319   - adaptor.convertToPublish(deviceSessionCtx, provisionResponseMsg).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush);
  326 + deviceSessionCtx.getPayloadAdaptor().convertToPublish(deviceSessionCtx, provisionResponseMsg).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush);
320 327 } catch (Exception e) {
321 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 361 break;
355 362 }
356 363 case MqttTopics.DEVICE_RPC_RESPONSE_SUB_TOPIC:
  364 + case MqttTopics.DEVICE_ATTRIBUTES_RESPONSES_TOPIC:
357 365 case MqttTopics.GATEWAY_ATTRIBUTES_TOPIC:
358 366 case MqttTopics.GATEWAY_RPC_TOPIC:
359 367 case MqttTopics.GATEWAY_ATTRIBUTES_RESPONSE_TOPIC:
360   - case MqttTopics.DEVICE_ATTRIBUTES_RESPONSES_TOPIC:
361 368 case MqttTopics.GATEWAY_PROVISION_RESPONSE_TOPIC:
362 369 case MqttTopics.DEVICE_PROVISION_RESPONSE_TOPIC:
363 370 registerSubQoS(topic, grantedQoSList, reqQoS);
... ... @@ -565,7 +572,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
565 572 if (infoNode != null) {
566 573 JsonNode gatewayNode = infoNode.get("gateway");
567 574 if (gatewayNode != null && gatewayNode.asBoolean()) {
568   - gatewaySessionHandler = new GatewaySessionHandler(context, deviceSessionCtx, sessionId);
  575 + gatewaySessionHandler = new GatewaySessionHandler(deviceSessionCtx, sessionId);
569 576 }
570 577 }
571 578 } catch (IOException e) {
... ... @@ -619,7 +626,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
619 626 @Override
620 627 public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg response) {
621 628 try {
622   - adaptor.convertToPublish(deviceSessionCtx, response).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush);
  629 + deviceSessionCtx.getPayloadAdaptor().convertToPublish(deviceSessionCtx, response).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush);
623 630 } catch (Exception e) {
624 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 635 @Override
629 636 public void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg notification) {
630 637 try {
631   - adaptor.convertToPublish(deviceSessionCtx, notification).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush);
  638 + deviceSessionCtx.getPayloadAdaptor().convertToPublish(deviceSessionCtx, notification).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush);
632 639 } catch (Exception e) {
633 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 651 public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg rpcRequest) {
645 652 log.trace("[{}] Received RPC command to device", sessionId);
646 653 try {
647   - adaptor.convertToPublish(deviceSessionCtx, rpcRequest).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush);
  654 + deviceSessionCtx.getPayloadAdaptor().convertToPublish(deviceSessionCtx, rpcRequest).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush);
648 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 660 @Override
654 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 663 try {
657   - adaptor.convertToPublish(deviceSessionCtx, rpcResponse).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush);
  664 + deviceSessionCtx.getPayloadAdaptor().convertToPublish(deviceSessionCtx, rpcResponse).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush);
658 665 } catch (Exception e) {
659 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 38 import org.thingsboard.server.transport.mqtt.session.MqttDeviceAwareSessionContext;
39 39
40 40 import java.nio.charset.Charset;
  41 +import java.nio.charset.StandardCharsets;
41 42 import java.util.Arrays;
42 43 import java.util.HashSet;
43 44 import java.util.Optional;
... ... @@ -47,12 +48,13 @@ import java.util.UUID;
47 48 /**
48 49 * @author Andrew Shvayka
49 50 */
50   -@Component("JsonMqttAdaptor")
  51 +@Component
51 52 @Slf4j
52 53 public class JsonMqttAdaptor implements MqttTransportAdaptor {
53 54
  55 + protected static final Charset UTF8 = StandardCharsets.UTF_8;
  56 +
54 57 private static final Gson GSON = new Gson();
55   - private static final Charset UTF8 = Charset.forName("UTF-8");
56 58 private static final ByteBufAllocator ALLOCATOR = new UnpooledByteBufAllocator(false);
57 59
58 60 @Override
... ... @@ -76,106 +78,68 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor {
76 78 }
77 79
78 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 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 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 92 String payload = validatePayload(ctx.getSessionId(), inbound.payload(), false);
118 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 95 } catch (IllegalStateException | JsonSyntaxException ex) {
122 96 throw new AdaptorException(ex);
123 97 }
124 98 }
125 99
126 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 115 @Override
137 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 120 @Override
152 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 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 127 return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_ATTRIBUTES_TOPIC, JsonConverter.toJson(notificationMsg)));
164 128 }
165 129
166 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 132 JsonObject result = JsonConverter.getJsonObjectForGateway(deviceName, notificationMsg);
169 133 return Optional.of(createMqttPublishMsg(ctx, MqttTopics.GATEWAY_ATTRIBUTES_TOPIC, result));
170 134 }
171 135
172 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 138 return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_RPC_REQUESTS_TOPIC + rpcRequest.getRequestId(), JsonConverter.toJson(rpcRequest, false)));
175 139 }
176 140
177 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 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 154 }
191 155
192 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 209 String payload = validatePayload(ctx.getSessionId(), inbound.payload(), false);
195 210 try {
196   - return JsonConverter.convertToProvisionRequestMsg(payload);
  211 + int requestId = getRequestId(topicName, topic);
  212 + return JsonConverter.convertToServerRpcRequest(new JsonParser().parse(payload), requestId);
197 213 } catch (IllegalStateException | JsonSyntaxException ex) {
198 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 242 MqttFixedHeader mqttFixedHeader =
211 243 new MqttFixedHeader(MqttMessageType.PUBLISH, false, ctx.getQoSForTopic(topic), false, 0);
212 244 MqttPublishVariableHeader header = new MqttPublishVariableHeader(topic, ctx.nextMsgId());
... ... @@ -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 259 private static String validatePayload(UUID sessionId, ByteBuf payloadData, boolean isEmptyPayloadAllowed) throws AdaptorException {
238 260 String payload = payloadData.toString(UTF8);
239 261 if (payload == null) {
... ... @@ -245,4 +267,8 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor {
245 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 69
70 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 +}
... ...