Commit b5bfb082b7c96bac7d9b7b1c10c4d112f8641d2e

Authored by Andrii Shvaika
2 parents 080a5f15 17053118

Merge branch 'develop/3.2' of github.com:thingsboard/thingsboard into develop/3.2

Showing 67 changed files with 3792 additions and 432 deletions

Too many changes to show.

To preserve performance only 67 of 81 files are displayed.

@@ -24,6 +24,7 @@ import org.springframework.beans.factory.annotation.Autowired; @@ -24,6 +24,7 @@ import org.springframework.beans.factory.annotation.Autowired;
24 import org.springframework.beans.factory.annotation.Value; 24 import org.springframework.beans.factory.annotation.Value;
25 import org.springframework.http.HttpStatus; 25 import org.springframework.http.HttpStatus;
26 import org.springframework.security.access.prepost.PreAuthorize; 26 import org.springframework.security.access.prepost.PreAuthorize;
  27 +import org.springframework.util.CollectionUtils;
27 import org.springframework.util.StringUtils; 28 import org.springframework.util.StringUtils;
28 import org.springframework.web.bind.annotation.PathVariable; 29 import org.springframework.web.bind.annotation.PathVariable;
29 import org.springframework.web.bind.annotation.RequestBody; 30 import org.springframework.web.bind.annotation.RequestBody;
@@ -49,6 +50,8 @@ import org.thingsboard.server.common.data.page.PageLink; @@ -49,6 +50,8 @@ import org.thingsboard.server.common.data.page.PageLink;
49 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; 50 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
50 import org.thingsboard.server.common.data.rule.DefaultRuleChainCreateRequest; 51 import org.thingsboard.server.common.data.rule.DefaultRuleChainCreateRequest;
51 import org.thingsboard.server.common.data.rule.RuleChain; 52 import org.thingsboard.server.common.data.rule.RuleChain;
  53 +import org.thingsboard.server.common.data.rule.RuleChainData;
  54 +import org.thingsboard.server.common.data.rule.RuleChainImportResult;
52 import org.thingsboard.server.common.data.rule.RuleChainMetaData; 55 import org.thingsboard.server.common.data.rule.RuleChainMetaData;
53 import org.thingsboard.server.common.data.rule.RuleNode; 56 import org.thingsboard.server.common.data.rule.RuleNode;
54 import org.thingsboard.server.common.msg.TbMsg; 57 import org.thingsboard.server.common.msg.TbMsg;
@@ -386,6 +389,36 @@ public class RuleChainController extends BaseController { @@ -386,6 +389,36 @@ public class RuleChainController extends BaseController {
386 } 389 }
387 } 390 }
388 391
  392 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  393 + @RequestMapping(value = "/ruleChains/export", params = {"limit"}, method = RequestMethod.GET)
  394 + @ResponseBody
  395 + public RuleChainData exportRuleChains(@RequestParam("limit") int limit) throws ThingsboardException {
  396 + try {
  397 + TenantId tenantId = getCurrentUser().getTenantId();
  398 + PageLink pageLink = new PageLink(limit);
  399 + return checkNotNull(ruleChainService.exportTenantRuleChains(tenantId, pageLink));
  400 + } catch (Exception e) {
  401 + throw handleException(e);
  402 + }
  403 + }
  404 +
  405 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  406 + @RequestMapping(value = "/ruleChains/import", method = RequestMethod.POST)
  407 + @ResponseBody
  408 + public void importRuleChains(@RequestBody RuleChainData ruleChainData, @RequestParam(required = false, defaultValue = "false") boolean overwrite) throws ThingsboardException {
  409 + try {
  410 + TenantId tenantId = getCurrentUser().getTenantId();
  411 + List<RuleChainImportResult> importResults = ruleChainService.importTenantRuleChains(tenantId, ruleChainData, overwrite);
  412 + if (!CollectionUtils.isEmpty(importResults)) {
  413 + for (RuleChainImportResult importResult : importResults) {
  414 + tbClusterService.onEntityStateChange(importResult.getTenantId(), importResult.getRuleChainId(), importResult.getLifecycleEvent());
  415 + }
  416 + }
  417 + } catch (Exception e) {
  418 + throw handleException(e);
  419 + }
  420 + }
  421 +
389 private String msgToOutput(TbMsg msg) throws Exception { 422 private String msgToOutput(TbMsg msg) throws Exception {
390 ObjectNode msgData = objectMapper.createObjectNode(); 423 ObjectNode msgData = objectMapper.createObjectNode();
391 if (!StringUtils.isEmpty(msg.getData())) { 424 if (!StringUtils.isEmpty(msg.getData())) {
@@ -225,6 +225,8 @@ public class DefaultTransportApiService implements TransportApiService { @@ -225,6 +225,8 @@ public class DefaultTransportApiService implements TransportApiService {
225 device.setName(requestMsg.getDeviceName()); 225 device.setName(requestMsg.getDeviceName());
226 device.setType(requestMsg.getDeviceType()); 226 device.setType(requestMsg.getDeviceType());
227 device.setCustomerId(gateway.getCustomerId()); 227 device.setCustomerId(gateway.getCustomerId());
  228 + DeviceProfile deviceProfile = deviceProfileService.findOrCreateDeviceProfile(gateway.getTenantId(), requestMsg.getDeviceType());
  229 + device.setDeviceProfileId(deviceProfile.getId());
228 device = deviceService.saveDevice(device); 230 device = deviceService.saveDevice(device);
229 relationService.saveRelationAsync(TenantId.SYS_TENANT_ID, new EntityRelation(gateway.getId(), device.getId(), "Created")); 231 relationService.saveRelationAsync(TenantId.SYS_TENANT_ID, new EntityRelation(gateway.getId(), device.getId(), "Created"));
230 deviceStateService.onDeviceAdded(device); 232 deviceStateService.onDeviceAdded(device);
@@ -226,6 +226,10 @@ public abstract class AbstractWebTest { @@ -226,6 +226,10 @@ public abstract class AbstractWebTest {
226 login(CUSTOMER_USER_EMAIL, CUSTOMER_USER_PASSWORD); 226 login(CUSTOMER_USER_EMAIL, CUSTOMER_USER_PASSWORD);
227 } 227 }
228 228
  229 + protected void loginUser(String userName, String password) throws Exception {
  230 + login(userName, password);
  231 + }
  232 +
229 private Tenant savedDifferentTenant; 233 private Tenant savedDifferentTenant;
230 234
231 protected void loginDifferentTenant() throws Exception { 235 protected void loginDifferentTenant() throws Exception {
@@ -251,15 +255,27 @@ public abstract class AbstractWebTest { @@ -251,15 +255,27 @@ public abstract class AbstractWebTest {
251 protected User createUserAndLogin(User user, String password) throws Exception { 255 protected User createUserAndLogin(User user, String password) throws Exception {
252 User savedUser = doPost("/api/user", user, User.class); 256 User savedUser = doPost("/api/user", user, User.class);
253 logout(); 257 logout();
  258 + JsonNode activateRequest = getActivateRequest(password);
  259 + JsonNode tokenInfo = readResponse(doPost("/api/noauth/activate", activateRequest).andExpect(status().isOk()), JsonNode.class);
  260 + validateAndSetJwtToken(tokenInfo, user.getEmail());
  261 + return savedUser;
  262 + }
  263 +
  264 + protected User createUser(User user, String password) throws Exception {
  265 + User savedUser = doPost("/api/user", user, User.class);
  266 + JsonNode activateRequest = getActivateRequest(password);
  267 + ResultActions resultActions = doPost("/api/noauth/activate", activateRequest);
  268 + resultActions.andExpect(status().isOk());
  269 + return savedUser;
  270 + }
  271 +
  272 + private JsonNode getActivateRequest(String password) throws Exception {
254 doGet("/api/noauth/activate?activateToken={activateToken}", TestMailService.currentActivateToken) 273 doGet("/api/noauth/activate?activateToken={activateToken}", TestMailService.currentActivateToken)
255 .andExpect(status().isSeeOther()) 274 .andExpect(status().isSeeOther())
256 .andExpect(header().string(HttpHeaders.LOCATION, "/login/createPassword?activateToken=" + TestMailService.currentActivateToken)); 275 .andExpect(header().string(HttpHeaders.LOCATION, "/login/createPassword?activateToken=" + TestMailService.currentActivateToken));
257 - JsonNode activateRequest = new ObjectMapper().createObjectNode() 276 + return new ObjectMapper().createObjectNode()
258 .put("activateToken", TestMailService.currentActivateToken) 277 .put("activateToken", TestMailService.currentActivateToken)
259 .put("password", password); 278 .put("password", password);
260 - JsonNode tokenInfo = readResponse(doPost("/api/noauth/activate", activateRequest).andExpect(status().isOk()), JsonNode.class);  
261 - validateAndSetJwtToken(tokenInfo, user.getEmail());  
262 - return savedUser;  
263 } 279 }
264 280
265 protected void login(String username, String password) throws Exception { 281 protected void login(String username, String password) throws Exception {
@@ -442,6 +458,10 @@ public abstract class AbstractWebTest { @@ -442,6 +458,10 @@ public abstract class AbstractWebTest {
442 return readResponse(doPostAsync(urlTemplate, content, timeout, params).andExpect(resultMatcher), responseClass); 458 return readResponse(doPostAsync(urlTemplate, content, timeout, params).andExpect(resultMatcher), responseClass);
443 } 459 }
444 460
  461 + protected <T> T doPostClaimAsync(String urlTemplate, Object content, Class<T> responseClass, ResultMatcher resultMatcher, String... params) throws Exception {
  462 + return readResponse(doPostAsync(urlTemplate, content, DEFAULT_TIMEOUT, params).andExpect(resultMatcher), responseClass);
  463 + }
  464 +
445 protected <T> T doDelete(String urlTemplate, Class<T> responseClass, String... params) throws Exception { 465 protected <T> T doDelete(String urlTemplate, Class<T> responseClass, String... params) throws Exception {
446 return readResponse(doDelete(urlTemplate, params).andExpect(status().isOk()), responseClass); 466 return readResponse(doDelete(urlTemplate, params).andExpect(status().isOk()), responseClass);
447 } 467 }
  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 +}
@@ -27,7 +27,11 @@ import java.util.Arrays; @@ -27,7 +27,11 @@ import java.util.Arrays;
27 @RunWith(ClasspathSuite.class) 27 @RunWith(ClasspathSuite.class)
28 @ClasspathSuite.ClassnameFilters({ 28 @ClasspathSuite.ClassnameFilters({
29 "org.thingsboard.server.mqtt.rpc.sql.*Test", 29 "org.thingsboard.server.mqtt.rpc.sql.*Test",
30 - "org.thingsboard.server.mqtt.telemetry.sql.*Test" 30 + "org.thingsboard.server.mqtt.telemetry.timeseries.sql.*Test",
  31 + "org.thingsboard.server.mqtt.telemetry.attributes.sql.*Test",
  32 + "org.thingsboard.server.mqtt.attributes.updates.sql.*Test",
  33 + "org.thingsboard.server.mqtt.attributes.request.sql.*Test",
  34 + "org.thingsboard.server.mqtt.claim.sql.*Test"
31 }) 35 })
32 public class MqttSqlTestSuite { 36 public class MqttSqlTestSuite {
33 37
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.attributes;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
  20 +import org.eclipse.paho.client.mqttv3.MqttCallback;
  21 +import org.eclipse.paho.client.mqttv3.MqttMessage;
  22 +import org.thingsboard.server.common.data.TransportPayloadType;
  23 +import org.thingsboard.server.gen.transport.TransportProtos;
  24 +import org.thingsboard.server.mqtt.AbstractMqttIntegrationTest;
  25 +
  26 +import java.util.ArrayList;
  27 +import java.util.List;
  28 +import java.util.concurrent.CountDownLatch;
  29 +
  30 +@Slf4j
  31 +public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqttIntegrationTest {
  32 +
  33 + protected static final String POST_ATTRIBUTES_PAYLOAD = "{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73," +
  34 + "\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}}";
  35 +
  36 + protected void processBeforeTest(String deviceName, String gatewayName, TransportPayloadType payloadType, String telemetryTopic, String attributesTopic) throws Exception {
  37 + super.processBeforeTest(deviceName, gatewayName, payloadType, telemetryTopic, attributesTopic);
  38 + }
  39 +
  40 + protected void processAfterTest() throws Exception {
  41 + super.processAfterTest();
  42 + }
  43 +
  44 + protected List<TransportProtos.TsKvProto> getTsKvProtoList() {
  45 + TransportProtos.TsKvProto tsKvProtoAttribute1 = getTsKvProto("attribute1", "value1", TransportProtos.KeyValueType.STRING_V);
  46 + TransportProtos.TsKvProto tsKvProtoAttribute2 = getTsKvProto("attribute2", "true", TransportProtos.KeyValueType.BOOLEAN_V);
  47 + TransportProtos.TsKvProto tsKvProtoAttribute3 = getTsKvProto("attribute3", "42.0", TransportProtos.KeyValueType.DOUBLE_V);
  48 + TransportProtos.TsKvProto tsKvProtoAttribute4 = getTsKvProto("attribute4", "73", TransportProtos.KeyValueType.LONG_V);
  49 + TransportProtos.TsKvProto tsKvProtoAttribute5 = getTsKvProto("attribute5", "{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}", TransportProtos.KeyValueType.JSON_V);
  50 + List<TransportProtos.TsKvProto> tsKvProtoList = new ArrayList<>();
  51 + tsKvProtoList.add(tsKvProtoAttribute1);
  52 + tsKvProtoList.add(tsKvProtoAttribute2);
  53 + tsKvProtoList.add(tsKvProtoAttribute3);
  54 + tsKvProtoList.add(tsKvProtoAttribute4);
  55 + tsKvProtoList.add(tsKvProtoAttribute5);
  56 + return tsKvProtoList;
  57 + }
  58 +
  59 +
  60 + protected TransportProtos.TsKvProto getTsKvProto(String key, String value, TransportProtos.KeyValueType keyValueType) {
  61 + TransportProtos.TsKvProto.Builder tsKvProtoBuilder = TransportProtos.TsKvProto.newBuilder();
  62 + TransportProtos.KeyValueProto keyValueProto = getKeyValueProto(key, value, keyValueType);
  63 + tsKvProtoBuilder.setKv(keyValueProto);
  64 + return tsKvProtoBuilder.build();
  65 + }
  66 +
  67 + protected TestMqttCallback getTestMqttCallback() {
  68 + CountDownLatch latch = new CountDownLatch(1);
  69 + return new TestMqttCallback(latch);
  70 + }
  71 +
  72 + protected static class TestMqttCallback implements MqttCallback {
  73 +
  74 + private final CountDownLatch latch;
  75 + private Integer qoS;
  76 + private byte[] payloadBytes;
  77 +
  78 + TestMqttCallback(CountDownLatch latch) {
  79 + this.latch = latch;
  80 + }
  81 +
  82 + public int getQoS() {
  83 + return qoS;
  84 + }
  85 +
  86 + public byte[] getPayloadBytes() {
  87 + return payloadBytes;
  88 + }
  89 +
  90 + public CountDownLatch getLatch() {
  91 + return latch;
  92 + }
  93 +
  94 + @Override
  95 + public void connectionLost(Throwable throwable) {
  96 + }
  97 +
  98 + @Override
  99 + public void messageArrived(String requestTopic, MqttMessage mqttMessage) throws Exception {
  100 + qoS = mqttMessage.getQos();
  101 + payloadBytes = mqttMessage.getPayload();
  102 + latch.countDown();
  103 + }
  104 +
  105 + @Override
  106 + public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
  107 +
  108 + }
  109 + }
  110 +
  111 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.attributes.request;
  17 +
  18 +import com.google.protobuf.InvalidProtocolBufferException;
  19 +import io.netty.handler.codec.mqtt.MqttQoS;
  20 +import lombok.extern.slf4j.Slf4j;
  21 +import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
  22 +import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
  23 +import org.eclipse.paho.client.mqttv3.MqttCallback;
  24 +import org.eclipse.paho.client.mqttv3.MqttException;
  25 +import org.eclipse.paho.client.mqttv3.MqttMessage;
  26 +import org.junit.After;
  27 +import org.junit.Before;
  28 +import org.junit.Test;
  29 +import org.thingsboard.server.common.data.Device;
  30 +import org.thingsboard.server.common.data.device.profile.MqttTopics;
  31 +import org.thingsboard.server.dao.util.mapping.JacksonUtil;
  32 +import org.thingsboard.server.mqtt.attributes.AbstractMqttAttributesIntegrationTest;
  33 +
  34 +import java.nio.charset.StandardCharsets;
  35 +import java.util.concurrent.CountDownLatch;
  36 +import java.util.concurrent.TimeUnit;
  37 +
  38 +import static org.junit.Assert.assertEquals;
  39 +import static org.junit.Assert.assertFalse;
  40 +import static org.junit.Assert.assertNotNull;
  41 +import static org.junit.Assert.assertTrue;
  42 +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
  43 +
  44 +@Slf4j
  45 +public abstract class AbstractMqttAttributesRequestIntegrationTest extends AbstractMqttAttributesIntegrationTest {
  46 +
  47 + @Before
  48 + public void beforeTest() throws Exception {
  49 + processBeforeTest("Test Request attribute values from the server", "Gateway Test Request attribute values from the server", null, null, null);
  50 + }
  51 +
  52 + @After
  53 + public void afterTest() throws Exception {
  54 + processAfterTest();
  55 + }
  56 +
  57 + @Test
  58 + public void testRequestAttributesValuesFromTheServer() throws Exception {
  59 + processTestRequestAttributesValuesFromTheServer();
  60 + }
  61 +
  62 + @Test
  63 + public void testRequestAttributesValuesFromTheServerGateway() throws Exception {
  64 + processTestGatewayRequestAttributesValuesFromTheServer();
  65 + }
  66 +
  67 + protected void processTestRequestAttributesValuesFromTheServer() throws Exception {
  68 +
  69 + MqttAsyncClient client = getMqttAsyncClient(accessToken);
  70 +
  71 + postAttributesAndSubscribeToTopic(savedDevice, client);
  72 +
  73 + Thread.sleep(1000);
  74 +
  75 + TestMqttCallback callback = getTestMqttCallback();
  76 + client.setCallback(callback);
  77 +
  78 + validateResponse(client, callback.getLatch(), callback);
  79 + }
  80 +
  81 + protected void processTestGatewayRequestAttributesValuesFromTheServer() throws Exception {
  82 +
  83 + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
  84 +
  85 + postGatewayDeviceClientAttributes(client);
  86 +
  87 + Thread.sleep(1000);
  88 +
  89 + Device savedDevice = doGet("/api/tenant/devices?deviceName=" + "Gateway Device Request Attributes", Device.class);
  90 + assertNotNull(savedDevice);
  91 + doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk());
  92 +
  93 + Thread.sleep(1000);
  94 +
  95 + client.subscribe(MqttTopics.GATEWAY_ATTRIBUTES_RESPONSE_TOPIC, MqttQoS.AT_LEAST_ONCE.value());
  96 +
  97 + TestMqttCallback clientAttributesCallback = getTestMqttCallback();
  98 + client.setCallback(clientAttributesCallback);
  99 + validateClientResponseGateway(client, clientAttributesCallback);
  100 +
  101 + TestMqttCallback sharedAttributesCallback = getTestMqttCallback();
  102 + client.setCallback(sharedAttributesCallback);
  103 + validateSharedResponseGateway(client, sharedAttributesCallback);
  104 + }
  105 +
  106 + protected void postAttributesAndSubscribeToTopic(Device savedDevice, MqttAsyncClient client) throws Exception {
  107 + doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk());
  108 + client.publish(MqttTopics.DEVICE_ATTRIBUTES_TOPIC, new MqttMessage(POST_ATTRIBUTES_PAYLOAD.getBytes()));
  109 + client.subscribe(MqttTopics.DEVICE_ATTRIBUTES_RESPONSES_TOPIC, MqttQoS.AT_MOST_ONCE.value());
  110 + }
  111 +
  112 + protected void postGatewayDeviceClientAttributes(MqttAsyncClient client) throws Exception {
  113 + String postClientAttributes = "{\"" + "Gateway Device Request Attributes" + "\":{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73,\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}}}";
  114 + client.publish(MqttTopics.GATEWAY_ATTRIBUTES_TOPIC, new MqttMessage(postClientAttributes.getBytes()));
  115 + }
  116 +
  117 + protected void validateResponse(MqttAsyncClient client, CountDownLatch latch, TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException {
  118 + String keys = "attribute1,attribute2,attribute3,attribute4,attribute5";
  119 + String payloadStr = "{\"clientKeys\":\"" + keys + "\", \"sharedKeys\":\"" + keys + "\"}";
  120 + MqttMessage mqttMessage = new MqttMessage();
  121 + mqttMessage.setPayload(payloadStr.getBytes());
  122 + client.publish(MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX + "1", mqttMessage);
  123 + latch.await(3, TimeUnit.SECONDS);
  124 + assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS());
  125 + String expectedRequestPayload = "{\"client\":{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73,\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}},\"shared\":{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73,\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}}}";
  126 + assertEquals(JacksonUtil.toJsonNode(expectedRequestPayload), JacksonUtil.toJsonNode(new String(callback.getPayloadBytes(), StandardCharsets.UTF_8)));
  127 + }
  128 +
  129 + protected void validateClientResponseGateway(MqttAsyncClient client, TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException {
  130 + String payloadStr = "{\"id\": 1, \"device\": \"" + "Gateway Device Request Attributes" + "\", \"client\": true, \"keys\": [\"attribute1\", \"attribute2\", \"attribute3\", \"attribute4\", \"attribute5\"]}";
  131 + MqttMessage mqttMessage = new MqttMessage();
  132 + mqttMessage.setPayload(payloadStr.getBytes());
  133 + client.publish(MqttTopics.GATEWAY_ATTRIBUTES_REQUEST_TOPIC, mqttMessage);
  134 + callback.getLatch().await(3, TimeUnit.SECONDS);
  135 + assertEquals(MqttQoS.AT_LEAST_ONCE.value(), callback.getQoS());
  136 + String expectedRequestPayload = "{\"id\":1,\"device\":\"" + "Gateway Device Request Attributes" + "\",\"values\":{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73,\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}}}";
  137 + assertEquals(JacksonUtil.toJsonNode(expectedRequestPayload), JacksonUtil.toJsonNode(new String(callback.getPayloadBytes(), StandardCharsets.UTF_8)));
  138 + }
  139 +
  140 + protected void validateSharedResponseGateway(MqttAsyncClient client, TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException {
  141 + String payloadStr = "{\"id\": 1, \"device\": \"" + "Gateway Device Request Attributes" + "\", \"client\": false, \"keys\": [\"attribute1\", \"attribute2\", \"attribute3\", \"attribute4\", \"attribute5\"]}";
  142 + MqttMessage mqttMessage = new MqttMessage();
  143 + mqttMessage.setPayload(payloadStr.getBytes());
  144 + client.publish(MqttTopics.GATEWAY_ATTRIBUTES_REQUEST_TOPIC, mqttMessage);
  145 + callback.getLatch().await(3, TimeUnit.SECONDS);
  146 + assertEquals(MqttQoS.AT_LEAST_ONCE.value(), callback.getQoS());
  147 + String expectedRequestPayload = "{\"id\":1,\"device\":\"" + "Gateway Device Request Attributes" + "\",\"values\":{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73,\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}}}";
  148 + assertEquals(JacksonUtil.toJsonNode(expectedRequestPayload), JacksonUtil.toJsonNode(new String(callback.getPayloadBytes(), StandardCharsets.UTF_8)));
  149 + }
  150 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.attributes.request;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.junit.After;
  20 +import org.junit.Before;
  21 +import org.junit.Ignore;
  22 +import org.junit.Test;
  23 +import org.thingsboard.server.common.data.TransportPayloadType;
  24 +
  25 +import static org.junit.Assert.assertEquals;
  26 +import static org.junit.Assert.assertNotNull;
  27 +import static org.junit.Assert.assertTrue;
  28 +
  29 +@Slf4j
  30 +public abstract class AbstractMqttAttributesRequestJsonIntegrationTest extends AbstractMqttAttributesRequestIntegrationTest {
  31 +
  32 + @Before
  33 + public void beforeTest() throws Exception {
  34 + processBeforeTest("Test Request attribute values from the server json", "Gateway Test Request attribute values from the server json", TransportPayloadType.JSON, null, null);
  35 + }
  36 +
  37 + @After
  38 + public void afterTest() throws Exception {
  39 + processAfterTest();
  40 + }
  41 +
  42 + @Test
  43 + public void testRequestAttributesValuesFromTheServer() throws Exception {
  44 + processTestRequestAttributesValuesFromTheServer();
  45 + }
  46 +
  47 + @Test
  48 + public void testRequestAttributesValuesFromTheServerGateway() throws Exception {
  49 + processTestGatewayRequestAttributesValuesFromTheServer();
  50 + }
  51 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.attributes.request;
  17 +
  18 +import com.google.protobuf.InvalidProtocolBufferException;
  19 +import io.netty.handler.codec.mqtt.MqttQoS;
  20 +import lombok.extern.slf4j.Slf4j;
  21 +import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
  22 +import org.eclipse.paho.client.mqttv3.MqttException;
  23 +import org.eclipse.paho.client.mqttv3.MqttMessage;
  24 +import org.junit.After;
  25 +import org.junit.Before;
  26 +import org.junit.Test;
  27 +import org.thingsboard.server.common.data.Device;
  28 +import org.thingsboard.server.common.data.TransportPayloadType;
  29 +import org.thingsboard.server.common.data.device.profile.MqttTopics;
  30 +import org.thingsboard.server.gen.transport.TransportApiProtos;
  31 +import org.thingsboard.server.gen.transport.TransportProtos;
  32 +
  33 +import java.util.ArrayList;
  34 +import java.util.Arrays;
  35 +import java.util.List;
  36 +import java.util.concurrent.CountDownLatch;
  37 +import java.util.concurrent.TimeUnit;
  38 +import java.util.stream.Collectors;
  39 +
  40 +import static org.junit.Assert.assertEquals;
  41 +import static org.junit.Assert.assertTrue;
  42 +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
  43 +
  44 +@Slf4j
  45 +public abstract class AbstractMqttAttributesRequestProtoIntegrationTest extends AbstractMqttAttributesRequestIntegrationTest {
  46 +
  47 + @Before
  48 + public void beforeTest() throws Exception {
  49 + processBeforeTest("Test Request attribute values from the server proto", "Gateway Test Request attribute values from the server proto", TransportPayloadType.PROTOBUF, null, null);
  50 + }
  51 +
  52 + @After
  53 + public void afterTest() throws Exception {
  54 + processAfterTest();
  55 + }
  56 +
  57 + @Test
  58 + public void testRequestAttributesValuesFromTheServer() throws Exception {
  59 + processTestRequestAttributesValuesFromTheServer();
  60 + }
  61 +
  62 +
  63 + @Test
  64 + public void testRequestAttributesValuesFromTheServerGateway() throws Exception {
  65 + processTestGatewayRequestAttributesValuesFromTheServer();
  66 + }
  67 +
  68 + protected void postAttributesAndSubscribeToTopic(Device savedDevice, MqttAsyncClient client) throws Exception {
  69 + doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk());
  70 + String keys = "attribute1,attribute2,attribute3,attribute4,attribute5";
  71 + List<String> expectedKeys = Arrays.asList(keys.split(","));
  72 + TransportProtos.PostAttributeMsg postAttributeMsg = getPostAttributeMsg(expectedKeys);
  73 + byte[] payload = postAttributeMsg.toByteArray();
  74 + client.publish(MqttTopics.DEVICE_ATTRIBUTES_TOPIC, new MqttMessage(payload));
  75 + client.subscribe(MqttTopics.DEVICE_ATTRIBUTES_RESPONSES_TOPIC, MqttQoS.AT_MOST_ONCE.value());
  76 + }
  77 +
  78 + protected void postGatewayDeviceClientAttributes(MqttAsyncClient client) throws Exception {
  79 + String keys = "attribute1,attribute2,attribute3,attribute4,attribute5";
  80 + List<String> expectedKeys = Arrays.asList(keys.split(","));
  81 + TransportProtos.PostAttributeMsg postAttributeMsg = getPostAttributeMsg(expectedKeys);
  82 + TransportApiProtos.AttributesMsg.Builder attributesMsgBuilder = TransportApiProtos.AttributesMsg.newBuilder();
  83 + attributesMsgBuilder.setDeviceName("Gateway Device Request Attributes");
  84 + attributesMsgBuilder.setMsg(postAttributeMsg);
  85 + TransportApiProtos.AttributesMsg attributesMsg = attributesMsgBuilder.build();
  86 + TransportApiProtos.GatewayAttributesMsg.Builder gatewayAttributeMsgBuilder = TransportApiProtos.GatewayAttributesMsg.newBuilder();
  87 + gatewayAttributeMsgBuilder.addMsg(attributesMsg);
  88 + byte[] bytes = gatewayAttributeMsgBuilder.build().toByteArray();
  89 + client.publish(MqttTopics.GATEWAY_ATTRIBUTES_TOPIC, new MqttMessage(bytes));
  90 + }
  91 +
  92 + protected void validateResponse(MqttAsyncClient client, CountDownLatch latch, TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException {
  93 + String keys = "attribute1,attribute2,attribute3,attribute4,attribute5";
  94 + TransportApiProtos.AttributesRequest.Builder attributesRequestBuilder = TransportApiProtos.AttributesRequest.newBuilder();
  95 + attributesRequestBuilder.setClientKeys(keys);
  96 + attributesRequestBuilder.setSharedKeys(keys);
  97 + TransportApiProtos.AttributesRequest attributesRequest = attributesRequestBuilder.build();
  98 + MqttMessage mqttMessage = new MqttMessage();
  99 + mqttMessage.setPayload(attributesRequest.toByteArray());
  100 + client.publish(MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX + "1", mqttMessage);
  101 + latch.await(3, TimeUnit.SECONDS);
  102 + assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS());
  103 + TransportProtos.GetAttributeResponseMsg expectedAttributesResponse = getExpectedAttributeResponseMsg();
  104 + TransportProtos.GetAttributeResponseMsg actualAttributesResponse = TransportProtos.GetAttributeResponseMsg.parseFrom(callback.getPayloadBytes());
  105 + assertEquals(expectedAttributesResponse.getRequestId(), actualAttributesResponse.getRequestId());
  106 + List<TransportProtos.KeyValueProto> expectedClientKeyValueProtos = expectedAttributesResponse.getClientAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList());
  107 + List<TransportProtos.KeyValueProto> expectedSharedKeyValueProtos = expectedAttributesResponse.getSharedAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList());
  108 + List<TransportProtos.KeyValueProto> actualClientKeyValueProtos = actualAttributesResponse.getClientAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList());
  109 + List<TransportProtos.KeyValueProto> actualSharedKeyValueProtos = actualAttributesResponse.getSharedAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList());
  110 + assertTrue(actualClientKeyValueProtos.containsAll(expectedClientKeyValueProtos));
  111 + assertTrue(actualSharedKeyValueProtos.containsAll(expectedSharedKeyValueProtos));
  112 + }
  113 +
  114 + protected void validateClientResponseGateway(MqttAsyncClient client, TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException {
  115 + String keys = "attribute1,attribute2,attribute3,attribute4,attribute5";
  116 + TransportApiProtos.GatewayAttributesRequestMsg gatewayAttributesRequestMsg = getGatewayAttributesRequestMsg(keys, true);
  117 + client.publish(MqttTopics.GATEWAY_ATTRIBUTES_REQUEST_TOPIC, new MqttMessage(gatewayAttributesRequestMsg.toByteArray()));
  118 + callback.getLatch().await(3, TimeUnit.SECONDS);
  119 + assertEquals(MqttQoS.AT_LEAST_ONCE.value(), callback.getQoS());
  120 + TransportApiProtos.GatewayAttributeResponseMsg expectedGatewayAttributeResponseMsg = getExpectedGatewayAttributeResponseMsg(true);
  121 + TransportApiProtos.GatewayAttributeResponseMsg actualGatewayAttributeResponseMsg = TransportApiProtos.GatewayAttributeResponseMsg.parseFrom(callback.getPayloadBytes());
  122 + assertEquals(expectedGatewayAttributeResponseMsg.getDeviceName(), actualGatewayAttributeResponseMsg.getDeviceName());
  123 +
  124 + TransportProtos.GetAttributeResponseMsg expectedResponseMsg = expectedGatewayAttributeResponseMsg.getResponseMsg();
  125 + TransportProtos.GetAttributeResponseMsg actualResponseMsg = actualGatewayAttributeResponseMsg.getResponseMsg();
  126 + assertEquals(expectedResponseMsg.getRequestId(), actualResponseMsg.getRequestId());
  127 +
  128 + List<TransportProtos.KeyValueProto> expectedClientKeyValueProtos = expectedResponseMsg.getClientAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList());
  129 + List<TransportProtos.KeyValueProto> actualClientKeyValueProtos = actualResponseMsg.getClientAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList());
  130 + assertTrue(actualClientKeyValueProtos.containsAll(expectedClientKeyValueProtos));
  131 + }
  132 +
  133 + protected void validateSharedResponseGateway(MqttAsyncClient client, TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException {
  134 + String keys = "attribute1,attribute2,attribute3,attribute4,attribute5";
  135 + TransportApiProtos.GatewayAttributesRequestMsg gatewayAttributesRequestMsg = getGatewayAttributesRequestMsg(keys, false);
  136 + client.publish(MqttTopics.GATEWAY_ATTRIBUTES_REQUEST_TOPIC, new MqttMessage(gatewayAttributesRequestMsg.toByteArray()));
  137 + callback.getLatch().await(3, TimeUnit.SECONDS);
  138 + assertEquals(MqttQoS.AT_LEAST_ONCE.value(), callback.getQoS());
  139 + TransportApiProtos.GatewayAttributeResponseMsg expectedGatewayAttributeResponseMsg = getExpectedGatewayAttributeResponseMsg(false);
  140 + TransportApiProtos.GatewayAttributeResponseMsg actualGatewayAttributeResponseMsg = TransportApiProtos.GatewayAttributeResponseMsg.parseFrom(callback.getPayloadBytes());
  141 + assertEquals(expectedGatewayAttributeResponseMsg.getDeviceName(), actualGatewayAttributeResponseMsg.getDeviceName());
  142 +
  143 + TransportProtos.GetAttributeResponseMsg expectedResponseMsg = expectedGatewayAttributeResponseMsg.getResponseMsg();
  144 + TransportProtos.GetAttributeResponseMsg actualResponseMsg = actualGatewayAttributeResponseMsg.getResponseMsg();
  145 + assertEquals(expectedResponseMsg.getRequestId(), actualResponseMsg.getRequestId());
  146 +
  147 + List<TransportProtos.KeyValueProto> expectedSharedKeyValueProtos = expectedResponseMsg.getSharedAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList());
  148 + List<TransportProtos.KeyValueProto> actualSharedKeyValueProtos = actualResponseMsg.getSharedAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList());
  149 +
  150 + assertTrue(actualSharedKeyValueProtos.containsAll(expectedSharedKeyValueProtos));
  151 + }
  152 +
  153 + private TransportApiProtos.GatewayAttributesRequestMsg getGatewayAttributesRequestMsg(String keys, boolean client) {
  154 + return TransportApiProtos.GatewayAttributesRequestMsg.newBuilder()
  155 + .setClient(client)
  156 + .addAllKeys(Arrays.asList(keys.split(",")))
  157 + .setDeviceName("Gateway Device Request Attributes")
  158 + .setId(1).build();
  159 + }
  160 +
  161 + private TransportProtos.GetAttributeResponseMsg getExpectedAttributeResponseMsg() {
  162 + TransportProtos.GetAttributeResponseMsg.Builder result = TransportProtos.GetAttributeResponseMsg.newBuilder();
  163 + List<TransportProtos.TsKvProto> tsKvProtoList = getTsKvProtoList();
  164 + result.addAllClientAttributeList(tsKvProtoList);
  165 + result.addAllSharedAttributeList(tsKvProtoList);
  166 + result.setRequestId(1);
  167 + return result.build();
  168 + }
  169 +
  170 + private TransportApiProtos.GatewayAttributeResponseMsg getExpectedGatewayAttributeResponseMsg(boolean client) {
  171 + TransportApiProtos.GatewayAttributeResponseMsg.Builder gatewayAttributeResponseMsg = TransportApiProtos.GatewayAttributeResponseMsg.newBuilder();
  172 + TransportProtos.GetAttributeResponseMsg.Builder getAttributeResponseMsgBuilder = TransportProtos.GetAttributeResponseMsg.newBuilder();
  173 + List<TransportProtos.TsKvProto> tsKvProtoList = getTsKvProtoList();
  174 + if (client) {
  175 + getAttributeResponseMsgBuilder.addAllClientAttributeList(tsKvProtoList);
  176 + } else {
  177 + getAttributeResponseMsgBuilder.addAllSharedAttributeList(tsKvProtoList);
  178 + }
  179 + getAttributeResponseMsgBuilder.setRequestId(1);
  180 + TransportProtos.GetAttributeResponseMsg getAttributeResponseMsg = getAttributeResponseMsgBuilder.build();
  181 + gatewayAttributeResponseMsg.setDeviceName("Gateway Device Request Attributes");
  182 + gatewayAttributeResponseMsg.setResponseMsg(getAttributeResponseMsg);
  183 + return gatewayAttributeResponseMsg.build();
  184 + }
  185 +
  186 + protected List<TransportProtos.KeyValueProto> getKvProtos(List<String> expectedKeys) {
  187 + List<TransportProtos.KeyValueProto> keyValueProtos = new ArrayList<>();
  188 + TransportProtos.KeyValueProto strKeyValueProto = getKeyValueProto(expectedKeys.get(0), "value1", TransportProtos.KeyValueType.STRING_V);
  189 + TransportProtos.KeyValueProto boolKeyValueProto = getKeyValueProto(expectedKeys.get(1), "true", TransportProtos.KeyValueType.BOOLEAN_V);
  190 + TransportProtos.KeyValueProto dblKeyValueProto = getKeyValueProto(expectedKeys.get(2), "42.0", TransportProtos.KeyValueType.DOUBLE_V);
  191 + TransportProtos.KeyValueProto longKeyValueProto = getKeyValueProto(expectedKeys.get(3), "73", TransportProtos.KeyValueType.LONG_V);
  192 + TransportProtos.KeyValueProto jsonKeyValueProto = getKeyValueProto(expectedKeys.get(4), "{\"someNumber\": 42, \"someArray\": [1,2,3], \"someNestedObject\": {\"key\": \"value\"}}", TransportProtos.KeyValueType.JSON_V);
  193 + keyValueProtos.add(strKeyValueProto);
  194 + keyValueProtos.add(boolKeyValueProto);
  195 + keyValueProtos.add(dblKeyValueProto);
  196 + keyValueProtos.add(longKeyValueProto);
  197 + keyValueProtos.add(jsonKeyValueProto);
  198 + return keyValueProtos;
  199 + }
  200 +
  201 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.attributes.request.nosql;
  17 +
  18 +import org.thingsboard.server.dao.service.DaoNoSqlTest;
  19 +import org.thingsboard.server.mqtt.attributes.request.AbstractMqttAttributesRequestIntegrationTest;
  20 +
  21 +
  22 +@DaoNoSqlTest
  23 +public class MqttAttributesRequestNoSqlIntegrationTest extends AbstractMqttAttributesRequestIntegrationTest {
  24 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.attributes.request.sql;
  17 +
  18 +import org.thingsboard.server.dao.service.DaoSqlTest;
  19 +import org.thingsboard.server.mqtt.attributes.request.AbstractMqttAttributesRequestIntegrationTest;
  20 +import org.thingsboard.server.mqtt.attributes.request.AbstractMqttAttributesRequestJsonIntegrationTest;
  21 +
  22 +@DaoSqlTest
  23 +public class MqttAttributesRequestJsonSqlIntegrationTest extends AbstractMqttAttributesRequestJsonIntegrationTest {
  24 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.attributes.request.sql;
  17 +
  18 +import org.thingsboard.server.dao.service.DaoSqlTest;
  19 +import org.thingsboard.server.mqtt.attributes.request.AbstractMqttAttributesRequestJsonIntegrationTest;
  20 +import org.thingsboard.server.mqtt.attributes.request.AbstractMqttAttributesRequestProtoIntegrationTest;
  21 +
  22 +@DaoSqlTest
  23 +public class MqttAttributesRequestProtoSqlIntegrationTest extends AbstractMqttAttributesRequestProtoIntegrationTest {
  24 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.attributes.request.sql;
  17 +
  18 +import org.thingsboard.server.dao.service.DaoSqlTest;
  19 +import org.thingsboard.server.mqtt.attributes.request.AbstractMqttAttributesRequestIntegrationTest;
  20 +
  21 +@DaoSqlTest
  22 +public class MqttAttributesRequestSqlIntegrationTest extends AbstractMqttAttributesRequestIntegrationTest {
  23 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.attributes.updates;
  17 +
  18 +import com.google.protobuf.InvalidProtocolBufferException;
  19 +import io.netty.handler.codec.mqtt.MqttQoS;
  20 +import lombok.extern.slf4j.Slf4j;
  21 +import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
  22 +import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
  23 +import org.eclipse.paho.client.mqttv3.MqttCallback;
  24 +import org.eclipse.paho.client.mqttv3.MqttException;
  25 +import org.eclipse.paho.client.mqttv3.MqttMessage;
  26 +import org.junit.After;
  27 +import org.junit.Before;
  28 +import org.junit.Test;
  29 +import org.thingsboard.server.common.data.Device;
  30 +import org.thingsboard.server.common.data.TransportPayloadType;
  31 +import org.thingsboard.server.common.data.device.profile.MqttTopics;
  32 +import org.thingsboard.server.dao.util.mapping.JacksonUtil;
  33 +import org.thingsboard.server.mqtt.attributes.AbstractMqttAttributesIntegrationTest;
  34 +
  35 +import java.nio.charset.StandardCharsets;
  36 +import java.util.concurrent.CountDownLatch;
  37 +import java.util.concurrent.TimeUnit;
  38 +
  39 +import static org.junit.Assert.assertEquals;
  40 +import static org.junit.Assert.assertNotNull;
  41 +import static org.junit.Assert.assertTrue;
  42 +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
  43 +
  44 +@Slf4j
  45 +public abstract class AbstractMqttAttributesUpdatesIntegrationTest extends AbstractMqttAttributesIntegrationTest {
  46 +
  47 + private static final String RESPONSE_ATTRIBUTES_PAYLOAD_DELETED = "{\"deleted\":[\"attribute5\"]}";
  48 +
  49 + private static String getResponseGatewayAttributesUpdatedPayload() {
  50 + return "{\"device\":\"" + "Gateway Device Subscribe to attribute updates" + "\"," +
  51 + "\"data\":{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73,\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}}}";
  52 + }
  53 +
  54 + private static String getResponseGatewayAttributesDeletedPayload() {
  55 + return "{\"device\":\"" + "Gateway Device Subscribe to attribute updates" + "\",\"data\":{\"deleted\":[\"attribute5\"]}}";
  56 + }
  57 +
  58 + @Before
  59 + public void beforeTest() throws Exception {
  60 + processBeforeTest("Test Subscribe to attribute updates", "Gateway Test Subscribe to attribute updates", TransportPayloadType.JSON, null, null);
  61 + }
  62 +
  63 + @After
  64 + public void afterTest() throws Exception {
  65 + processAfterTest();
  66 + }
  67 +
  68 + @Test
  69 + public void testSubscribeToAttributesUpdatesFromTheServer() throws Exception {
  70 + processTestSubscribeToAttributesUpdates();
  71 + }
  72 +
  73 + @Test
  74 + public void testSubscribeToAttributesUpdatesFromTheServerGateway() throws Exception {
  75 + processGatewayTestSubscribeToAttributesUpdates();
  76 + }
  77 +
  78 + protected void processTestSubscribeToAttributesUpdates() throws Exception {
  79 +
  80 + MqttAsyncClient client = getMqttAsyncClient(accessToken);
  81 +
  82 + TestMqttCallback onUpdateCallback = getTestMqttCallback();
  83 + client.setCallback(onUpdateCallback);
  84 +
  85 + client.subscribe(MqttTopics.DEVICE_ATTRIBUTES_TOPIC, MqttQoS.AT_MOST_ONCE.value());
  86 +
  87 + Thread.sleep(2000);
  88 +
  89 + doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk());
  90 + onUpdateCallback.getLatch().await(3, TimeUnit.SECONDS);
  91 +
  92 + validateUpdateAttributesResponse(onUpdateCallback);
  93 +
  94 + TestMqttCallback onDeleteCallback = getTestMqttCallback();
  95 + client.setCallback(onDeleteCallback);
  96 +
  97 + doDelete("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/SHARED_SCOPE?keys=attribute5", String.class);
  98 + onDeleteCallback.getLatch().await(3, TimeUnit.SECONDS);
  99 +
  100 + validateDeleteAttributesResponse(onDeleteCallback);
  101 + }
  102 +
  103 + protected void validateUpdateAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException {
  104 + assertNotNull(callback.getPayloadBytes());
  105 + String response = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8);
  106 + assertEquals(JacksonUtil.toJsonNode(POST_ATTRIBUTES_PAYLOAD), JacksonUtil.toJsonNode(response));
  107 + }
  108 +
  109 + protected void validateDeleteAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException {
  110 + assertNotNull(callback.getPayloadBytes());
  111 + String response = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8);
  112 + assertEquals(JacksonUtil.toJsonNode(RESPONSE_ATTRIBUTES_PAYLOAD_DELETED), JacksonUtil.toJsonNode(response));
  113 + }
  114 +
  115 + protected void processGatewayTestSubscribeToAttributesUpdates() throws Exception {
  116 +
  117 + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
  118 +
  119 + TestMqttCallback onUpdateCallback = getTestMqttCallback();
  120 + client.setCallback(onUpdateCallback);
  121 +
  122 + Device device = new Device();
  123 + device.setName("Gateway Device Subscribe to attribute updates");
  124 + device.setType("default");
  125 +
  126 + byte[] connectPayloadBytes = getConnectPayloadBytes();
  127 +
  128 + publishMqttMsg(client, connectPayloadBytes, MqttTopics.GATEWAY_CONNECT_TOPIC);
  129 +
  130 + Thread.sleep(1000);
  131 +
  132 + Device savedDevice = doGet("/api/tenant/devices?deviceName=" + "Gateway Device Subscribe to attribute updates", Device.class);
  133 + assertNotNull(savedDevice);
  134 +
  135 + client.subscribe(MqttTopics.GATEWAY_ATTRIBUTES_TOPIC, MqttQoS.AT_MOST_ONCE.value());
  136 +
  137 + Thread.sleep(2000);
  138 +
  139 + doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk());
  140 + onUpdateCallback.getLatch().await(3, TimeUnit.SECONDS);
  141 +
  142 + validateGatewayUpdateAttributesResponse(onUpdateCallback);
  143 +
  144 + TestMqttCallback onDeleteCallback = getTestMqttCallback();
  145 + client.setCallback(onDeleteCallback);
  146 +
  147 + doDelete("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/SHARED_SCOPE?keys=attribute5", String.class);
  148 + onDeleteCallback.getLatch().await(3, TimeUnit.SECONDS);
  149 +
  150 + validateGatewayDeleteAttributesResponse(onDeleteCallback);
  151 +
  152 + }
  153 +
  154 + protected void validateGatewayUpdateAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException {
  155 + assertNotNull(callback.getPayloadBytes());
  156 + String s = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8);
  157 + assertEquals(getResponseGatewayAttributesUpdatedPayload(), s);
  158 + }
  159 +
  160 + protected void validateGatewayDeleteAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException {
  161 + assertNotNull(callback.getPayloadBytes());
  162 + String s = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8);
  163 + assertEquals(s, getResponseGatewayAttributesDeletedPayload());
  164 + }
  165 +
  166 + protected byte[] getConnectPayloadBytes() {
  167 + String connectPayload = "{\"device\": \"Gateway Device Subscribe to attribute updates\", \"type\": \"" + TransportPayloadType.JSON.name() + "\"}";
  168 + return connectPayload.getBytes();
  169 + }
  170 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.attributes.updates;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.junit.After;
  20 +import org.junit.Before;
  21 +import org.junit.Test;
  22 +import org.thingsboard.server.common.data.TransportPayloadType;
  23 +
  24 +import static org.junit.Assert.assertEquals;
  25 +import static org.junit.Assert.assertFalse;
  26 +import static org.junit.Assert.assertNotNull;
  27 +import static org.junit.Assert.assertTrue;
  28 +
  29 +@Slf4j
  30 +public abstract class AbstractMqttAttributesUpdatesJsonIntegrationTest extends AbstractMqttAttributesUpdatesIntegrationTest {
  31 +
  32 + @Before
  33 + public void beforeTest() throws Exception {
  34 + processBeforeTest("Test Subscribe to attribute updates", "Gateway Test Subscribe to attribute updates", TransportPayloadType.JSON, null, null);
  35 + }
  36 +
  37 + @After
  38 + public void afterTest() throws Exception {
  39 + processAfterTest();
  40 + }
  41 +
  42 + @Test
  43 + public void testSubscribeToAttributesUpdatesFromTheServer() throws Exception {
  44 + processTestSubscribeToAttributesUpdates();
  45 + }
  46 +
  47 + @Test
  48 + public void testSubscribeToAttributesUpdatesFromTheServerGateway() throws Exception {
  49 + processGatewayTestSubscribeToAttributesUpdates();
  50 + }
  51 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.attributes.updates;
  17 +
  18 +import com.google.protobuf.InvalidProtocolBufferException;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.junit.After;
  21 +import org.junit.Before;
  22 +import org.junit.Test;
  23 +import org.thingsboard.server.common.data.TransportPayloadType;
  24 +import org.thingsboard.server.common.data.device.profile.MqttTopics;
  25 +import org.thingsboard.server.gen.transport.TransportApiProtos;
  26 +import org.thingsboard.server.gen.transport.TransportProtos;
  27 +
  28 +import java.nio.charset.StandardCharsets;
  29 +import java.util.List;
  30 +import java.util.stream.Collectors;
  31 +
  32 +import static org.junit.Assert.assertEquals;
  33 +import static org.junit.Assert.assertNotNull;
  34 +import static org.junit.Assert.assertTrue;
  35 +
  36 +@Slf4j
  37 +public abstract class AbstractMqttAttributesUpdatesProtoIntegrationTest extends AbstractMqttAttributesUpdatesIntegrationTest {
  38 +
  39 + @Before
  40 + public void beforeTest() throws Exception {
  41 + processBeforeTest("Test Subscribe to attribute updates", "Gateway Test Subscribe to attribute updates", TransportPayloadType.PROTOBUF, null, null);
  42 + }
  43 +
  44 + @After
  45 + public void afterTest() throws Exception {
  46 + processAfterTest();
  47 + }
  48 +
  49 + @Test
  50 + public void testSubscribeToAttributesUpdatesFromTheServer() throws Exception {
  51 + processTestSubscribeToAttributesUpdates();
  52 + }
  53 +
  54 + @Test
  55 + public void testSubscribeToAttributesUpdatesFromTheServerGateway() throws Exception {
  56 + processGatewayTestSubscribeToAttributesUpdates();
  57 + }
  58 +
  59 + protected void validateUpdateAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException {
  60 + assertNotNull(callback.getPayloadBytes());
  61 + TransportProtos.AttributeUpdateNotificationMsg.Builder attributeUpdateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder();
  62 + List<TransportProtos.TsKvProto> tsKvProtoList = getTsKvProtoList();
  63 + attributeUpdateNotificationMsgBuilder.addAllSharedUpdated(tsKvProtoList);
  64 +
  65 + TransportProtos.AttributeUpdateNotificationMsg expectedAttributeUpdateNotificationMsg = attributeUpdateNotificationMsgBuilder.build();
  66 + TransportProtos.AttributeUpdateNotificationMsg actualAttributeUpdateNotificationMsg = TransportProtos.AttributeUpdateNotificationMsg.parseFrom(callback.getPayloadBytes());
  67 +
  68 + List<TransportProtos.KeyValueProto> actualSharedUpdatedList = actualAttributeUpdateNotificationMsg.getSharedUpdatedList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList());
  69 + List<TransportProtos.KeyValueProto> expectedSharedUpdatedList = expectedAttributeUpdateNotificationMsg.getSharedUpdatedList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList());
  70 +
  71 + assertEquals(expectedSharedUpdatedList.size(), actualSharedUpdatedList.size());
  72 + assertTrue(actualSharedUpdatedList.containsAll(expectedSharedUpdatedList));
  73 +
  74 + }
  75 +
  76 + protected void validateDeleteAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException {
  77 + assertNotNull(callback.getPayloadBytes());
  78 + TransportProtos.AttributeUpdateNotificationMsg.Builder attributeUpdateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder();
  79 + attributeUpdateNotificationMsgBuilder.addSharedDeleted("attribute5");
  80 +
  81 + TransportProtos.AttributeUpdateNotificationMsg expectedAttributeUpdateNotificationMsg = attributeUpdateNotificationMsgBuilder.build();
  82 + TransportProtos.AttributeUpdateNotificationMsg actualAttributeUpdateNotificationMsg = TransportProtos.AttributeUpdateNotificationMsg.parseFrom(callback.getPayloadBytes());
  83 +
  84 + assertEquals(expectedAttributeUpdateNotificationMsg.getSharedDeletedList().size(), actualAttributeUpdateNotificationMsg.getSharedDeletedList().size());
  85 + assertEquals("attribute5", actualAttributeUpdateNotificationMsg.getSharedDeletedList().get(0));
  86 +
  87 + }
  88 +
  89 + protected void validateGatewayUpdateAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException {
  90 + assertNotNull(callback.getPayloadBytes());
  91 +
  92 + TransportProtos.AttributeUpdateNotificationMsg.Builder attributeUpdateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder();
  93 + List<TransportProtos.TsKvProto> tsKvProtoList = getTsKvProtoList();
  94 + attributeUpdateNotificationMsgBuilder.addAllSharedUpdated(tsKvProtoList);
  95 + TransportProtos.AttributeUpdateNotificationMsg expectedAttributeUpdateNotificationMsg = attributeUpdateNotificationMsgBuilder.build();
  96 +
  97 + TransportApiProtos.GatewayAttributeUpdateNotificationMsg.Builder gatewayAttributeUpdateNotificationMsgBuilder = TransportApiProtos.GatewayAttributeUpdateNotificationMsg.newBuilder();
  98 + gatewayAttributeUpdateNotificationMsgBuilder.setDeviceName("Gateway Device Subscribe to attribute updates");
  99 + gatewayAttributeUpdateNotificationMsgBuilder.setNotificationMsg(expectedAttributeUpdateNotificationMsg);
  100 +
  101 + TransportApiProtos.GatewayAttributeUpdateNotificationMsg expectedGatewayAttributeUpdateNotificationMsg = gatewayAttributeUpdateNotificationMsgBuilder.build();
  102 + TransportApiProtos.GatewayAttributeUpdateNotificationMsg actualGatewayAttributeUpdateNotificationMsg = TransportApiProtos.GatewayAttributeUpdateNotificationMsg.parseFrom(callback.getPayloadBytes());
  103 +
  104 + assertEquals(expectedGatewayAttributeUpdateNotificationMsg.getDeviceName(), actualGatewayAttributeUpdateNotificationMsg.getDeviceName());
  105 +
  106 + List<TransportProtos.KeyValueProto> actualSharedUpdatedList = actualGatewayAttributeUpdateNotificationMsg.getNotificationMsg().getSharedUpdatedList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList());
  107 + List<TransportProtos.KeyValueProto> expectedSharedUpdatedList = expectedGatewayAttributeUpdateNotificationMsg.getNotificationMsg().getSharedUpdatedList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList());
  108 +
  109 + assertEquals(expectedSharedUpdatedList.size(), actualSharedUpdatedList.size());
  110 + assertTrue(actualSharedUpdatedList.containsAll(expectedSharedUpdatedList));
  111 +
  112 + }
  113 +
  114 + protected void validateGatewayDeleteAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException {
  115 + assertNotNull(callback.getPayloadBytes());
  116 + TransportProtos.AttributeUpdateNotificationMsg.Builder attributeUpdateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder();
  117 + attributeUpdateNotificationMsgBuilder.addSharedDeleted("attribute5");
  118 + TransportProtos.AttributeUpdateNotificationMsg attributeUpdateNotificationMsg = attributeUpdateNotificationMsgBuilder.build();
  119 +
  120 + TransportApiProtos.GatewayAttributeUpdateNotificationMsg.Builder gatewayAttributeUpdateNotificationMsgBuilder = TransportApiProtos.GatewayAttributeUpdateNotificationMsg.newBuilder();
  121 + gatewayAttributeUpdateNotificationMsgBuilder.setDeviceName("Gateway Device Subscribe to attribute updates");
  122 + gatewayAttributeUpdateNotificationMsgBuilder.setNotificationMsg(attributeUpdateNotificationMsg);
  123 +
  124 + TransportApiProtos.GatewayAttributeUpdateNotificationMsg expectedGatewayAttributeUpdateNotificationMsg = gatewayAttributeUpdateNotificationMsgBuilder.build();
  125 + TransportApiProtos.GatewayAttributeUpdateNotificationMsg actualGatewayAttributeUpdateNotificationMsg = TransportApiProtos.GatewayAttributeUpdateNotificationMsg.parseFrom(callback.getPayloadBytes());
  126 +
  127 + assertEquals(expectedGatewayAttributeUpdateNotificationMsg.getDeviceName(), actualGatewayAttributeUpdateNotificationMsg.getDeviceName());
  128 +
  129 + TransportProtos.AttributeUpdateNotificationMsg expectedAttributeUpdateNotificationMsg = expectedGatewayAttributeUpdateNotificationMsg.getNotificationMsg();
  130 + TransportProtos.AttributeUpdateNotificationMsg actualAttributeUpdateNotificationMsg = actualGatewayAttributeUpdateNotificationMsg.getNotificationMsg();
  131 +
  132 + assertEquals(expectedAttributeUpdateNotificationMsg.getSharedDeletedList().size(), actualAttributeUpdateNotificationMsg.getSharedDeletedList().size());
  133 + assertEquals("attribute5", actualAttributeUpdateNotificationMsg.getSharedDeletedList().get(0));
  134 +
  135 + }
  136 +
  137 + protected byte[] getConnectPayloadBytes() {
  138 + TransportApiProtos.ConnectMsg connectProto = getConnectProto();
  139 + return connectProto.toByteArray();
  140 + }
  141 +
  142 + private TransportApiProtos.ConnectMsg getConnectProto() {
  143 + TransportApiProtos.ConnectMsg.Builder builder = TransportApiProtos.ConnectMsg.newBuilder();
  144 + builder.setDeviceName("Gateway Device Subscribe to attribute updates");
  145 + builder.setDeviceType(TransportPayloadType.PROTOBUF.name());
  146 + return builder.build();
  147 + }
  148 +
  149 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.attributes.updates.nosql;
  17 +
  18 +import org.thingsboard.server.dao.service.DaoNoSqlTest;
  19 +import org.thingsboard.server.mqtt.attributes.updates.AbstractMqttAttributesUpdatesJsonIntegrationTest;
  20 +
  21 +
  22 +@DaoNoSqlTest
  23 +public class MqttAttributesUpdatesNoSqlIntegrationTest extends AbstractMqttAttributesUpdatesJsonIntegrationTest {
  24 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.attributes.updates.sql;
  17 +
  18 +import org.thingsboard.server.dao.service.DaoSqlTest;
  19 +import org.thingsboard.server.mqtt.attributes.updates.AbstractMqttAttributesUpdatesIntegrationTest;
  20 +
  21 +@DaoSqlTest
  22 +public class MqttAttributesUpdatesSqlIntegrationTest extends AbstractMqttAttributesUpdatesIntegrationTest {
  23 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.attributes.updates.sql;
  17 +
  18 +import org.thingsboard.server.dao.service.DaoSqlTest;
  19 +import org.thingsboard.server.mqtt.attributes.updates.AbstractMqttAttributesUpdatesIntegrationTest;
  20 +import org.thingsboard.server.mqtt.attributes.updates.AbstractMqttAttributesUpdatesJsonIntegrationTest;
  21 +
  22 +@DaoSqlTest
  23 +public class MqttAttributesUpdatesSqlJsonIntegrationTest extends AbstractMqttAttributesUpdatesJsonIntegrationTest {
  24 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.attributes.updates.sql;
  17 +
  18 +import org.thingsboard.server.dao.service.DaoSqlTest;
  19 +import org.thingsboard.server.mqtt.attributes.updates.AbstractMqttAttributesUpdatesJsonIntegrationTest;
  20 +import org.thingsboard.server.mqtt.attributes.updates.AbstractMqttAttributesUpdatesProtoIntegrationTest;
  21 +
  22 +@DaoSqlTest
  23 +public class MqttAttributesUpdatesSqlProtoIntegrationTest extends AbstractMqttAttributesUpdatesProtoIntegrationTest {
  24 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.claim;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
  20 +import org.eclipse.paho.client.mqttv3.MqttMessage;
  21 +import org.junit.After;
  22 +import org.junit.Before;
  23 +import org.junit.Test;
  24 +import org.thingsboard.server.common.data.ClaimRequest;
  25 +import org.thingsboard.server.common.data.Customer;
  26 +import org.thingsboard.server.common.data.Device;
  27 +import org.thingsboard.server.common.data.User;
  28 +import org.thingsboard.server.common.data.device.profile.MqttTopics;
  29 +import org.thingsboard.server.common.data.security.Authority;
  30 +import org.thingsboard.server.dao.device.claim.ClaimResponse;
  31 +import org.thingsboard.server.dao.device.claim.ClaimResult;
  32 +import org.thingsboard.server.mqtt.AbstractMqttIntegrationTest;
  33 +
  34 +import static org.junit.Assert.assertEquals;
  35 +import static org.junit.Assert.assertNotNull;
  36 +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
  37 +
  38 +@Slf4j
  39 +public abstract class AbstractMqttClaimDeviceTest extends AbstractMqttIntegrationTest {
  40 +
  41 + protected static final String CUSTOMER_USER_PASSWORD = "customerUser123!";
  42 +
  43 + protected User customerAdmin;
  44 + protected Customer savedCustomer;
  45 +
  46 + @Before
  47 + public void beforeTest() throws Exception {
  48 + super.processBeforeTest("Test Claim device", "Test Claim gateway", null, null, null);
  49 + createCustomerAndUser();
  50 + }
  51 +
  52 + protected void createCustomerAndUser() throws Exception {
  53 + Customer customer = new Customer();
  54 + customer.setTenantId(savedTenant.getId());
  55 + customer.setTitle("Test Claiming Customer");
  56 + savedCustomer = doPost("/api/customer", customer, Customer.class);
  57 + assertNotNull(savedCustomer);
  58 + assertEquals(savedTenant.getId(), savedCustomer.getTenantId());
  59 +
  60 + User user = new User();
  61 + user.setAuthority(Authority.CUSTOMER_USER);
  62 + user.setTenantId(savedTenant.getId());
  63 + user.setCustomerId(savedCustomer.getId());
  64 + user.setEmail("customer@thingsboard.org");
  65 +
  66 + customerAdmin = createUser(user, CUSTOMER_USER_PASSWORD);
  67 + assertNotNull(customerAdmin);
  68 + assertEquals(customerAdmin.getCustomerId(), savedCustomer.getId());
  69 + }
  70 +
  71 + @After
  72 + public void afterTest() throws Exception {
  73 + super.processAfterTest();
  74 + }
  75 +
  76 + @Test
  77 + public void testClaimingDevice() throws Exception {
  78 + processTestClaimingDevice(false);
  79 + }
  80 +
  81 + @Test
  82 + public void testClaimingDeviceWithoutSecretAndDuration() throws Exception {
  83 + processTestClaimingDevice(true);
  84 + }
  85 +
  86 + @Test
  87 + public void testGatewayClaimingDevice() throws Exception {
  88 + processTestGatewayClaimingDevice("Test claiming gateway device", false);
  89 + }
  90 +
  91 + @Test
  92 + public void testGatewayClaimingDeviceWithoutSecretAndDuration() throws Exception {
  93 + processTestGatewayClaimingDevice("Test claiming gateway device empty payload", true);
  94 + }
  95 +
  96 +
  97 + protected void processTestClaimingDevice(boolean emptyPayload) throws Exception {
  98 + MqttAsyncClient client = getMqttAsyncClient(accessToken);
  99 + byte[] payloadBytes;
  100 + byte[] failurePayloadBytes;
  101 + if (emptyPayload) {
  102 + payloadBytes = "{}".getBytes();
  103 + failurePayloadBytes = "{\"durationMs\":1}".getBytes();
  104 + } else {
  105 + payloadBytes = "{\"secretKey\":\"value\", \"durationMs\":60000}".getBytes();
  106 + failurePayloadBytes = "{\"secretKey\":\"value\", \"durationMs\":1}".getBytes();
  107 + }
  108 + validateClaimResponse(emptyPayload, client, payloadBytes, failurePayloadBytes);
  109 + }
  110 +
  111 + protected void validateClaimResponse(boolean emptyPayload, MqttAsyncClient client, byte[] payloadBytes, byte[] failurePayloadBytes) throws Exception {
  112 + client.publish(MqttTopics.DEVICE_CLAIM_TOPIC, new MqttMessage(failurePayloadBytes));
  113 +
  114 + Thread.sleep(2000);
  115 +
  116 + loginUser(customerAdmin.getName(), CUSTOMER_USER_PASSWORD);
  117 + ClaimRequest claimRequest;
  118 + if (!emptyPayload) {
  119 + claimRequest = new ClaimRequest("value");
  120 + } else {
  121 + claimRequest = new ClaimRequest(null);
  122 + }
  123 +
  124 + ClaimResponse claimResponse = doPostClaimAsync("/api/customer/device/" + savedDevice.getName() + "/claim", claimRequest, ClaimResponse.class, status().isBadRequest());
  125 + assertEquals(claimResponse, ClaimResponse.FAILURE);
  126 +
  127 + client.publish(MqttTopics.DEVICE_CLAIM_TOPIC, new MqttMessage(payloadBytes));
  128 +
  129 + Thread.sleep(2000);
  130 +
  131 + ClaimResult claimResult = doPostClaimAsync("/api/customer/device/" + savedDevice.getName() + "/claim", claimRequest, ClaimResult.class, status().isOk());
  132 + assertEquals(claimResult.getResponse(), ClaimResponse.SUCCESS);
  133 + Device claimedDevice = claimResult.getDevice();
  134 + assertNotNull(claimedDevice);
  135 + assertNotNull(claimedDevice.getCustomerId());
  136 + assertEquals(customerAdmin.getCustomerId(), claimedDevice.getCustomerId());
  137 +
  138 + claimResponse = doPostClaimAsync("/api/customer/device/" + savedDevice.getName() + "/claim", claimRequest, ClaimResponse.class, status().isBadRequest());
  139 + assertEquals(claimResponse, ClaimResponse.CLAIMED);
  140 + }
  141 +
  142 + protected void validateGatewayClaimResponse(String deviceName, boolean emptyPayload, MqttAsyncClient client, byte[] failurePayloadBytes, byte[] payloadBytes) throws Exception {
  143 + client.publish(MqttTopics.GATEWAY_CLAIM_TOPIC, new MqttMessage(failurePayloadBytes));
  144 +
  145 + Thread.sleep(2000);
  146 +
  147 + Device savedDevice = doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class);
  148 + assertNotNull(savedDevice);
  149 +
  150 + loginUser(customerAdmin.getName(), CUSTOMER_USER_PASSWORD);
  151 + ClaimRequest claimRequest;
  152 + if (!emptyPayload) {
  153 + claimRequest = new ClaimRequest("value");
  154 + } else {
  155 + claimRequest = new ClaimRequest(null);
  156 + }
  157 +
  158 + ClaimResponse claimResponse = doPostClaimAsync("/api/customer/device/" + deviceName + "/claim", claimRequest, ClaimResponse.class, status().isBadRequest());
  159 + assertEquals(claimResponse, ClaimResponse.FAILURE);
  160 +
  161 + client.publish(MqttTopics.GATEWAY_CLAIM_TOPIC, new MqttMessage(payloadBytes));
  162 +
  163 + Thread.sleep(2000);
  164 +
  165 + ClaimResult claimResult = doPostClaimAsync("/api/customer/device/" + deviceName + "/claim", claimRequest, ClaimResult.class, status().isOk());
  166 + assertEquals(claimResult.getResponse(), ClaimResponse.SUCCESS);
  167 + Device claimedDevice = claimResult.getDevice();
  168 + assertNotNull(claimedDevice);
  169 + assertNotNull(claimedDevice.getCustomerId());
  170 + assertEquals(customerAdmin.getCustomerId(), claimedDevice.getCustomerId());
  171 +
  172 + claimResponse = doPostClaimAsync("/api/customer/device/" + deviceName + "/claim", claimRequest, ClaimResponse.class, status().isBadRequest());
  173 + assertEquals(claimResponse, ClaimResponse.CLAIMED);
  174 + }
  175 +
  176 + protected void processTestGatewayClaimingDevice(String deviceName, boolean emptyPayload) throws Exception {
  177 + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
  178 + byte[] failurePayloadBytes;
  179 + byte[] payloadBytes;
  180 + String failurePayload;
  181 + String payload;
  182 + if (emptyPayload) {
  183 + failurePayload = "{\"" + deviceName + "\": " + "{\"durationMs\":1}" + "}";
  184 + payload = "{\"" + deviceName + "\": " + "{}" + "}";
  185 + } else {
  186 + failurePayload = "{\"" + deviceName + "\": " + "{\"secretKey\":\"value\", \"durationMs\":1}" + "}";
  187 + payload = "{\"" + deviceName + "\": " + "{\"secretKey\":\"value\", \"durationMs\":60000}" + "}";
  188 + }
  189 + payloadBytes = payload.getBytes();
  190 + failurePayloadBytes = failurePayload.getBytes();
  191 + validateGatewayClaimResponse(deviceName, emptyPayload, client, failurePayloadBytes, payloadBytes);
  192 + }
  193 +
  194 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.claim;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.junit.After;
  20 +import org.junit.Before;
  21 +import org.junit.Test;
  22 +import org.thingsboard.server.common.data.TransportPayloadType;
  23 +
  24 +@Slf4j
  25 +public abstract class AbstractMqttClaimJsonDeviceTest extends AbstractMqttClaimDeviceTest {
  26 +
  27 + @Before
  28 + public void beforeTest() throws Exception {
  29 + super.processBeforeTest("Test Claim device", "Test Claim gateway", TransportPayloadType.JSON, null, null);
  30 + createCustomerAndUser();
  31 + }
  32 +
  33 + @After
  34 + public void afterTest() throws Exception {
  35 + super.afterTest();
  36 + }
  37 +
  38 + @Test
  39 + public void testClaimingDevice() throws Exception {
  40 + processTestClaimingDevice(false);
  41 + }
  42 +
  43 + @Test
  44 + public void testClaimingDeviceWithoutSecretAndDuration() throws Exception {
  45 + processTestClaimingDevice(true);
  46 + }
  47 +
  48 + @Test
  49 + public void testGatewayClaimingDevice() throws Exception {
  50 + processTestGatewayClaimingDevice("Test claiming gateway device Json", false);
  51 + }
  52 +
  53 + @Test
  54 + public void testGatewayClaimingDeviceWithoutSecretAndDuration() throws Exception {
  55 + processTestGatewayClaimingDevice("Test claiming gateway device empty payload Json", true);
  56 + }
  57 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.claim;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
  20 +import org.junit.After;
  21 +import org.junit.Before;
  22 +import org.junit.Test;
  23 +import org.thingsboard.server.common.data.TransportPayloadType;
  24 +import org.thingsboard.server.gen.transport.TransportApiProtos;
  25 +
  26 +@Slf4j
  27 +public abstract class AbstractMqttClaimProtoDeviceTest extends AbstractMqttClaimDeviceTest {
  28 +
  29 + @Before
  30 + public void beforeTest() throws Exception {
  31 + processBeforeTest("Test Claim device", "Test Claim gateway", TransportPayloadType.PROTOBUF, null, null);
  32 + createCustomerAndUser();
  33 + }
  34 +
  35 + @After
  36 + public void afterTest() throws Exception { super.afterTest(); }
  37 +
  38 + @Test
  39 + public void testClaimingDevice() throws Exception {
  40 + processTestClaimingDevice(false);
  41 + }
  42 +
  43 + @Test
  44 + public void testClaimingDeviceWithoutSecretAndDuration() throws Exception {
  45 + processTestClaimingDevice(true);
  46 + }
  47 +
  48 + @Test
  49 + public void testGatewayClaimingDevice() throws Exception {
  50 + processTestGatewayClaimingDevice("Test claiming gateway device Proto", false);
  51 + }
  52 +
  53 + @Test
  54 + public void testGatewayClaimingDeviceWithoutSecretAndDuration() throws Exception {
  55 + processTestGatewayClaimingDevice("Test claiming gateway device empty payload Proto", true);
  56 + }
  57 +
  58 + protected void processTestClaimingDevice(boolean emptyPayload) throws Exception {
  59 + MqttAsyncClient client = getMqttAsyncClient(accessToken);
  60 + byte[] payloadBytes;
  61 + if (emptyPayload) {
  62 + payloadBytes = getClaimDevice(0, emptyPayload).toByteArray();
  63 + } else {
  64 + payloadBytes = getClaimDevice(60000, emptyPayload).toByteArray();
  65 + }
  66 + byte[] failurePayloadBytes = getClaimDevice(1, emptyPayload).toByteArray();
  67 + validateClaimResponse(emptyPayload, client, payloadBytes, failurePayloadBytes);
  68 + }
  69 +
  70 + protected void processTestGatewayClaimingDevice(String deviceName, boolean emptyPayload) throws Exception {
  71 + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
  72 + byte[] failurePayloadBytes;
  73 + byte[] payloadBytes;
  74 + if (emptyPayload) {
  75 + payloadBytes = getGatewayClaimMsg(deviceName, 0, emptyPayload).toByteArray();
  76 + } else {
  77 + payloadBytes = getGatewayClaimMsg(deviceName, 60000, emptyPayload).toByteArray();
  78 + }
  79 + failurePayloadBytes = getGatewayClaimMsg(deviceName, 1, emptyPayload).toByteArray();
  80 +
  81 + validateGatewayClaimResponse(deviceName, emptyPayload, client, failurePayloadBytes, payloadBytes);
  82 + }
  83 +
  84 + private TransportApiProtos.GatewayClaimMsg getGatewayClaimMsg(String deviceName, long duration, boolean emptyPayload) {
  85 + TransportApiProtos.GatewayClaimMsg.Builder gatewayClaimMsgBuilder = TransportApiProtos.GatewayClaimMsg.newBuilder();
  86 + TransportApiProtos.ClaimDeviceMsg.Builder claimDeviceMsgBuilder = TransportApiProtos.ClaimDeviceMsg.newBuilder();
  87 + TransportApiProtos.ClaimDevice.Builder claimDeviceBuilder = TransportApiProtos.ClaimDevice.newBuilder();
  88 + if (!emptyPayload) {
  89 + claimDeviceBuilder.setSecretKey("value");
  90 + }
  91 + if (duration > 0) {
  92 + claimDeviceBuilder.setDurationMs(duration);
  93 + }
  94 + TransportApiProtos.ClaimDevice claimDevice = claimDeviceBuilder.build();
  95 + claimDeviceMsgBuilder.setClaimRequest(claimDevice);
  96 + claimDeviceMsgBuilder.setDeviceName(deviceName);
  97 + TransportApiProtos.ClaimDeviceMsg claimDeviceMsg = claimDeviceMsgBuilder.build();
  98 + gatewayClaimMsgBuilder.addMsg(claimDeviceMsg);
  99 + return gatewayClaimMsgBuilder.build();
  100 + }
  101 +
  102 + private TransportApiProtos.ClaimDevice getClaimDevice(long duration, boolean emptyPayload) {
  103 + TransportApiProtos.ClaimDevice.Builder claimDeviceBuilder = TransportApiProtos.ClaimDevice.newBuilder();
  104 + if (!emptyPayload) {
  105 + claimDeviceBuilder.setSecretKey("value");
  106 + }
  107 + if (duration > 0) {
  108 + claimDeviceBuilder.setSecretKey("value");
  109 + claimDeviceBuilder.setDurationMs(duration);
  110 + }
  111 + return claimDeviceBuilder.build();
  112 + }
  113 +
  114 +
  115 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.claim.nosql;
  17 +
  18 +import org.thingsboard.server.dao.service.DaoNoSqlTest;
  19 +import org.thingsboard.server.mqtt.claim.AbstractMqttClaimDeviceTest;
  20 +
  21 +
  22 +@DaoNoSqlTest
  23 +public class MqttClaimDeviceNoSqlTest extends AbstractMqttClaimDeviceTest {
  24 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.claim.sql;
  17 +
  18 +import org.thingsboard.server.dao.service.DaoSqlTest;
  19 +import org.thingsboard.server.mqtt.claim.AbstractMqttClaimDeviceTest;
  20 +import org.thingsboard.server.mqtt.claim.AbstractMqttClaimJsonDeviceTest;
  21 +
  22 +@DaoSqlTest
  23 +public class MqttClaimDeviceJsonSqlTest extends AbstractMqttClaimJsonDeviceTest {
  24 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.claim.sql;
  17 +
  18 +import org.thingsboard.server.dao.service.DaoSqlTest;
  19 +import org.thingsboard.server.mqtt.claim.AbstractMqttClaimJsonDeviceTest;
  20 +import org.thingsboard.server.mqtt.claim.AbstractMqttClaimProtoDeviceTest;
  21 +
  22 +@DaoSqlTest
  23 +public class MqttClaimDeviceProtoSqlTest extends AbstractMqttClaimProtoDeviceTest {
  24 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.claim.sql;
  17 +
  18 +import org.thingsboard.server.dao.service.DaoSqlTest;
  19 +import org.thingsboard.server.mqtt.claim.AbstractMqttClaimDeviceTest;
  20 +
  21 +@DaoSqlTest
  22 +public class MqttClaimDeviceSqlTest extends AbstractMqttClaimDeviceTest {
  23 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.rpc;
  17 +
  18 +import com.fasterxml.jackson.databind.JsonNode;
  19 +import com.fasterxml.jackson.databind.node.ObjectNode;
  20 +import com.google.protobuf.InvalidProtocolBufferException;
  21 +import com.nimbusds.jose.util.StandardCharset;
  22 +import com.datastax.oss.driver.api.core.uuid.Uuids;
  23 +import io.netty.handler.codec.mqtt.MqttQoS;
  24 +import lombok.extern.slf4j.Slf4j;
  25 +import org.apache.commons.lang3.StringUtils;
  26 +import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
  27 +import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
  28 +import org.eclipse.paho.client.mqttv3.MqttCallback;
  29 +import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
  30 +import org.eclipse.paho.client.mqttv3.MqttException;
  31 +import org.eclipse.paho.client.mqttv3.MqttMessage;
  32 +import org.junit.After;
  33 +import org.junit.Assert;
  34 +import org.junit.Before;
  35 +import org.junit.Ignore;
  36 +import org.junit.Test;
  37 +import org.thingsboard.server.common.data.Device;
  38 +import org.thingsboard.server.common.data.DeviceProfile;
  39 +import org.thingsboard.server.common.data.DeviceProfileType;
  40 +import org.thingsboard.server.common.data.DeviceTransportType;
  41 +import org.thingsboard.server.common.data.Tenant;
  42 +import org.thingsboard.server.common.data.TransportPayloadType;
  43 +import org.thingsboard.server.common.data.User;
  44 +import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration;
  45 +import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
  46 +import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration;
  47 +import org.thingsboard.server.common.data.device.profile.MqttTopics;
  48 +import org.thingsboard.server.common.data.security.Authority;
  49 +import org.thingsboard.server.common.data.security.DeviceCredentials;
  50 +import org.thingsboard.server.controller.AbstractControllerTest;
  51 +import org.thingsboard.server.dao.util.mapping.JacksonUtil;
  52 +import org.thingsboard.server.service.security.AccessValidator;
  53 +
  54 +import java.util.Arrays;
  55 +import java.util.concurrent.CountDownLatch;
  56 +import java.util.concurrent.TimeUnit;
  57 +import java.util.concurrent.atomic.AtomicInteger;
  58 +
  59 +import static org.junit.Assert.assertEquals;
  60 +import static org.junit.Assert.assertNotNull;
  61 +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
  62 +
  63 +/**
  64 + * @author Valerii Sosliuk
  65 + */
  66 +@Slf4j
  67 +public abstract class AbstractMqttServerSideRpcDefaultIntegrationTest extends AbstractMqttServerSideRpcIntegrationTest {
  68 +
  69 + @Before
  70 + public void beforeTest() throws Exception {
  71 + processBeforeTest("RPC test device", "RPC test gateway", null, null, null);
  72 + }
  73 +
  74 + @After
  75 + public void afterTest() throws Exception {
  76 + super.processAfterTest();
  77 + }
  78 +
  79 + @Test
  80 + public void testServerMqttOneWayRpcDeviceOffline() throws Exception {
  81 + String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"24\",\"value\": 1},\"timeout\": 6000}";
  82 + String deviceId = savedDevice.getId().getId().toString();
  83 +
  84 + doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().is(409),
  85 + asyncContextTimeoutToUseRpcPlugin);
  86 + }
  87 +
  88 + @Test
  89 + public void testServerMqttOneWayRpcDeviceDoesNotExist() throws Exception {
  90 + String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"25\",\"value\": 1}}";
  91 + String nonExistentDeviceId = Uuids.timeBased().toString();
  92 +
  93 + String result = doPostAsync("/api/plugins/rpc/oneway/" + nonExistentDeviceId, setGpioRequest, String.class,
  94 + status().isNotFound());
  95 + Assert.assertEquals(AccessValidator.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result);
  96 + }
  97 +
  98 + @Test
  99 + public void testServerMqttTwoWayRpcDeviceOffline() throws Exception {
  100 + String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"27\",\"value\": 1},\"timeout\": 6000}";
  101 + String deviceId = savedDevice.getId().getId().toString();
  102 +
  103 + doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().is(409),
  104 + asyncContextTimeoutToUseRpcPlugin);
  105 + }
  106 +
  107 + @Test
  108 + public void testServerMqttTwoWayRpcDeviceDoesNotExist() throws Exception {
  109 + String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"28\",\"value\": 1}}";
  110 + String nonExistentDeviceId = Uuids.timeBased().toString();
  111 +
  112 + String result = doPostAsync("/api/plugins/rpc/twoway/" + nonExistentDeviceId, setGpioRequest, String.class,
  113 + status().isNotFound());
  114 + Assert.assertEquals(AccessValidator.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result);
  115 + }
  116 +
  117 + @Test
  118 + public void testServerMqttOneWayRpc() throws Exception {
  119 + processOneWayRpcTest();
  120 + }
  121 +
  122 + @Test
  123 + public void testServerMqttTwoWayRpc() throws Exception {
  124 + processTwoWayRpcTest();
  125 + }
  126 +
  127 + @Test
  128 + public void testGatewayServerMqttOneWayRpc() throws Exception {
  129 + processOneWayRpcTestGateway("Gateway Device OneWay RPC");
  130 + }
  131 +
  132 + @Test
  133 + public void testGatewayServerMqttTwoWayRpc() throws Exception {
  134 + processTwoWayRpcTestGateway("Gateway Device TwoWay RPC");
  135 + }
  136 +
  137 +}
@@ -16,6 +16,10 @@ @@ -16,6 +16,10 @@
16 package org.thingsboard.server.mqtt.rpc; 16 package org.thingsboard.server.mqtt.rpc;
17 17
18 import com.datastax.oss.driver.api.core.uuid.Uuids; 18 import com.datastax.oss.driver.api.core.uuid.Uuids;
  19 +import com.fasterxml.jackson.databind.JsonNode;
  20 +import com.fasterxml.jackson.databind.node.ObjectNode;
  21 +import com.google.protobuf.InvalidProtocolBufferException;
  22 +import com.nimbusds.jose.util.StandardCharset;
19 import io.netty.handler.codec.mqtt.MqttQoS; 23 import io.netty.handler.codec.mqtt.MqttQoS;
20 import lombok.extern.slf4j.Slf4j; 24 import lombok.extern.slf4j.Slf4j;
21 import org.apache.commons.lang3.StringUtils; 25 import org.apache.commons.lang3.StringUtils;
@@ -23,15 +27,28 @@ import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; @@ -23,15 +27,28 @@ import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
23 import org.eclipse.paho.client.mqttv3.MqttAsyncClient; 27 import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
24 import org.eclipse.paho.client.mqttv3.MqttCallback; 28 import org.eclipse.paho.client.mqttv3.MqttCallback;
25 import org.eclipse.paho.client.mqttv3.MqttConnectOptions; 29 import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
  30 +import org.eclipse.paho.client.mqttv3.MqttException;
26 import org.eclipse.paho.client.mqttv3.MqttMessage; 31 import org.eclipse.paho.client.mqttv3.MqttMessage;
27 -import org.junit.*; 32 +import org.junit.After;
  33 +import org.junit.Assert;
  34 +import org.junit.Before;
  35 +import org.junit.Test;
28 import org.thingsboard.server.common.data.Device; 36 import org.thingsboard.server.common.data.Device;
  37 +import org.thingsboard.server.common.data.DeviceProfile;
  38 +import org.thingsboard.server.common.data.DeviceProfileType;
  39 +import org.thingsboard.server.common.data.DeviceTransportType;
29 import org.thingsboard.server.common.data.Tenant; 40 import org.thingsboard.server.common.data.Tenant;
  41 +import org.thingsboard.server.common.data.TransportPayloadType;
30 import org.thingsboard.server.common.data.User; 42 import org.thingsboard.server.common.data.User;
  43 +import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration;
  44 +import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
  45 +import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration;
  46 +import org.thingsboard.server.common.data.device.profile.MqttTopics;
31 import org.thingsboard.server.common.data.security.Authority; 47 import org.thingsboard.server.common.data.security.Authority;
32 import org.thingsboard.server.common.data.security.DeviceCredentials; 48 import org.thingsboard.server.common.data.security.DeviceCredentials;
33 import org.thingsboard.server.controller.AbstractControllerTest; 49 import org.thingsboard.server.controller.AbstractControllerTest;
34 -import org.thingsboard.server.mqtt.telemetry.AbstractMqttTelemetryIntegrationTest; 50 +import org.thingsboard.server.dao.util.mapping.JacksonUtil;
  51 +import org.thingsboard.server.mqtt.AbstractMqttIntegrationTest;
35 import org.thingsboard.server.service.security.AccessValidator; 52 import org.thingsboard.server.service.security.AccessValidator;
36 53
37 import java.util.Arrays; 54 import java.util.Arrays;
@@ -47,74 +64,88 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @@ -47,74 +64,88 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
47 * @author Valerii Sosliuk 64 * @author Valerii Sosliuk
48 */ 65 */
49 @Slf4j 66 @Slf4j
50 -public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractControllerTest { 67 +public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractMqttIntegrationTest {
51 68
52 - private static final String MQTT_URL = "tcp://localhost:1883";  
53 - private static final Long TIME_TO_HANDLE_REQUEST = 500L; 69 + protected static final String DEVICE_RESPONSE = "{\"value1\":\"A\",\"value2\":\"B\"}";
54 70
55 - private Tenant savedTenant;  
56 - private User tenantAdmin;  
57 - private Long asyncContextTimeoutToUseRpcPlugin; 71 + protected Long asyncContextTimeoutToUseRpcPlugin;
58 72
59 - private static final AtomicInteger atomicInteger = new AtomicInteger(2); 73 + protected void processBeforeTest(String deviceName, String gatewayName, TransportPayloadType payloadType, String telemetryTopic, String attributesTopic) throws Exception {
  74 + super.processBeforeTest(deviceName, gatewayName, payloadType, telemetryTopic, attributesTopic);
  75 + asyncContextTimeoutToUseRpcPlugin = 10000L;
  76 + }
60 77
  78 + protected void processOneWayRpcTest() throws Exception {
  79 + MqttAsyncClient client = getMqttAsyncClient(accessToken);
61 80
62 - @Before  
63 - public void beforeTest() throws Exception {  
64 - loginSysAdmin(); 81 + CountDownLatch latch = new CountDownLatch(1);
  82 + TestMqttCallback callback = new TestMqttCallback(client, latch);
  83 + client.setCallback(callback);
65 84
66 - asyncContextTimeoutToUseRpcPlugin = 10000L; 85 + client.subscribe(MqttTopics.DEVICE_RPC_REQUESTS_SUB_TOPIC, MqttQoS.AT_MOST_ONCE.value());
67 86
68 - Tenant tenant = new Tenant();  
69 - tenant.setTitle("My tenant");  
70 - savedTenant = doPost("/api/tenant", tenant, Tenant.class);  
71 - Assert.assertNotNull(savedTenant); 87 + Thread.sleep(2000);
72 88
73 - tenantAdmin = new User();  
74 - tenantAdmin.setAuthority(Authority.TENANT_ADMIN);  
75 - tenantAdmin.setTenantId(savedTenant.getId());  
76 - tenantAdmin.setEmail("tenant" + atomicInteger.getAndIncrement() + "@thingsboard.org");  
77 - tenantAdmin.setFirstName("Joe");  
78 - tenantAdmin.setLastName("Downs"); 89 + String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}";
  90 + String deviceId = savedDevice.getId().getId().toString();
  91 + String result = doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isOk());
  92 + Assert.assertTrue(StringUtils.isEmpty(result));
  93 + latch.await(3, TimeUnit.SECONDS);
  94 + assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS());
  95 + }
79 96
80 - createUserAndLogin(tenantAdmin, "testPassword1"); 97 + protected void processOneWayRpcTestGateway(String deviceName) throws Exception {
  98 + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
  99 + String payload = "{\"device\":\"" + deviceName + "\"}";
  100 + byte[] payloadBytes = payload.getBytes();
  101 + validateOneWayRpcGatewayResponse(deviceName, client, payloadBytes);
81 } 102 }
82 103
83 - @After  
84 - public void afterTest() throws Exception {  
85 - loginSysAdmin();  
86 - if (savedTenant != null) {  
87 - doDelete("/api/tenant/" + savedTenant.getId().getId().toString()).andExpect(status().isOk());  
88 - } 104 + protected void processTwoWayRpcTest() throws Exception {
  105 + MqttAsyncClient client = getMqttAsyncClient(accessToken);
  106 + client.subscribe(MqttTopics.DEVICE_RPC_REQUESTS_SUB_TOPIC, 1);
  107 +
  108 + CountDownLatch latch = new CountDownLatch(1);
  109 + TestMqttCallback callback = new TestMqttCallback(client, latch);
  110 + client.setCallback(callback);
  111 +
  112 + Thread.sleep(2000);
  113 +
  114 + String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"26\",\"value\": 1}}";
  115 + String deviceId = savedDevice.getId().getId().toString();
  116 +
  117 + String result = doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
  118 + String expected = "{\"value1\":\"A\",\"value2\":\"B\"}";
  119 + latch.await(3, TimeUnit.SECONDS);
  120 + Assert.assertEquals(expected, result);
89 } 121 }
90 122
91 - @Test  
92 - public void testServerMqttOneWayRpc() throws Exception {  
93 - Device device = new Device();  
94 - device.setName("Test One-Way Server-Side RPC");  
95 - device.setType("default");  
96 - Device savedDevice = getSavedDevice(device);  
97 - DeviceCredentials deviceCredentials = getDeviceCredentials(savedDevice);  
98 - assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId());  
99 - String accessToken = deviceCredentials.getCredentialsId();  
100 - assertNotNull(accessToken); 123 + protected void processTwoWayRpcTestGateway(String deviceName) throws Exception {
  124 + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
  125 +
  126 + String payload = "{\"device\":\"" + deviceName + "\"}";
  127 + byte[] payloadBytes = payload.getBytes();
101 128
102 - String clientId = MqttAsyncClient.generateClientId();  
103 - MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId); 129 + validateTwoWayRpcGateway(deviceName, client, payloadBytes);
  130 + }
104 131
105 - MqttConnectOptions options = new MqttConnectOptions();  
106 - options.setUserName(accessToken);  
107 - client.connect(options).waitForCompletion(); 132 + protected void validateOneWayRpcGatewayResponse(String deviceName, MqttAsyncClient client, byte[] payloadBytes) throws Exception {
  133 + publishMqttMsg(client, payloadBytes, MqttTopics.GATEWAY_CONNECT_TOPIC);
  134 +
  135 + Thread.sleep(2000);
  136 +
  137 + Device savedDevice = getDeviceByName(deviceName);
  138 + assertNotNull(savedDevice);
108 139
109 CountDownLatch latch = new CountDownLatch(1); 140 CountDownLatch latch = new CountDownLatch(1);
110 TestMqttCallback callback = new TestMqttCallback(client, latch); 141 TestMqttCallback callback = new TestMqttCallback(client, latch);
111 client.setCallback(callback); 142 client.setCallback(callback);
112 143
113 - client.subscribe("v1/devices/me/rpc/request/+", MqttQoS.AT_MOST_ONCE.value()); 144 + client.subscribe(MqttTopics.GATEWAY_RPC_TOPIC, MqttQoS.AT_MOST_ONCE.value());
114 145
115 Thread.sleep(2000); 146 Thread.sleep(2000);
116 147
117 - String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}"; 148 + String setGpioRequest = "{\"method\": \"toggle_gpio\", \"params\": {\"pin\":1}}";
118 String deviceId = savedDevice.getId().getId().toString(); 149 String deviceId = savedDevice.getId().getId().toString();
119 String result = doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isOk()); 150 String result = doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isOk());
120 Assert.assertTrue(StringUtils.isEmpty(result)); 151 Assert.assertTrue(StringUtils.isEmpty(result));
@@ -122,100 +153,49 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC @@ -122,100 +153,49 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC
122 assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS()); 153 assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS());
123 } 154 }
124 155
125 - @Test  
126 - public void testServerMqttOneWayRpcDeviceOffline() throws Exception {  
127 - Device device = new Device();  
128 - device.setName("Test One-Way Server-Side RPC Device Offline");  
129 - device.setType("default");  
130 - Device savedDevice = getSavedDevice(device);  
131 - DeviceCredentials deviceCredentials = getDeviceCredentials(savedDevice);  
132 - assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId());  
133 - String accessToken = deviceCredentials.getCredentialsId();  
134 - assertNotNull(accessToken);  
135 -  
136 - String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"24\",\"value\": 1},\"timeout\": 6000}";  
137 - String deviceId = savedDevice.getId().getId().toString(); 156 + protected void validateTwoWayRpcGateway(String deviceName, MqttAsyncClient client, byte[] payloadBytes) throws Exception {
  157 + publishMqttMsg(client, payloadBytes, MqttTopics.GATEWAY_CONNECT_TOPIC);
138 158
139 - doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().is(409),  
140 - asyncContextTimeoutToUseRpcPlugin);  
141 - } 159 + Thread.sleep(2000);
142 160
143 - @Test  
144 - public void testServerMqttOneWayRpcDeviceDoesNotExist() throws Exception {  
145 - String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"25\",\"value\": 1}}";  
146 - String nonExistentDeviceId = Uuids.timeBased().toString(); 161 + Device savedDevice = getDeviceByName(deviceName);
  162 + assertNotNull(savedDevice);
147 163
148 - String result = doPostAsync("/api/plugins/rpc/oneway/" + nonExistentDeviceId, setGpioRequest, String.class,  
149 - status().isNotFound());  
150 - Assert.assertEquals(AccessValidator.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result);  
151 - } 164 + CountDownLatch latch = new CountDownLatch(1);
  165 + TestMqttCallback callback = new TestMqttCallback(client, latch);
  166 + client.setCallback(callback);
152 167
153 - @Test  
154 - public void testServerMqttTwoWayRpc() throws Exception {  
155 - Device device = new Device();  
156 - device.setName("Test Two-Way Server-Side RPC");  
157 - device.setType("default");  
158 - Device savedDevice = getSavedDevice(device);  
159 - DeviceCredentials deviceCredentials = getDeviceCredentials(savedDevice);  
160 - assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId());  
161 - String accessToken = deviceCredentials.getCredentialsId();  
162 - assertNotNull(accessToken);  
163 -  
164 - String clientId = MqttAsyncClient.generateClientId();  
165 - MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId);  
166 -  
167 - MqttConnectOptions options = new MqttConnectOptions();  
168 - options.setUserName(accessToken);  
169 - client.connect(options).waitForCompletion();  
170 - client.subscribe("v1/devices/me/rpc/request/+", 1);  
171 - client.setCallback(new TestMqttCallback(client, new CountDownLatch(1))); 168 + client.subscribe(MqttTopics.GATEWAY_RPC_TOPIC, MqttQoS.AT_MOST_ONCE.value());
172 169
173 Thread.sleep(2000); 170 Thread.sleep(2000);
174 171
175 - String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"26\",\"value\": 1}}"; 172 + String setGpioRequest = "{\"method\": \"toggle_gpio\", \"params\": {\"pin\":1}}";
176 String deviceId = savedDevice.getId().getId().toString(); 173 String deviceId = savedDevice.getId().getId().toString();
177 -  
178 String result = doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk()); 174 String result = doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
179 - Assert.assertEquals("{\"value1\":\"A\",\"value2\":\"B\"}", result);  
180 - }  
181 -  
182 - @Test  
183 - public void testServerMqttTwoWayRpcDeviceOffline() throws Exception {  
184 - Device device = new Device();  
185 - device.setName("Test Two-Way Server-Side RPC Device Offline");  
186 - device.setType("default");  
187 - Device savedDevice = getSavedDevice(device);  
188 - DeviceCredentials deviceCredentials = getDeviceCredentials(savedDevice);  
189 - assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId());  
190 - String accessToken = deviceCredentials.getCredentialsId();  
191 - assertNotNull(accessToken);  
192 -  
193 - String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"27\",\"value\": 1},\"timeout\": 6000}";  
194 - String deviceId = savedDevice.getId().getId().toString();  
195 -  
196 - doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().is(409),  
197 - asyncContextTimeoutToUseRpcPlugin);  
198 - }  
199 -  
200 - @Test  
201 - public void testServerMqttTwoWayRpcDeviceDoesNotExist() throws Exception {  
202 - String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"28\",\"value\": 1}}";  
203 - String nonExistentDeviceId = Uuids.timeBased().toString();  
204 -  
205 - String result = doPostAsync("/api/plugins/rpc/twoway/" + nonExistentDeviceId, setGpioRequest, String.class,  
206 - status().isNotFound());  
207 - Assert.assertEquals(AccessValidator.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result); 175 + latch.await(3, TimeUnit.SECONDS);
  176 + String expected = "{\"success\":true}";
  177 + assertEquals(expected, result);
  178 + assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS());
208 } 179 }
209 180
210 - private Device getSavedDevice(Device device) throws Exception {  
211 - return doPost("/api/device", device, Device.class); 181 + private Device getDeviceByName(String deviceName) throws Exception {
  182 + return doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class);
212 } 183 }
213 184
214 - private DeviceCredentials getDeviceCredentials(Device savedDevice) throws Exception {  
215 - return doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class); 185 + protected MqttMessage processMessageArrived(String requestTopic, MqttMessage mqttMessage) throws MqttException, InvalidProtocolBufferException {
  186 + MqttMessage message = new MqttMessage();
  187 + if (requestTopic.startsWith(MqttTopics.BASE_DEVICE_API_TOPIC)) {
  188 + message.setPayload(DEVICE_RESPONSE.getBytes(StandardCharset.UTF_8));
  189 + } else {
  190 + JsonNode requestMsgNode = JacksonUtil.toJsonNode(new String(mqttMessage.getPayload(), StandardCharset.UTF_8));
  191 + String deviceName = requestMsgNode.get("device").asText();
  192 + int requestId = requestMsgNode.get("data").get("id").asInt();
  193 + message.setPayload(("{\"device\": \"" + deviceName + "\", \"id\": " + requestId + ", \"data\": {\"success\": true}}").getBytes(StandardCharset.UTF_8));
  194 + }
  195 + return message;
216 } 196 }
217 197
218 - private static class TestMqttCallback implements MqttCallback { 198 + private class TestMqttCallback implements MqttCallback {
219 199
220 private final MqttAsyncClient client; 200 private final MqttAsyncClient client;
221 private final CountDownLatch latch; 201 private final CountDownLatch latch;
@@ -237,11 +217,9 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC @@ -237,11 +217,9 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC
237 @Override 217 @Override
238 public void messageArrived(String requestTopic, MqttMessage mqttMessage) throws Exception { 218 public void messageArrived(String requestTopic, MqttMessage mqttMessage) throws Exception {
239 log.info("Message Arrived: " + Arrays.toString(mqttMessage.getPayload())); 219 log.info("Message Arrived: " + Arrays.toString(mqttMessage.getPayload()));
240 - MqttMessage message = new MqttMessage();  
241 String responseTopic = requestTopic.replace("request", "response"); 220 String responseTopic = requestTopic.replace("request", "response");
242 - message.setPayload("{\"value1\":\"A\", \"value2\":\"B\"}".getBytes("UTF-8"));  
243 qoS = mqttMessage.getQos(); 221 qoS = mqttMessage.getQos();
244 - client.publish(responseTopic, message); 222 + client.publish(responseTopic, processMessageArrived(requestTopic, mqttMessage));
245 latch.countDown(); 223 latch.countDown();
246 } 224 }
247 225
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.rpc;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
  20 +import org.junit.After;
  21 +import org.junit.Before;
  22 +import org.junit.Ignore;
  23 +import org.junit.Test;
  24 +import org.thingsboard.server.common.data.TransportPayloadType;
  25 +
  26 +@Slf4j
  27 +public abstract class AbstractMqttServerSideRpcJsonIntegrationTest extends AbstractMqttServerSideRpcIntegrationTest {
  28 +
  29 + @Before
  30 + public void beforeTest() throws Exception {
  31 + processBeforeTest("RPC test device", "RPC test gateway", TransportPayloadType.JSON, null, null);
  32 + }
  33 +
  34 + @After
  35 + public void afterTest() throws Exception {
  36 + super.processAfterTest();
  37 + }
  38 +
  39 + @Test
  40 + public void testServerMqttOneWayRpc() throws Exception {
  41 + processOneWayRpcTest();
  42 + }
  43 +
  44 + @Test
  45 + public void testServerMqttTwoWayRpc() throws Exception {
  46 + processTwoWayRpcTest();
  47 + }
  48 +
  49 + @Test
  50 + public void testGatewayServerMqttOneWayRpc() throws Exception {
  51 + processOneWayRpcTestGateway("Gateway Device OneWay RPC Json");
  52 + }
  53 +
  54 + @Test
  55 + public void testGatewayServerMqttTwoWayRpc() throws Exception {
  56 + processTwoWayRpcTestGateway("Gateway Device TwoWay RPC Json");
  57 + }
  58 +
  59 + protected void processOneWayRpcTestGateway(String deviceName) throws Exception {
  60 + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
  61 + String payload = "{\"device\": \"" + deviceName + "\", \"type\": \"" + TransportPayloadType.JSON.name() + "\"}";
  62 + byte[] payloadBytes = payload.getBytes();
  63 + validateOneWayRpcGatewayResponse(deviceName, client, payloadBytes);
  64 + }
  65 +
  66 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.rpc;
  17 +
  18 +import com.google.protobuf.InvalidProtocolBufferException;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
  21 +import org.eclipse.paho.client.mqttv3.MqttException;
  22 +import org.eclipse.paho.client.mqttv3.MqttMessage;
  23 +import org.junit.After;
  24 +import org.junit.Before;
  25 +import org.junit.Ignore;
  26 +import org.junit.Test;
  27 +import org.thingsboard.server.common.data.TransportPayloadType;
  28 +import org.thingsboard.server.common.data.device.profile.MqttTopics;
  29 +import org.thingsboard.server.gen.transport.TransportApiProtos;
  30 +import org.thingsboard.server.gen.transport.TransportProtos;
  31 +
  32 +import static org.junit.Assert.assertEquals;
  33 +import static org.junit.Assert.assertNotNull;
  34 +
  35 +@Slf4j
  36 +public abstract class AbstractMqttServerSideRpcProtoIntegrationTest extends AbstractMqttServerSideRpcIntegrationTest {
  37 +
  38 + @Before
  39 + public void beforeTest() throws Exception {
  40 + processBeforeTest("RPC test device", "RPC test gateway", TransportPayloadType.PROTOBUF, null, null);
  41 + }
  42 +
  43 + @After
  44 + public void afterTest() throws Exception {
  45 + super.processAfterTest();
  46 + }
  47 +
  48 + @Test
  49 + public void testServerMqttOneWayRpc() throws Exception {
  50 + processOneWayRpcTest();
  51 + }
  52 +
  53 + @Test
  54 + public void testServerMqttTwoWayRpc() throws Exception {
  55 + processTwoWayRpcTest();
  56 + }
  57 +
  58 + @Test
  59 + public void testGatewayServerMqttOneWayRpc() throws Exception {
  60 + processOneWayRpcTestGateway("Gateway Device OneWay RPC Proto");
  61 + }
  62 +
  63 + @Test
  64 + public void testGatewayServerMqttTwoWayRpc() throws Exception {
  65 + processTwoWayRpcTestGateway("Gateway Device TwoWay RPC Proto");
  66 + }
  67 +
  68 + protected void processTwoWayRpcTestGateway(String deviceName) throws Exception {
  69 + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
  70 + TransportApiProtos.ConnectMsg connectMsgProto = getConnectProto(deviceName);
  71 + byte[] payloadBytes = connectMsgProto.toByteArray();
  72 + validateTwoWayRpcGateway(deviceName, client, payloadBytes);
  73 + }
  74 +
  75 + protected void processOneWayRpcTestGateway(String deviceName) throws Exception {
  76 + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
  77 + TransportApiProtos.ConnectMsg connectMsgProto = getConnectProto(deviceName);
  78 + byte[] payloadBytes = connectMsgProto.toByteArray();
  79 + validateOneWayRpcGatewayResponse(deviceName, client, payloadBytes);
  80 + }
  81 +
  82 +
  83 + private TransportApiProtos.ConnectMsg getConnectProto(String deviceName) {
  84 + TransportApiProtos.ConnectMsg.Builder builder = TransportApiProtos.ConnectMsg.newBuilder();
  85 + builder.setDeviceName(deviceName);
  86 + builder.setDeviceType(TransportPayloadType.PROTOBUF.name());
  87 + return builder.build();
  88 + }
  89 +
  90 + protected MqttMessage processMessageArrived(String requestTopic, MqttMessage mqttMessage) throws MqttException, InvalidProtocolBufferException {
  91 + MqttMessage message = new MqttMessage();
  92 + if (requestTopic.startsWith(MqttTopics.BASE_DEVICE_API_TOPIC)) {
  93 + TransportProtos.ToDeviceRpcResponseMsg toDeviceRpcResponseMsg = TransportProtos.ToDeviceRpcResponseMsg.newBuilder()
  94 + .setPayload(DEVICE_RESPONSE)
  95 + .setRequestId(0)
  96 + .build();
  97 + message.setPayload(toDeviceRpcResponseMsg.toByteArray());
  98 + } else {
  99 + TransportApiProtos.GatewayDeviceRpcRequestMsg msg = TransportApiProtos.GatewayDeviceRpcRequestMsg.parseFrom(mqttMessage.getPayload());
  100 + String deviceName = msg.getDeviceName();
  101 + int requestId = msg.getRpcRequestMsg().getRequestId();
  102 + TransportApiProtos.GatewayRpcResponseMsg gatewayRpcResponseMsg = TransportApiProtos.GatewayRpcResponseMsg.newBuilder()
  103 + .setDeviceName(deviceName)
  104 + .setId(requestId)
  105 + .setData("{\"success\": true}")
  106 + .build();
  107 + message.setPayload(gatewayRpcResponseMsg.toByteArray());
  108 + }
  109 + return message;
  110 + }
  111 +
  112 +
  113 +
  114 +}
@@ -16,11 +16,11 @@ @@ -16,11 +16,11 @@
16 package org.thingsboard.server.mqtt.rpc.nosql; 16 package org.thingsboard.server.mqtt.rpc.nosql;
17 17
18 import org.thingsboard.server.dao.service.DaoNoSqlTest; 18 import org.thingsboard.server.dao.service.DaoNoSqlTest;
19 -import org.thingsboard.server.mqtt.rpc.AbstractMqttServerSideRpcIntegrationTest; 19 +import org.thingsboard.server.mqtt.rpc.AbstractMqttServerSideRpcDefaultIntegrationTest;
20 20
21 /** 21 /**
22 * Created by Valerii Sosliuk on 8/22/2017. 22 * Created by Valerii Sosliuk on 8/22/2017.
23 */ 23 */
24 @DaoNoSqlTest 24 @DaoNoSqlTest
25 -public class MqttServerSideRpcNoSqlIntegrationTest extends AbstractMqttServerSideRpcIntegrationTest { 25 +public class MqttServerSideRpcNoSqlIntegrationTest extends AbstractMqttServerSideRpcDefaultIntegrationTest {
26 } 26 }
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.rpc.sql;
  17 +
  18 +import org.thingsboard.server.dao.service.DaoSqlTest;
  19 +import org.thingsboard.server.mqtt.rpc.AbstractMqttServerSideRpcJsonIntegrationTest;
  20 +
  21 +@DaoSqlTest
  22 +public class MqttServerSideRpcJsonSqlIntegrationTest extends AbstractMqttServerSideRpcJsonIntegrationTest {
  23 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.rpc.sql;
  17 +
  18 +import org.thingsboard.server.dao.service.DaoSqlTest;
  19 +import org.thingsboard.server.mqtt.rpc.AbstractMqttServerSideRpcProtoIntegrationTest;
  20 +
  21 +
  22 +@DaoSqlTest
  23 +public class MqttServerSideRpcProtoSqlIntegrationTest extends AbstractMqttServerSideRpcProtoIntegrationTest {
  24 +}
@@ -16,11 +16,11 @@ @@ -16,11 +16,11 @@
16 package org.thingsboard.server.mqtt.rpc.sql; 16 package org.thingsboard.server.mqtt.rpc.sql;
17 17
18 import org.thingsboard.server.dao.service.DaoSqlTest; 18 import org.thingsboard.server.dao.service.DaoSqlTest;
19 -import org.thingsboard.server.mqtt.rpc.AbstractMqttServerSideRpcIntegrationTest; 19 +import org.thingsboard.server.mqtt.rpc.AbstractMqttServerSideRpcDefaultIntegrationTest;
20 20
21 /** 21 /**
22 * Created by Valerii Sosliuk on 8/22/2017. 22 * Created by Valerii Sosliuk on 8/22/2017.
23 */ 23 */
24 @DaoSqlTest 24 @DaoSqlTest
25 -public class MqttServerSideRpcSqlIntegrationTest extends AbstractMqttServerSideRpcIntegrationTest { 25 +public class MqttServerSideRpcSqlIntegrationTest extends AbstractMqttServerSideRpcDefaultIntegrationTest {
26 } 26 }
1 -/**  
2 - * Copyright © 2016-2020 The Thingsboard Authors  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - */  
16 -package org.thingsboard.server.mqtt.telemetry;  
17 -  
18 -import io.netty.handler.codec.mqtt.MqttQoS;  
19 -import lombok.extern.slf4j.Slf4j;  
20 -import org.eclipse.paho.client.mqttv3.*;  
21 -import org.junit.Before;  
22 -import org.junit.Ignore;  
23 -import org.junit.Test;  
24 -import org.springframework.web.util.UriComponentsBuilder;  
25 -import org.thingsboard.server.common.data.Device;  
26 -import org.thingsboard.server.common.data.security.DeviceCredentials;  
27 -import org.thingsboard.server.controller.AbstractControllerTest;  
28 -import org.thingsboard.server.dao.service.DaoNoSqlTest;  
29 -  
30 -import java.net.URI;  
31 -import java.util.*;  
32 -import java.util.concurrent.CountDownLatch;  
33 -import java.util.concurrent.TimeUnit;  
34 -  
35 -import static org.junit.Assert.assertEquals;  
36 -import static org.junit.Assert.assertNotNull;  
37 -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;  
38 -  
39 -/**  
40 - * @author Valerii Sosliuk  
41 - */  
42 -@Slf4j  
43 -public abstract class AbstractMqttTelemetryIntegrationTest extends AbstractControllerTest {  
44 -  
45 - private static final String MQTT_URL = "tcp://localhost:1883";  
46 -  
47 - private Device savedDevice;  
48 - private String accessToken;  
49 -  
50 - @Before  
51 - public void beforeTest() throws Exception {  
52 - loginTenantAdmin();  
53 -  
54 - Device device = new Device();  
55 - device.setName("Test device");  
56 - device.setType("default");  
57 - savedDevice = doPost("/api/device", device, Device.class);  
58 -  
59 - DeviceCredentials deviceCredentials =  
60 - doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);  
61 -  
62 - assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId());  
63 - accessToken = deviceCredentials.getCredentialsId();  
64 - assertNotNull(accessToken);  
65 - }  
66 -  
67 - @Test  
68 - public void testPushMqttRpcData() throws Exception {  
69 - String clientId = MqttAsyncClient.generateClientId();  
70 - MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId);  
71 -  
72 - MqttConnectOptions options = new MqttConnectOptions();  
73 - options.setUserName(accessToken);  
74 - client.connect(options);  
75 - Thread.sleep(3000);  
76 - MqttMessage message = new MqttMessage();  
77 - message.setPayload("{\"key1\":\"value1\", \"key2\":true, \"key3\": 3.0, \"key4\": 4}".getBytes());  
78 - client.publish("v1/devices/me/telemetry", message);  
79 -  
80 - String deviceId = savedDevice.getId().getId().toString();  
81 -  
82 - Thread.sleep(2000);  
83 - List<String> actualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + deviceId + "/keys/timeseries", List.class);  
84 - Set<String> actualKeySet = new HashSet<>(actualKeys);  
85 -  
86 - List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4");  
87 - Set<String> expectedKeySet = new HashSet<>(expectedKeys);  
88 -  
89 - assertEquals(expectedKeySet, actualKeySet);  
90 -  
91 - String getTelemetryValuesUrl = "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/timeseries?keys=" + String.join(",", actualKeySet);  
92 - Map<String, List<Map<String, String>>> values = doGetAsync(getTelemetryValuesUrl, Map.class);  
93 -  
94 - assertEquals("value1", values.get("key1").get(0).get("value"));  
95 - assertEquals("true", values.get("key2").get(0).get("value"));  
96 - assertEquals("3.0", values.get("key3").get(0).get("value"));  
97 - assertEquals("4", values.get("key4").get(0).get("value"));  
98 - }  
99 -  
100 -  
101 -// @Test - Unstable  
102 - public void testMqttQoSLevel() throws Exception {  
103 - String clientId = MqttAsyncClient.generateClientId();  
104 - MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId);  
105 -  
106 - MqttConnectOptions options = new MqttConnectOptions();  
107 - options.setUserName(accessToken);  
108 - CountDownLatch latch = new CountDownLatch(1);  
109 - TestMqttCallback callback = new TestMqttCallback(client, latch);  
110 - client.setCallback(callback);  
111 - client.connect(options).waitForCompletion(5000);  
112 - client.subscribe("v1/devices/me/attributes", MqttQoS.AT_MOST_ONCE.value());  
113 - String payload = "{\"key\":\"uniqueValue\"}";  
114 -// TODO 3.1: we need to acknowledge subscription only after it is processed by device actor and not when the message is pushed to queue.  
115 -// MqttClient -> SUB REQUEST -> Transport -> Kafka -> Device Actor (subscribed)  
116 -// MqttClient <- SUB_ACK <- Transport  
117 - Thread.sleep(5000);  
118 - doPostAsync("/api/plugins/telemetry/" + savedDevice.getId() + "/SHARED_SCOPE", payload, String.class, status().isOk());  
119 - latch.await(10, TimeUnit.SECONDS);  
120 - assertEquals(payload, callback.getPayload());  
121 - assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS());  
122 - }  
123 -  
124 - private static class TestMqttCallback implements MqttCallback {  
125 -  
126 - private final MqttAsyncClient client;  
127 - private final CountDownLatch latch;  
128 - private volatile Integer qoS;  
129 - private volatile String payload;  
130 -  
131 - String getPayload() {  
132 - return payload;  
133 - }  
134 -  
135 - TestMqttCallback(MqttAsyncClient client, CountDownLatch latch) {  
136 - this.client = client;  
137 - this.latch = latch;  
138 - }  
139 -  
140 - int getQoS() {  
141 - return qoS;  
142 - }  
143 -  
144 - @Override  
145 - public void connectionLost(Throwable throwable) {  
146 - log.error("Client connection lost", throwable);  
147 - }  
148 -  
149 - @Override  
150 - public void messageArrived(String requestTopic, MqttMessage mqttMessage) {  
151 - payload = new String(mqttMessage.getPayload());  
152 - qoS = mqttMessage.getQos();  
153 - latch.countDown();  
154 - }  
155 -  
156 - @Override  
157 - public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {  
158 -  
159 - }  
160 - }  
161 -  
162 -  
163 -}  
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.telemetry.attributes;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
  20 +import org.junit.After;
  21 +import org.junit.Before;
  22 +import org.junit.Test;
  23 +import org.thingsboard.server.common.data.Device;
  24 +import org.thingsboard.server.common.data.device.profile.MqttTopics;
  25 +import org.thingsboard.server.common.data.id.DeviceId;
  26 +import org.thingsboard.server.mqtt.AbstractMqttIntegrationTest;
  27 +
  28 +import java.util.Arrays;
  29 +import java.util.HashSet;
  30 +import java.util.LinkedHashMap;
  31 +import java.util.List;
  32 +import java.util.Map;
  33 +import java.util.Set;
  34 +
  35 +import static org.junit.Assert.assertEquals;
  36 +import static org.junit.Assert.assertNotNull;
  37 +import static org.junit.Assert.assertTrue;
  38 +
  39 +@Slf4j
  40 +public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqttIntegrationTest {
  41 +
  42 + protected static final String PAYLOAD_VALUES_STR = "{\"key1\":\"value1\", \"key2\":true, \"key3\": 3.0, \"key4\": 4," +
  43 + " \"key5\": {\"someNumber\": 42, \"someArray\": [1,2,3], \"someNestedObject\": {\"key\": \"value\"}}}";
  44 +
  45 + @Before
  46 + public void beforeTest() throws Exception {
  47 + processBeforeTest("Test Post Attributes device", "Test Post Attributes gateway", null, null, null);
  48 + }
  49 +
  50 + @After
  51 + public void afterTest() throws Exception {
  52 + processAfterTest();
  53 + }
  54 +
  55 + @Test
  56 + public void testPushMqttAttributes() throws Exception {
  57 + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");
  58 + processAttributesTest(MqttTopics.DEVICE_ATTRIBUTES_TOPIC, expectedKeys, PAYLOAD_VALUES_STR.getBytes());
  59 + }
  60 +
  61 + @Test
  62 + public void testPushMqttAttributesGateway() throws Exception {
  63 + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");
  64 + String deviceName1 = "Device A";
  65 + String deviceName2 = "Device B";
  66 + String payload = getGatewayAttributesJsonPayload(deviceName1, deviceName2);
  67 + processGatewayAttributesTest(expectedKeys, payload.getBytes(), deviceName1, deviceName2);
  68 + }
  69 +
  70 + protected void processAttributesTest(String topic, List<String> expectedKeys, byte[] payload) throws Exception {
  71 + MqttAsyncClient client = getMqttAsyncClient(accessToken);
  72 +
  73 + publishMqttMsg(client, payload, topic);
  74 +
  75 + DeviceId deviceId = savedDevice.getId();
  76 +
  77 + long start = System.currentTimeMillis();
  78 + long end = System.currentTimeMillis() + 2000;
  79 +
  80 + List<String> actualKeys = null;
  81 + while (start <= end) {
  82 + actualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + deviceId + "/keys/attributes/CLIENT_SCOPE", List.class);
  83 + if (actualKeys.size() == expectedKeys.size()) {
  84 + break;
  85 + }
  86 + Thread.sleep(100);
  87 + start += 100;
  88 + }
  89 + assertNotNull(actualKeys);
  90 +
  91 + Set<String> actualKeySet = new HashSet<>(actualKeys);
  92 +
  93 + Set<String> expectedKeySet = new HashSet<>(expectedKeys);
  94 +
  95 + assertEquals(expectedKeySet, actualKeySet);
  96 +
  97 + String getAttributesValuesUrl = getAttributesValuesUrl(deviceId, actualKeySet);
  98 + List<Map<String, Object>> values = doGetAsync(getAttributesValuesUrl, List.class);
  99 + assertAttributesValues(values, expectedKeySet);
  100 + String deleteAttributesUrl = "/api/plugins/telemetry/DEVICE/" + deviceId + "/CLIENT_SCOPE?keys=" + String.join(",", actualKeySet);
  101 + doDelete(deleteAttributesUrl);
  102 + }
  103 +
  104 + protected void processGatewayAttributesTest(List<String> expectedKeys, byte[] payload, String firstDeviceName, String secondDeviceName) throws Exception {
  105 + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
  106 +
  107 + publishMqttMsg(client, payload, MqttTopics.GATEWAY_ATTRIBUTES_TOPIC);
  108 +
  109 + Thread.sleep(2000);
  110 +
  111 + Device firstDevice = doGet("/api/tenant/devices?deviceName=" + firstDeviceName, Device.class);
  112 + assertNotNull(firstDevice);
  113 + Device secondDevice = doGet("/api/tenant/devices?deviceName=" + secondDeviceName, Device.class);
  114 + assertNotNull(secondDevice);
  115 +
  116 + List<String> firstDeviceActualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + firstDevice.getId() + "/keys/attributes/CLIENT_SCOPE", List.class);
  117 + Set<String> firstDeviceActualKeySet = new HashSet<>(firstDeviceActualKeys);
  118 +
  119 + List<String> secondDeviceActualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + secondDevice.getId() + "/keys/attributes/CLIENT_SCOPE", List.class);
  120 + Set<String> secondDeviceActualKeySet = new HashSet<>(secondDeviceActualKeys);
  121 +
  122 + Set<String> expectedKeySet = new HashSet<>(expectedKeys);
  123 +
  124 + assertEquals(expectedKeySet, firstDeviceActualKeySet);
  125 + assertEquals(expectedKeySet, secondDeviceActualKeySet);
  126 +
  127 + String getAttributesValuesUrlFirstDevice = getAttributesValuesUrl(firstDevice.getId(), firstDeviceActualKeySet);
  128 + String getAttributesValuesUrlSecondDevice = getAttributesValuesUrl(firstDevice.getId(), secondDeviceActualKeySet);
  129 +
  130 + List<Map<String, Object>> firstDeviceValues = doGetAsync(getAttributesValuesUrlFirstDevice, List.class);
  131 + List<Map<String, Object>> secondDeviceValues = doGetAsync(getAttributesValuesUrlSecondDevice, List.class);
  132 +
  133 + assertAttributesValues(firstDeviceValues, expectedKeySet);
  134 + assertAttributesValues(secondDeviceValues, expectedKeySet);
  135 +
  136 + }
  137 +
  138 + protected void assertAttributesValues(List<Map<String, Object>> deviceValues, Set<String> expectedKeySet) {
  139 + for (Map<String, Object> map : deviceValues) {
  140 + String key = (String) map.get("key");
  141 + Object value = map.get("value");
  142 + assertTrue(expectedKeySet.contains(key));
  143 + switch (key) {
  144 + case "key1":
  145 + assertEquals("value1", value);
  146 + break;
  147 + case "key2":
  148 + assertEquals(true, value);
  149 + break;
  150 + case "key3":
  151 + assertEquals(3.0, value);
  152 + break;
  153 + case "key4":
  154 + assertEquals(4, value);
  155 + break;
  156 + case "key5":
  157 + assertNotNull(value);
  158 + assertEquals(3, ((LinkedHashMap) value).size());
  159 + assertEquals(42, ((LinkedHashMap) value).get("someNumber"));
  160 + assertEquals(Arrays.asList(1, 2, 3), ((LinkedHashMap) value).get("someArray"));
  161 + LinkedHashMap<String, String> someNestedObject = (LinkedHashMap) ((LinkedHashMap) value).get("someNestedObject");
  162 + assertEquals("value", someNestedObject.get("key"));
  163 + break;
  164 + }
  165 + }
  166 + }
  167 +
  168 + protected String getGatewayAttributesJsonPayload(String deviceA, String deviceB) {
  169 + return "{\"" + deviceA + "\": " + PAYLOAD_VALUES_STR + ", \"" + deviceB + "\": " + PAYLOAD_VALUES_STR + "}";
  170 + }
  171 +
  172 + private String getAttributesValuesUrl(DeviceId deviceId, Set<String> actualKeySet) {
  173 + return "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/attributes/CLIENT_SCOPE?keys=" + String.join(",", actualKeySet);
  174 + }
  175 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.telemetry.attributes;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.junit.After;
  20 +import org.junit.Before;
  21 +import org.junit.Test;
  22 +import org.thingsboard.server.common.data.TransportPayloadType;
  23 +
  24 +import java.util.Arrays;
  25 +import java.util.List;
  26 +
  27 +@Slf4j
  28 +public abstract class AbstractMqttAttributesJsonIntegrationTest extends AbstractMqttAttributesIntegrationTest {
  29 +
  30 + private static final String POST_DATA_ATTRIBUTES_TOPIC = "data/attributes";
  31 +
  32 + @Before
  33 + public void beforeTest() throws Exception {
  34 + processBeforeTest("Test Post Attributes device", "Test Post Attributes gateway", TransportPayloadType.JSON, null, POST_DATA_ATTRIBUTES_TOPIC);
  35 + }
  36 +
  37 + @After
  38 + public void afterTest() throws Exception {
  39 + processAfterTest();
  40 + }
  41 +
  42 + @Test
  43 + public void testPushMqttAttributes() throws Exception {
  44 + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");
  45 + processAttributesTest(POST_DATA_ATTRIBUTES_TOPIC, expectedKeys, PAYLOAD_VALUES_STR.getBytes());
  46 + }
  47 +
  48 + @Test
  49 + public void testPushMqttAttributesGateway() throws Exception {
  50 + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");
  51 + String deviceName1 = "Device A";
  52 + String deviceName2 = "Device B";
  53 + String payload = getGatewayAttributesJsonPayload(deviceName1, deviceName2);
  54 + processGatewayAttributesTest(expectedKeys, payload.getBytes(), deviceName1, deviceName2);
  55 + }
  56 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.telemetry.attributes;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.junit.After;
  20 +import org.junit.Before;
  21 +import org.junit.Test;
  22 +import org.thingsboard.server.common.data.TransportPayloadType;
  23 +import org.thingsboard.server.gen.transport.TransportApiProtos;
  24 +import org.thingsboard.server.gen.transport.TransportProtos;
  25 +
  26 +import java.util.Arrays;
  27 +import java.util.List;
  28 +
  29 +import static org.junit.Assert.assertEquals;
  30 +import static org.junit.Assert.assertNotNull;
  31 +import static org.junit.Assert.assertTrue;
  32 +
  33 +@Slf4j
  34 +public abstract class AbstractMqttAttributesProtoIntegrationTest extends AbstractMqttAttributesIntegrationTest {
  35 +
  36 + private static final String POST_DATA_ATTRIBUTES_TOPIC = "proto/attributes";
  37 +
  38 + @Before
  39 + public void beforeTest() throws Exception {
  40 + processBeforeTest("Test Post Attributes device", "Test Post Attributes gateway", TransportPayloadType.PROTOBUF, null, POST_DATA_ATTRIBUTES_TOPIC);
  41 + }
  42 +
  43 + @After
  44 + public void afterTest() throws Exception {
  45 + processAfterTest();
  46 + }
  47 +
  48 + @Test
  49 + public void testPushMqttAttributes() throws Exception {
  50 + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");
  51 + TransportProtos.PostAttributeMsg msg = getPostAttributeMsg(expectedKeys);
  52 + processAttributesTest(POST_DATA_ATTRIBUTES_TOPIC, expectedKeys, msg.toByteArray());
  53 + }
  54 +
  55 + @Test
  56 + public void testPushMqttAttributesGateway() throws Exception {
  57 + TransportApiProtos.GatewayAttributesMsg.Builder gatewayAttributesMsgProtoBuilder = TransportApiProtos.GatewayAttributesMsg.newBuilder();
  58 + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");
  59 + String deviceName1 = "Device A";
  60 + String deviceName2 = "Device B";
  61 + TransportApiProtos.AttributesMsg firstDeviceAttributesMsgProto = getDeviceAttributesMsgProto(deviceName1, expectedKeys);
  62 + TransportApiProtos.AttributesMsg secondDeviceAttributesMsgProto = getDeviceAttributesMsgProto(deviceName2, expectedKeys);
  63 + gatewayAttributesMsgProtoBuilder.addAllMsg(Arrays.asList(firstDeviceAttributesMsgProto, secondDeviceAttributesMsgProto));
  64 + TransportApiProtos.GatewayAttributesMsg gatewayAttributesMsg = gatewayAttributesMsgProtoBuilder.build();
  65 + processGatewayAttributesTest(expectedKeys, gatewayAttributesMsg.toByteArray(), deviceName1, deviceName2);
  66 + }
  67 +
  68 + private TransportApiProtos.AttributesMsg getDeviceAttributesMsgProto(String deviceName, List<String> expectedKeys) {
  69 + TransportApiProtos.AttributesMsg.Builder deviceAttributesMsgBuilder = TransportApiProtos.AttributesMsg.newBuilder();
  70 + TransportProtos.PostAttributeMsg msg = getPostAttributeMsg(expectedKeys);
  71 + deviceAttributesMsgBuilder.setDeviceName(deviceName);
  72 + deviceAttributesMsgBuilder.setMsg(msg);
  73 + return deviceAttributesMsgBuilder.build();
  74 + }
  75 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.telemetry.attributes.nosql;
  17 +
  18 +import org.thingsboard.server.dao.service.DaoNoSqlTest;
  19 +import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesIntegrationTest;
  20 +
  21 +@DaoNoSqlTest
  22 +public class MqttAttributesNoSqlIntegrationTest extends AbstractMqttAttributesIntegrationTest {
  23 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.telemetry.attributes.nosql;
  17 +
  18 +import org.thingsboard.server.dao.service.DaoNoSqlTest;
  19 +import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesIntegrationTest;
  20 +import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesJsonIntegrationTest;
  21 +
  22 +@DaoNoSqlTest
  23 +public class MqttAttributesNoSqlJsonIntegrationTest extends AbstractMqttAttributesJsonIntegrationTest {
  24 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.telemetry.attributes.nosql;
  17 +
  18 +import org.thingsboard.server.dao.service.DaoNoSqlTest;
  19 +import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesIntegrationTest;
  20 +import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesProtoIntegrationTest;
  21 +
  22 +@DaoNoSqlTest
  23 +public class MqttAttributesNoSqlProtoIntegrationTest extends AbstractMqttAttributesProtoIntegrationTest {
  24 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.telemetry.attributes.sql;
  17 +
  18 +import org.thingsboard.server.dao.service.DaoSqlTest;
  19 +import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesIntegrationTest;
  20 +
  21 +@DaoSqlTest
  22 +public class MqttAttributesSqlIntegrationTest extends AbstractMqttAttributesIntegrationTest {
  23 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.telemetry.attributes.sql;
  17 +
  18 +import org.thingsboard.server.dao.service.DaoSqlTest;
  19 +import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesJsonIntegrationTest;
  20 +
  21 +@DaoSqlTest
  22 +public class MqttAttributesSqlJsonIntegrationTest extends AbstractMqttAttributesJsonIntegrationTest {
  23 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.telemetry.attributes.sql;
  17 +
  18 +import org.thingsboard.server.dao.service.DaoSqlTest;
  19 +import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesJsonIntegrationTest;
  20 +import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesProtoIntegrationTest;
  21 +
  22 +@DaoSqlTest
  23 +public class MqttAttributesSqlProtoIntegrationTest extends AbstractMqttAttributesProtoIntegrationTest {
  24 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.telemetry.timeseries;
  17 +
  18 +import io.netty.handler.codec.mqtt.MqttQoS;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
  21 +import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
  22 +import org.eclipse.paho.client.mqttv3.MqttCallback;
  23 +import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
  24 +import org.eclipse.paho.client.mqttv3.MqttMessage;
  25 +import org.junit.After;
  26 +import org.junit.Before;
  27 +import org.junit.Test;
  28 +import org.thingsboard.server.common.data.Device;
  29 +import org.thingsboard.server.common.data.TransportPayloadType;
  30 +import org.thingsboard.server.common.data.device.profile.MqttTopics;
  31 +import org.thingsboard.server.common.data.id.DeviceId;
  32 +import org.thingsboard.server.mqtt.AbstractMqttIntegrationTest;
  33 +
  34 +import java.util.Arrays;
  35 +import java.util.HashSet;
  36 +import java.util.List;
  37 +import java.util.Map;
  38 +import java.util.Set;
  39 +import java.util.concurrent.CountDownLatch;
  40 +import java.util.concurrent.TimeUnit;
  41 +
  42 +import static org.junit.Assert.assertEquals;
  43 +import static org.junit.Assert.assertNotNull;
  44 +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
  45 +
  46 +@Slf4j
  47 +public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqttIntegrationTest {
  48 +
  49 + protected static final String PAYLOAD_VALUES_STR = "{\"key1\":\"value1\", \"key2\":true, \"key3\": 3.0, \"key4\": 4," +
  50 + " \"key5\": {\"someNumber\": 42, \"someArray\": [1,2,3], \"someNestedObject\": {\"key\": \"value\"}}}";
  51 +
  52 + @Before
  53 + public void beforeTest() throws Exception {
  54 + processBeforeTest("Test Post Telemetry device", "Test Post Telemetry gateway", null, null, null);
  55 + }
  56 +
  57 + @After
  58 + public void afterTest() throws Exception {
  59 + processAfterTest();
  60 + }
  61 +
  62 + @Test
  63 + public void testPushMqttTelemetry() throws Exception {
  64 + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");
  65 + processTelemetryTest(MqttTopics.DEVICE_TELEMETRY_TOPIC, expectedKeys, PAYLOAD_VALUES_STR.getBytes(), false);
  66 + }
  67 +
  68 + @Test
  69 + public void testPushMqttTelemetryWithTs() throws Exception {
  70 + String payloadStr = "{\"ts\": 10000, \"values\": " + PAYLOAD_VALUES_STR + "}";
  71 + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");
  72 + processTelemetryTest(MqttTopics.DEVICE_TELEMETRY_TOPIC, expectedKeys, payloadStr.getBytes(), true);
  73 + }
  74 +
  75 + @Test
  76 + public void testPushMqttTelemetryGateway() throws Exception {
  77 + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");
  78 + String deviceName1 = "Device A";
  79 + String deviceName2 = "Device B";
  80 + String payload = getGatewayTelemetryJsonPayload(deviceName1, deviceName2, "10000", "20000");
  81 + processGatewayTelemetryTest(MqttTopics.GATEWAY_TELEMETRY_TOPIC, expectedKeys, payload.getBytes(), deviceName1, deviceName2);
  82 + }
  83 +
  84 + @Test
  85 + public void testGatewayConnect() throws Exception {
  86 + String payload = "{\"device\":\"Device A\"}";
  87 + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
  88 + publishMqttMsg(client, payload.getBytes(), MqttTopics.GATEWAY_CONNECT_TOPIC);
  89 +
  90 + Thread.sleep(2000);
  91 +
  92 + String deviceName = "Device A";
  93 + Device device = doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class);
  94 + assertNotNull(device);
  95 + }
  96 +
  97 + protected void processTelemetryTest(String topic, List<String> expectedKeys, byte[] payload, boolean withTs) throws Exception {
  98 + MqttAsyncClient client = getMqttAsyncClient(accessToken);
  99 + publishMqttMsg(client, payload, topic);
  100 +
  101 + String deviceId = savedDevice.getId().getId().toString();
  102 +
  103 + long start = System.currentTimeMillis();
  104 + long end = System.currentTimeMillis() + 2000;
  105 +
  106 + List<String> actualKeys = null;
  107 + while (start <= end) {
  108 + actualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + deviceId + "/keys/timeseries", List.class);
  109 + if (actualKeys.size() == expectedKeys.size()) {
  110 + break;
  111 + }
  112 + Thread.sleep(100);
  113 + start += 100;
  114 + }
  115 + assertNotNull(actualKeys);
  116 +
  117 + Set<String> actualKeySet = new HashSet<>(actualKeys);
  118 + Set<String> expectedKeySet = new HashSet<>(expectedKeys);
  119 +
  120 + assertEquals(expectedKeySet, actualKeySet);
  121 +
  122 + String getTelemetryValuesUrl;
  123 + if (withTs) {
  124 + getTelemetryValuesUrl = "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/timeseries?startTs=0&endTs=15000&keys=" + String.join(",", actualKeySet);
  125 + } else {
  126 + getTelemetryValuesUrl = "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/timeseries?keys=" + String.join(",", actualKeySet);
  127 + }
  128 + Map<String, List<Map<String, String>>> values = doGetAsync(getTelemetryValuesUrl, Map.class);
  129 +
  130 + if (withTs) {
  131 + assertTs(values, expectedKeys, 10000, 0);
  132 + }
  133 + assertValues(values, 0);
  134 + }
  135 +
  136 + protected void processGatewayTelemetryTest(String topic, List<String> expectedKeys, byte[] payload, String firstDeviceName, String secondDeviceName) throws Exception {
  137 + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
  138 +
  139 + publishMqttMsg(client, payload, topic);
  140 +
  141 + Thread.sleep(2000);
  142 +
  143 + Device firstDevice = doGet("/api/tenant/devices?deviceName=" + firstDeviceName, Device.class);
  144 + assertNotNull(firstDevice);
  145 + Device secondDevice = doGet("/api/tenant/devices?deviceName=" + secondDeviceName, Device.class);
  146 + assertNotNull(secondDevice);
  147 +
  148 + List<String> firstDeviceActualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + firstDevice.getId() + "/keys/timeseries", List.class);
  149 + Set<String> firstDeviceActualKeySet = new HashSet<>(firstDeviceActualKeys);
  150 +
  151 + List<String> secondDeviceActualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + secondDevice.getId() + "/keys/timeseries", List.class);
  152 + Set<String> secondDeviceActualKeySet = new HashSet<>(secondDeviceActualKeys);
  153 +
  154 + Set<String> expectedKeySet = new HashSet<>(expectedKeys);
  155 +
  156 + assertEquals(expectedKeySet, firstDeviceActualKeySet);
  157 + assertEquals(expectedKeySet, secondDeviceActualKeySet);
  158 +
  159 + String getTelemetryValuesUrlFirstDevice = getTelemetryValuesUrl(firstDevice.getId(), firstDeviceActualKeySet);
  160 + String getTelemetryValuesUrlSecondDevice = getTelemetryValuesUrl(firstDevice.getId(), secondDeviceActualKeySet);
  161 +
  162 + Map<String, List<Map<String, String>>> firstDeviceValues = doGetAsync(getTelemetryValuesUrlFirstDevice, Map.class);
  163 + Map<String, List<Map<String, String>>> secondDeviceValues = doGetAsync(getTelemetryValuesUrlSecondDevice, Map.class);
  164 +
  165 + assertGatewayDeviceData(firstDeviceValues, expectedKeys);
  166 + assertGatewayDeviceData(secondDeviceValues, expectedKeys);
  167 + }
  168 +
  169 + protected String getGatewayTelemetryJsonPayload(String deviceA, String deviceB, String firstTsValue, String secondTsValue) {
  170 + String payload = "[{\"ts\": " + firstTsValue + ", \"values\": " + PAYLOAD_VALUES_STR + "}, " +
  171 + "{\"ts\": " + secondTsValue + ", \"values\": " + PAYLOAD_VALUES_STR + "}]";
  172 + return "{\"" + deviceA + "\": " + payload + ", \"" + deviceB + "\": " + payload + "}";
  173 + }
  174 +
  175 + private String getTelemetryValuesUrl(DeviceId deviceId, Set<String> actualKeySet) {
  176 + return "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/timeseries?startTs=0&endTs=25000&keys=" + String.join(",", actualKeySet);
  177 + }
  178 +
  179 + private void assertGatewayDeviceData(Map<String, List<Map<String, String>>> deviceValues, List<String> expectedKeys) {
  180 +
  181 + assertEquals(2, deviceValues.get(expectedKeys.get(0)).size());
  182 + assertEquals(2, deviceValues.get(expectedKeys.get(1)).size());
  183 + assertEquals(2, deviceValues.get(expectedKeys.get(2)).size());
  184 + assertEquals(2, deviceValues.get(expectedKeys.get(3)).size());
  185 + assertEquals(2, deviceValues.get(expectedKeys.get(4)).size());
  186 +
  187 + assertTs(deviceValues, expectedKeys, 20000, 0);
  188 + assertTs(deviceValues, expectedKeys, 10000, 1);
  189 +
  190 + assertValues(deviceValues, 0);
  191 + assertValues(deviceValues, 1);
  192 +
  193 + }
  194 +
  195 + private void assertValues(Map<String, List<Map<String, String>>> deviceValues, int arrayIndex) {
  196 + for (Map.Entry<String, List<Map<String, String>>> entry : deviceValues.entrySet()) {
  197 + String key = entry.getKey();
  198 + List<Map<String, String>> tsKv = entry.getValue();
  199 + String value = tsKv.get(arrayIndex).get("value");
  200 + switch (key) {
  201 + case "key1":
  202 + assertEquals("value1", value);
  203 + break;
  204 + case "key2":
  205 + assertEquals("true", value);
  206 + break;
  207 + case "key3":
  208 + assertEquals("3.0", value);
  209 + break;
  210 + case "key4":
  211 + assertEquals("4", value);
  212 + break;
  213 + case "key5":
  214 + assertEquals("{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}", value);
  215 + break;
  216 + }
  217 + }
  218 + }
  219 +
  220 + private void assertTs(Map<String, List<Map<String, String>>> deviceValues, List<String> expectedKeys, int ts, int arrayIndex) {
  221 + assertEquals(ts, deviceValues.get(expectedKeys.get(0)).get(arrayIndex).get("ts"));
  222 + assertEquals(ts, deviceValues.get(expectedKeys.get(1)).get(arrayIndex).get("ts"));
  223 + assertEquals(ts, deviceValues.get(expectedKeys.get(2)).get(arrayIndex).get("ts"));
  224 + assertEquals(ts, deviceValues.get(expectedKeys.get(3)).get(arrayIndex).get("ts"));
  225 + assertEquals(ts, deviceValues.get(expectedKeys.get(4)).get(arrayIndex).get("ts"));
  226 + }
  227 +
  228 + // @Test - Unstable
  229 + public void testMqttQoSLevel() throws Exception {
  230 + String clientId = MqttAsyncClient.generateClientId();
  231 + MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId);
  232 +
  233 + MqttConnectOptions options = new MqttConnectOptions();
  234 + options.setUserName(accessToken);
  235 + CountDownLatch latch = new CountDownLatch(1);
  236 + TestMqttCallback callback = new TestMqttCallback(client, latch);
  237 + client.setCallback(callback);
  238 + client.connect(options).waitForCompletion(5000);
  239 + client.subscribe("v1/devices/me/attributes", MqttQoS.AT_MOST_ONCE.value());
  240 + String payload = "{\"key\":\"uniqueValue\"}";
  241 +// TODO 3.1: we need to acknowledge subscription only after it is processed by device actor and not when the message is pushed to queue.
  242 +// MqttClient -> SUB REQUEST -> Transport -> Kafka -> Device Actor (subscribed)
  243 +// MqttClient <- SUB_ACK <- Transport
  244 + Thread.sleep(5000);
  245 + doPostAsync("/api/plugins/telemetry/" + savedDevice.getId() + "/SHARED_SCOPE", payload, String.class, status().isOk());
  246 + latch.await(10, TimeUnit.SECONDS);
  247 + assertEquals(payload, callback.getPayload());
  248 + assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS());
  249 + }
  250 +
  251 + private static class TestMqttCallback implements MqttCallback {
  252 +
  253 + private final MqttAsyncClient client;
  254 + private final CountDownLatch latch;
  255 + private volatile Integer qoS;
  256 + private volatile String payload;
  257 +
  258 + String getPayload() {
  259 + return payload;
  260 + }
  261 +
  262 + TestMqttCallback(MqttAsyncClient client, CountDownLatch latch) {
  263 + this.client = client;
  264 + this.latch = latch;
  265 + }
  266 +
  267 + int getQoS() {
  268 + return qoS;
  269 + }
  270 +
  271 + @Override
  272 + public void connectionLost(Throwable throwable) {
  273 + log.error("Client connection lost", throwable);
  274 + }
  275 +
  276 + @Override
  277 + public void messageArrived(String requestTopic, MqttMessage mqttMessage) {
  278 + payload = new String(mqttMessage.getPayload());
  279 + qoS = mqttMessage.getQos();
  280 + latch.countDown();
  281 + }
  282 +
  283 + @Override
  284 + public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
  285 +
  286 + }
  287 + }
  288 +
  289 +
  290 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.telemetry.timeseries;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
  20 +import org.junit.After;
  21 +import org.junit.Before;
  22 +import org.junit.Test;
  23 +import org.thingsboard.server.common.data.Device;
  24 +import org.thingsboard.server.common.data.TransportPayloadType;
  25 +import org.thingsboard.server.common.data.device.profile.MqttTopics;
  26 +
  27 +import java.util.Arrays;
  28 +import java.util.List;
  29 +
  30 +import static org.junit.Assert.assertEquals;
  31 +import static org.junit.Assert.assertNotNull;
  32 +
  33 +@Slf4j
  34 +public abstract class AbstractMqttTimeseriesJsonIntegrationTest extends AbstractMqttTimeseriesIntegrationTest {
  35 +
  36 + private static final String POST_DATA_TELEMETRY_TOPIC = "data/telemetry";
  37 +
  38 + @Before
  39 + public void beforeTest() throws Exception {
  40 + processBeforeTest("Test Post Telemetry device json payload", "Test Post Telemetry gateway json payload", TransportPayloadType.JSON, POST_DATA_TELEMETRY_TOPIC, null);
  41 + }
  42 +
  43 + @After
  44 + public void afterTest() throws Exception {
  45 + processAfterTest();
  46 + }
  47 +
  48 + @Test
  49 + public void testPushMqttTelemetry() throws Exception {
  50 + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");
  51 + processTelemetryTest(POST_DATA_TELEMETRY_TOPIC, expectedKeys, PAYLOAD_VALUES_STR.getBytes(), false);
  52 + }
  53 +
  54 + @Test
  55 + public void testPushMqttTelemetryWithTs() throws Exception {
  56 + String payloadStr = "{\"ts\": 10000, \"values\": " + PAYLOAD_VALUES_STR + "}";
  57 + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");
  58 + processTelemetryTest(POST_DATA_TELEMETRY_TOPIC, expectedKeys, payloadStr.getBytes(), true);
  59 + }
  60 +
  61 + @Test
  62 + public void testPushMqttTelemetryGateway() throws Exception {
  63 + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");
  64 + String deviceName1 = "Device A";
  65 + String deviceName2 = "Device B";
  66 + String payload = getGatewayTelemetryJsonPayload(deviceName1, deviceName2, "10000", "20000");
  67 + processGatewayTelemetryTest(MqttTopics.GATEWAY_TELEMETRY_TOPIC, expectedKeys, payload.getBytes(), deviceName1, deviceName2);
  68 + }
  69 +
  70 + @Test
  71 + public void testGatewayConnect() throws Exception {
  72 + String payload = "{\"device\":\"Device A\", \"type\": \"" + TransportPayloadType.JSON.name() + "\"}";
  73 + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
  74 + publishMqttMsg(client, payload.getBytes(), MqttTopics.GATEWAY_CONNECT_TOPIC);
  75 +
  76 + Thread.sleep(2000);
  77 +
  78 + String deviceName = "Device A";
  79 + Device device = doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class);
  80 + assertNotNull(device);
  81 + }
  82 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.telemetry.timeseries;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
  20 +import org.junit.After;
  21 +import org.junit.Before;
  22 +import org.junit.Test;
  23 +import org.thingsboard.server.common.data.Device;
  24 +import org.thingsboard.server.common.data.TransportPayloadType;
  25 +import org.thingsboard.server.common.data.device.profile.MqttTopics;
  26 +import org.thingsboard.server.gen.transport.TransportApiProtos;
  27 +import org.thingsboard.server.gen.transport.TransportProtos;
  28 +
  29 +import java.util.Arrays;
  30 +import java.util.List;
  31 +
  32 +import static org.junit.Assert.assertEquals;
  33 +import static org.junit.Assert.assertNotNull;
  34 +
  35 +@Slf4j
  36 +public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends AbstractMqttTimeseriesIntegrationTest {
  37 +
  38 + private static final String POST_DATA_TELEMETRY_TOPIC = "proto/telemetry";
  39 +
  40 + @Before
  41 + public void beforeTest() throws Exception {
  42 + processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, POST_DATA_TELEMETRY_TOPIC, null);
  43 + }
  44 +
  45 + @After
  46 + public void afterTest() throws Exception {
  47 + processAfterTest();
  48 + }
  49 +
  50 + @Test
  51 + public void testPushMqttTelemetry() throws Exception {
  52 + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");
  53 + TransportProtos.TsKvListProto tsKvListProto = getTsKvListProto(expectedKeys, 0);
  54 + processTelemetryTest(POST_DATA_TELEMETRY_TOPIC, expectedKeys, tsKvListProto.toByteArray(), false);
  55 + }
  56 +
  57 + @Test
  58 + public void testPushMqttTelemetryWithTs() throws Exception {
  59 + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");
  60 + TransportProtos.TsKvListProto tsKvListProto = getTsKvListProto(expectedKeys, 10000);
  61 + processTelemetryTest(POST_DATA_TELEMETRY_TOPIC, expectedKeys, tsKvListProto.toByteArray(), true);
  62 + }
  63 +
  64 + @Test
  65 + public void testPushMqttTelemetryGateway() throws Exception {
  66 + TransportApiProtos.GatewayTelemetryMsg.Builder gatewayTelemetryMsgProtoBuilder = TransportApiProtos.GatewayTelemetryMsg.newBuilder();
  67 + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");
  68 + String deviceName1 = "Device A";
  69 + String deviceName2 = "Device B";
  70 + TransportApiProtos.TelemetryMsg deviceATelemetryMsgProto = getDeviceTelemetryMsgProto(deviceName1, expectedKeys, 10000, 20000);
  71 + TransportApiProtos.TelemetryMsg deviceBTelemetryMsgProto = getDeviceTelemetryMsgProto(deviceName2, expectedKeys, 10000, 20000);
  72 + gatewayTelemetryMsgProtoBuilder.addAllMsg(Arrays.asList(deviceATelemetryMsgProto, deviceBTelemetryMsgProto));
  73 + TransportApiProtos.GatewayTelemetryMsg gatewayTelemetryMsg = gatewayTelemetryMsgProtoBuilder.build();
  74 + processGatewayTelemetryTest(MqttTopics.GATEWAY_TELEMETRY_TOPIC, expectedKeys, gatewayTelemetryMsg.toByteArray(), deviceName1, deviceName2);
  75 + }
  76 +
  77 + @Test
  78 + public void testGatewayConnect() throws Exception {
  79 + String deviceName = "Device A";
  80 + TransportApiProtos.ConnectMsg connectMsgProto = getConnectProto(deviceName);
  81 + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
  82 + publishMqttMsg(client, connectMsgProto.toByteArray(), MqttTopics.GATEWAY_CONNECT_TOPIC);
  83 +
  84 + Thread.sleep(2000);
  85 +
  86 + Device device = doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class);
  87 + assertNotNull(device);
  88 + }
  89 +
  90 + private TransportApiProtos.ConnectMsg getConnectProto(String deviceName) {
  91 + TransportApiProtos.ConnectMsg.Builder builder = TransportApiProtos.ConnectMsg.newBuilder();
  92 + builder.setDeviceName(deviceName);
  93 + builder.setDeviceType(TransportPayloadType.PROTOBUF.name());
  94 + return builder.build();
  95 + }
  96 +
  97 + private TransportApiProtos.TelemetryMsg getDeviceTelemetryMsgProto(String deviceName, List<String> expectedKeys, long firstTs, long secondTs) {
  98 + TransportApiProtos.TelemetryMsg.Builder deviceTelemetryMsgBuilder = TransportApiProtos.TelemetryMsg.newBuilder();
  99 + TransportProtos.TsKvListProto tsKvListProto1 = getTsKvListProto(expectedKeys, firstTs);
  100 + TransportProtos.TsKvListProto tsKvListProto2 = getTsKvListProto(expectedKeys, secondTs);
  101 + TransportProtos.PostTelemetryMsg.Builder msg = TransportProtos.PostTelemetryMsg.newBuilder();
  102 + msg.addAllTsKvList(Arrays.asList(tsKvListProto1, tsKvListProto2));
  103 + deviceTelemetryMsgBuilder.setDeviceName(deviceName);
  104 + deviceTelemetryMsgBuilder.setMsg(msg);
  105 + return deviceTelemetryMsgBuilder.build();
  106 + }
  107 +
  108 + private TransportProtos.TsKvListProto getTsKvListProto(List<String> expectedKeys, long ts) {
  109 + List<TransportProtos.KeyValueProto> kvProtos = getKvProtos(expectedKeys);
  110 + TransportProtos.TsKvListProto.Builder builder = TransportProtos.TsKvListProto.newBuilder();
  111 + builder.addAllKv(kvProtos);
  112 + builder.setTs(ts);
  113 + return builder.build();
  114 + }
  115 +}
application/src/test/java/org/thingsboard/server/mqtt/telemetry/timeseries/nosql/MqttTimeseriesNoSqlIntegrationTest.java renamed from application/src/test/java/org/thingsboard/server/mqtt/telemetry/nosql/MqttTelemetryNoSqlIntegrationTest.java
@@ -13,14 +13,14 @@ @@ -13,14 +13,14 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.mqtt.telemetry.nosql; 16 +package org.thingsboard.server.mqtt.telemetry.timeseries.nosql;
17 17
18 import org.thingsboard.server.dao.service.DaoNoSqlTest; 18 import org.thingsboard.server.dao.service.DaoNoSqlTest;
19 -import org.thingsboard.server.mqtt.telemetry.AbstractMqttTelemetryIntegrationTest; 19 +import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesIntegrationTest;
20 20
21 /** 21 /**
22 * Created by Valerii Sosliuk on 8/22/2017. 22 * Created by Valerii Sosliuk on 8/22/2017.
23 */ 23 */
24 @DaoNoSqlTest 24 @DaoNoSqlTest
25 -public class MqttTelemetryNoSqlIntegrationTest extends AbstractMqttTelemetryIntegrationTest { 25 +public class MqttTimeseriesNoSqlIntegrationTest extends AbstractMqttTimeseriesIntegrationTest {
26 } 26 }
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.telemetry.timeseries.nosql;
  17 +
  18 +import org.thingsboard.server.dao.service.DaoNoSqlTest;
  19 +import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesJsonIntegrationTest;
  20 +
  21 +@DaoNoSqlTest
  22 +public class MqttTimeseriesNoSqlJsonIntegrationTest extends AbstractMqttTimeseriesJsonIntegrationTest {
  23 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.telemetry.timeseries.nosql;
  17 +
  18 +import org.thingsboard.server.dao.service.DaoNoSqlTest;
  19 +import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesProtoIntegrationTest;
  20 +
  21 +@DaoNoSqlTest
  22 +public class MqttTimeseriesNoSqlProtoIntegrationTest extends AbstractMqttTimeseriesProtoIntegrationTest {
  23 +}
application/src/test/java/org/thingsboard/server/mqtt/telemetry/timeseries/sql/MqttTimeseriesSqlIntegrationTest.java renamed from application/src/test/java/org/thingsboard/server/mqtt/telemetry/sql/MqttTelemetrySqlIntegrationTest.java
@@ -13,15 +13,14 @@ @@ -13,15 +13,14 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.mqtt.telemetry.sql; 16 +package org.thingsboard.server.mqtt.telemetry.timeseries.sql;
17 17
18 -import org.thingsboard.server.dao.service.DaoNoSqlTest;  
19 import org.thingsboard.server.dao.service.DaoSqlTest; 18 import org.thingsboard.server.dao.service.DaoSqlTest;
20 -import org.thingsboard.server.mqtt.telemetry.AbstractMqttTelemetryIntegrationTest; 19 +import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesIntegrationTest;
21 20
22 /** 21 /**
23 * Created by Valerii Sosliuk on 8/22/2017. 22 * Created by Valerii Sosliuk on 8/22/2017.
24 */ 23 */
25 @DaoSqlTest 24 @DaoSqlTest
26 -public class MqttTelemetrySqlIntegrationTest extends AbstractMqttTelemetryIntegrationTest { 25 +public class MqttTimeseriesSqlIntegrationTest extends AbstractMqttTimeseriesIntegrationTest {
27 } 26 }
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.telemetry.timeseries.sql;
  17 +
  18 +import org.thingsboard.server.dao.service.DaoSqlTest;
  19 +import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesIntegrationTest;
  20 +import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesJsonIntegrationTest;
  21 +
  22 +/**
  23 + * Created by Valerii Sosliuk on 8/22/2017.
  24 + */
  25 +@DaoSqlTest
  26 +public class MqttTimeseriesSqlJsonIntegrationTest extends AbstractMqttTimeseriesJsonIntegrationTest {
  27 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.telemetry.timeseries.sql;
  17 +
  18 +import org.thingsboard.server.dao.service.DaoSqlTest;
  19 +import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesJsonIntegrationTest;
  20 +import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesProtoIntegrationTest;
  21 +
  22 +/**
  23 + * Created by Valerii Sosliuk on 8/22/2017.
  24 + */
  25 +@DaoSqlTest
  26 +public class MqttTimeseriesSqlProtoIntegrationTest extends AbstractMqttTimeseriesProtoIntegrationTest {
  27 +}
@@ -16,7 +16,6 @@ @@ -16,7 +16,6 @@
16 package org.thingsboard.server.rules.flow.sql; 16 package org.thingsboard.server.rules.flow.sql;
17 17
18 import org.thingsboard.server.dao.service.DaoSqlTest; 18 import org.thingsboard.server.dao.service.DaoSqlTest;
19 -import org.thingsboard.server.mqtt.rpc.AbstractMqttServerSideRpcIntegrationTest;  
20 import org.thingsboard.server.rules.flow.AbstractRuleEngineFlowIntegrationTest; 19 import org.thingsboard.server.rules.flow.AbstractRuleEngineFlowIntegrationTest;
21 20
22 /** 21 /**
@@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
16 package org.thingsboard.server.dao.rule; 16 package org.thingsboard.server.dao.rule;
17 17
18 import com.google.common.util.concurrent.ListenableFuture; 18 import com.google.common.util.concurrent.ListenableFuture;
  19 +import org.thingsboard.server.common.data.exception.ThingsboardException;
19 import org.thingsboard.server.common.data.id.RuleChainId; 20 import org.thingsboard.server.common.data.id.RuleChainId;
20 import org.thingsboard.server.common.data.id.RuleNodeId; 21 import org.thingsboard.server.common.data.id.RuleNodeId;
21 import org.thingsboard.server.common.data.id.TenantId; 22 import org.thingsboard.server.common.data.id.TenantId;
@@ -23,6 +24,8 @@ import org.thingsboard.server.common.data.page.PageData; @@ -23,6 +24,8 @@ import org.thingsboard.server.common.data.page.PageData;
23 import org.thingsboard.server.common.data.page.PageLink; 24 import org.thingsboard.server.common.data.page.PageLink;
24 import org.thingsboard.server.common.data.relation.EntityRelation; 25 import org.thingsboard.server.common.data.relation.EntityRelation;
25 import org.thingsboard.server.common.data.rule.RuleChain; 26 import org.thingsboard.server.common.data.rule.RuleChain;
  27 +import org.thingsboard.server.common.data.rule.RuleChainData;
  28 +import org.thingsboard.server.common.data.rule.RuleChainImportResult;
26 import org.thingsboard.server.common.data.rule.RuleChainMetaData; 29 import org.thingsboard.server.common.data.rule.RuleChainMetaData;
27 import org.thingsboard.server.common.data.rule.RuleNode; 30 import org.thingsboard.server.common.data.rule.RuleNode;
28 31
@@ -63,4 +66,8 @@ public interface RuleChainService { @@ -63,4 +66,8 @@ public interface RuleChainService {
63 66
64 void deleteRuleChainsByTenantId(TenantId tenantId); 67 void deleteRuleChainsByTenantId(TenantId tenantId);
65 68
  69 + RuleChainData exportTenantRuleChains(TenantId tenantId, PageLink pageLink) throws ThingsboardException;
  70 +
  71 + List<RuleChainImportResult> importTenantRuleChains(TenantId tenantId, RuleChainData ruleChainData, boolean overwrite);
  72 +
66 } 73 }
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data;
  17 +
  18 +public enum TransportPayloadType {
  19 + JSON,
  20 + PROTOBUF
  21 +}
@@ -16,11 +16,14 @@ @@ -16,11 +16,14 @@
16 package org.thingsboard.server.common.data.device.profile; 16 package org.thingsboard.server.common.data.device.profile;
17 17
18 import lombok.Data; 18 import lombok.Data;
  19 +import org.thingsboard.server.common.data.TransportPayloadType;
19 import org.thingsboard.server.common.data.DeviceTransportType; 20 import org.thingsboard.server.common.data.DeviceTransportType;
20 21
21 @Data 22 @Data
22 public class MqttDeviceProfileTransportConfiguration implements DeviceProfileTransportConfiguration { 23 public class MqttDeviceProfileTransportConfiguration implements DeviceProfileTransportConfiguration {
23 24
  25 + private TransportPayloadType transportPayloadType = TransportPayloadType.JSON;
  26 +
24 private String deviceTelemetryTopic = MqttTopics.DEVICE_TELEMETRY_TOPIC; 27 private String deviceTelemetryTopic = MqttTopics.DEVICE_TELEMETRY_TOPIC;
25 private String deviceAttributesTopic = MqttTopics.DEVICE_ATTRIBUTES_TOPIC; 28 private String deviceAttributesTopic = MqttTopics.DEVICE_ATTRIBUTES_TOPIC;
26 29
@@ -20,28 +20,48 @@ package org.thingsboard.server.common.data.device.profile; @@ -20,28 +20,48 @@ package org.thingsboard.server.common.data.device.profile;
20 */ 20 */
21 public class MqttTopics { 21 public class MqttTopics {
22 22
  23 + private static final String RPC = "/rpc";
  24 + private static final String CONNECT = "/connect";
  25 + private static final String DISCONNECT = "/disconnect";
  26 + private static final String TELEMETRY = "/telemetry";
  27 + private static final String ATTRIBUTES = "/attributes";
  28 + private static final String CLAIM = "/claim";
  29 + private static final String SUB_TOPIC = "+";
  30 + private static final String ATTRIBUTES_RESPONSE = "/attributes/response";
  31 + private static final String ATTRIBUTES_REQUEST = "/attributes/request";
  32 +
  33 + private static final String DEVICE_RPC_RESPONSE = "/rpc/response/";
  34 + private static final String DEVICE_RPC_REQUEST = "/rpc/request/";
  35 +
  36 + private static final String DEVICE_ATTRIBUTES_RESPONSE = ATTRIBUTES_RESPONSE + "/";
  37 + private static final String DEVICE_ATTRIBUTES_REQUEST = ATTRIBUTES_REQUEST + "/";
  38 +
  39 + // V1_JSON topics
  40 +
23 public static final String BASE_DEVICE_API_TOPIC = "v1/devices/me"; 41 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 42
35 - public static final String BASE_GATEWAY_API_TOPIC = "v1/gateway";  
36 - public static final String GATEWAY_CONNECT_TOPIC = BASE_GATEWAY_API_TOPIC + "/connect";  
37 - public static final String GATEWAY_DISCONNECT_TOPIC = BASE_GATEWAY_API_TOPIC + "/disconnect";  
38 - public static final String GATEWAY_ATTRIBUTES_TOPIC = BASE_GATEWAY_API_TOPIC + "/attributes";  
39 - public static final String GATEWAY_TELEMETRY_TOPIC = BASE_GATEWAY_API_TOPIC + "/telemetry";  
40 - public static final String GATEWAY_CLAIM_TOPIC = BASE_GATEWAY_API_TOPIC + "/claim";  
41 - public static final String GATEWAY_RPC_TOPIC = BASE_GATEWAY_API_TOPIC + "/rpc";  
42 - public static final String GATEWAY_ATTRIBUTES_REQUEST_TOPIC = BASE_GATEWAY_API_TOPIC + "/attributes/request";  
43 - public static final String GATEWAY_ATTRIBUTES_RESPONSE_TOPIC = BASE_GATEWAY_API_TOPIC + "/attributes/response"; 43 + public static final String DEVICE_RPC_RESPONSE_TOPIC = BASE_DEVICE_API_TOPIC + DEVICE_RPC_RESPONSE;
  44 + public static final String DEVICE_RPC_RESPONSE_SUB_TOPIC = DEVICE_RPC_RESPONSE_TOPIC + SUB_TOPIC;
  45 + public static final String DEVICE_RPC_REQUESTS_TOPIC = BASE_DEVICE_API_TOPIC + DEVICE_RPC_REQUEST;
  46 + public static final String DEVICE_RPC_REQUESTS_SUB_TOPIC = DEVICE_RPC_REQUESTS_TOPIC + SUB_TOPIC;
  47 + public static final String DEVICE_ATTRIBUTES_RESPONSE_TOPIC_PREFIX = BASE_DEVICE_API_TOPIC + DEVICE_ATTRIBUTES_RESPONSE;
  48 + public static final String DEVICE_ATTRIBUTES_RESPONSES_TOPIC = DEVICE_ATTRIBUTES_RESPONSE_TOPIC_PREFIX + SUB_TOPIC;
  49 + public static final String DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX = BASE_DEVICE_API_TOPIC + DEVICE_ATTRIBUTES_REQUEST;
  50 + public static final String DEVICE_TELEMETRY_TOPIC = BASE_DEVICE_API_TOPIC + TELEMETRY;
  51 + public static final String DEVICE_CLAIM_TOPIC = BASE_DEVICE_API_TOPIC + CLAIM;
  52 + public static final String DEVICE_ATTRIBUTES_TOPIC = BASE_DEVICE_API_TOPIC + ATTRIBUTES;
  53 +
  54 + // V1_JSON gateway topics
44 55
  56 + public static final String BASE_GATEWAY_API_TOPIC = "v1/gateway";
  57 + public static final String GATEWAY_CONNECT_TOPIC = BASE_GATEWAY_API_TOPIC + CONNECT;
  58 + public static final String GATEWAY_DISCONNECT_TOPIC = BASE_GATEWAY_API_TOPIC + DISCONNECT;
  59 + public static final String GATEWAY_ATTRIBUTES_TOPIC = BASE_GATEWAY_API_TOPIC + ATTRIBUTES;
  60 + public static final String GATEWAY_TELEMETRY_TOPIC = BASE_GATEWAY_API_TOPIC + TELEMETRY;
  61 + public static final String GATEWAY_CLAIM_TOPIC = BASE_GATEWAY_API_TOPIC + CLAIM;
  62 + public static final String GATEWAY_RPC_TOPIC = BASE_GATEWAY_API_TOPIC + RPC;
  63 + public static final String GATEWAY_ATTRIBUTES_REQUEST_TOPIC = BASE_GATEWAY_API_TOPIC + ATTRIBUTES_REQUEST;
  64 + public static final String GATEWAY_ATTRIBUTES_RESPONSE_TOPIC = BASE_GATEWAY_API_TOPIC + ATTRIBUTES_RESPONSE;
45 65
46 private MqttTopics() { 66 private MqttTopics() {
47 } 67 }
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.rule;
  17 +
  18 +import lombok.Data;
  19 +
  20 +import java.util.List;
  21 +
  22 +@Data
  23 +public class RuleChainData {
  24 +
  25 + List<RuleChain> ruleChains;
  26 + List<RuleChainMetaData> metadata;
  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.common.data.rule;
  17 +
  18 +import lombok.AllArgsConstructor;
  19 +import lombok.Data;
  20 +import org.thingsboard.server.common.data.id.RuleChainId;
  21 +import org.thingsboard.server.common.data.id.TenantId;
  22 +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
  23 +
  24 +@Data
  25 +@AllArgsConstructor
  26 +public class RuleChainImportResult {
  27 +
  28 + private TenantId tenantId;
  29 + private RuleChainId ruleChainId;
  30 + private ComponentLifecycleEvent lifecycleEvent;
  31 +}
@@ -24,7 +24,8 @@ import org.springframework.beans.factory.annotation.Value; @@ -24,7 +24,8 @@ import org.springframework.beans.factory.annotation.Value;
24 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; 24 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
25 import org.springframework.stereotype.Component; 25 import org.springframework.stereotype.Component;
26 import org.thingsboard.server.common.transport.TransportContext; 26 import org.thingsboard.server.common.transport.TransportContext;
27 -import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor; 27 +import org.thingsboard.server.transport.mqtt.adaptors.JsonMqttAdaptor;
  28 +import org.thingsboard.server.transport.mqtt.adaptors.ProtoMqttAdaptor;
28 29
29 /** 30 /**
30 * Created by ashvayka on 04.10.18. 31 * Created by ashvayka on 04.10.18.
@@ -40,7 +41,11 @@ public class MqttTransportContext extends TransportContext { @@ -40,7 +41,11 @@ public class MqttTransportContext extends TransportContext {
40 41
41 @Getter 42 @Getter
42 @Autowired 43 @Autowired
43 - private MqttTransportAdaptor adaptor; 44 + private JsonMqttAdaptor jsonMqttAdaptor;
  45 +
  46 + @Getter
  47 + @Autowired
  48 + private ProtoMqttAdaptor protoMqttAdaptor;
44 49
45 @Getter 50 @Getter
46 @Value("${transport.mqtt.netty.max_payload_size}") 51 @Value("${transport.mqtt.netty.max_payload_size}")
@@ -38,7 +38,6 @@ import io.netty.util.ReferenceCountUtil; @@ -38,7 +38,6 @@ import io.netty.util.ReferenceCountUtil;
38 import io.netty.util.concurrent.Future; 38 import io.netty.util.concurrent.Future;
39 import io.netty.util.concurrent.GenericFutureListener; 39 import io.netty.util.concurrent.GenericFutureListener;
40 import lombok.extern.slf4j.Slf4j; 40 import lombok.extern.slf4j.Slf4j;
41 -import org.springframework.util.StringUtils;  
42 import org.thingsboard.server.common.data.DeviceProfile; 41 import org.thingsboard.server.common.data.DeviceProfile;
43 import org.thingsboard.server.common.data.DeviceTransportType; 42 import org.thingsboard.server.common.data.DeviceTransportType;
44 import org.thingsboard.server.common.data.device.profile.MqttTopics; 43 import org.thingsboard.server.common.data.device.profile.MqttTopics;
@@ -53,8 +52,6 @@ import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsRes @@ -53,8 +52,6 @@ import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsRes
53 import org.thingsboard.server.common.transport.service.DefaultTransportService; 52 import org.thingsboard.server.common.transport.service.DefaultTransportService;
54 import org.thingsboard.server.gen.transport.TransportProtos; 53 import org.thingsboard.server.gen.transport.TransportProtos;
55 import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent; 54 import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent;
56 -import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;  
57 -import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg;  
58 import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg; 55 import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg;
59 import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor; 56 import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor;
60 import org.thingsboard.server.transport.mqtt.session.DeviceSessionCtx; 57 import org.thingsboard.server.transport.mqtt.session.DeviceSessionCtx;
@@ -66,7 +63,6 @@ import javax.net.ssl.SSLPeerUnverifiedException; @@ -66,7 +63,6 @@ import javax.net.ssl.SSLPeerUnverifiedException;
66 import javax.security.cert.X509Certificate; 63 import javax.security.cert.X509Certificate;
67 import java.io.IOException; 64 import java.io.IOException;
68 import java.net.InetSocketAddress; 65 import java.net.InetSocketAddress;
69 -import java.nio.charset.StandardCharsets;  
70 import java.util.ArrayList; 66 import java.util.ArrayList;
71 import java.util.List; 67 import java.util.List;
72 import java.util.UUID; 68 import java.util.UUID;
@@ -74,7 +70,6 @@ import java.util.concurrent.ConcurrentHashMap; @@ -74,7 +70,6 @@ import java.util.concurrent.ConcurrentHashMap;
74 import java.util.concurrent.ConcurrentMap; 70 import java.util.concurrent.ConcurrentMap;
75 71
76 import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_ACCEPTED; 72 import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_ACCEPTED;
77 -import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD;  
78 import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_REFUSED_NOT_AUTHORIZED; 73 import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_REFUSED_NOT_AUTHORIZED;
79 import static io.netty.handler.codec.mqtt.MqttMessageType.CONNACK; 74 import static io.netty.handler.codec.mqtt.MqttMessageType.CONNACK;
80 import static io.netty.handler.codec.mqtt.MqttMessageType.PINGRESP; 75 import static io.netty.handler.codec.mqtt.MqttMessageType.PINGRESP;
@@ -95,7 +90,6 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -95,7 +90,6 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
95 90
96 private final UUID sessionId; 91 private final UUID sessionId;
97 private final MqttTransportContext context; 92 private final MqttTransportContext context;
98 - private final MqttTransportAdaptor adaptor;  
99 private final TransportService transportService; 93 private final TransportService transportService;
100 private final SslHandler sslHandler; 94 private final SslHandler sslHandler;
101 private final ConcurrentMap<MqttTopicMatcher, Integer> mqttQoSMap; 95 private final ConcurrentMap<MqttTopicMatcher, Integer> mqttQoSMap;
@@ -108,10 +102,9 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -108,10 +102,9 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
108 this.sessionId = UUID.randomUUID(); 102 this.sessionId = UUID.randomUUID();
109 this.context = context; 103 this.context = context;
110 this.transportService = context.getTransportService(); 104 this.transportService = context.getTransportService();
111 - this.adaptor = context.getAdaptor();  
112 this.sslHandler = sslHandler; 105 this.sslHandler = sslHandler;
113 this.mqttQoSMap = new ConcurrentHashMap<>(); 106 this.mqttQoSMap = new ConcurrentHashMap<>();
114 - this.deviceSessionCtx = new DeviceSessionCtx(sessionId, mqttQoSMap); 107 + this.deviceSessionCtx = new DeviceSessionCtx(sessionId, mqttQoSMap, context);
115 } 108 }
116 109
117 @Override 110 @Override
@@ -215,23 +208,24 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -215,23 +208,24 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
215 208
216 private void processDevicePublish(ChannelHandlerContext ctx, MqttPublishMessage mqttMsg, String topicName, int msgId) { 209 private void processDevicePublish(ChannelHandlerContext ctx, MqttPublishMessage mqttMsg, String topicName, int msgId) {
217 try { 210 try {
  211 + MqttTransportAdaptor payloadAdaptor = deviceSessionCtx.getPayloadAdaptor();
218 if (deviceSessionCtx.isDeviceTelemetryTopic(topicName)) { 212 if (deviceSessionCtx.isDeviceTelemetryTopic(topicName)) {
219 - TransportProtos.PostTelemetryMsg postTelemetryMsg = adaptor.convertToPostTelemetry(deviceSessionCtx, mqttMsg); 213 + TransportProtos.PostTelemetryMsg postTelemetryMsg = payloadAdaptor.convertToPostTelemetry(deviceSessionCtx, mqttMsg);
220 transportService.process(deviceSessionCtx.getSessionInfo(), postTelemetryMsg, getPubAckCallback(ctx, msgId, postTelemetryMsg)); 214 transportService.process(deviceSessionCtx.getSessionInfo(), postTelemetryMsg, getPubAckCallback(ctx, msgId, postTelemetryMsg));
221 } else if (deviceSessionCtx.isDeviceAttributesTopic(topicName)) { 215 } else if (deviceSessionCtx.isDeviceAttributesTopic(topicName)) {
222 - TransportProtos.PostAttributeMsg postAttributeMsg = adaptor.convertToPostAttributes(deviceSessionCtx, mqttMsg); 216 + TransportProtos.PostAttributeMsg postAttributeMsg = payloadAdaptor.convertToPostAttributes(deviceSessionCtx, mqttMsg);
223 transportService.process(deviceSessionCtx.getSessionInfo(), postAttributeMsg, getPubAckCallback(ctx, msgId, postAttributeMsg)); 217 transportService.process(deviceSessionCtx.getSessionInfo(), postAttributeMsg, getPubAckCallback(ctx, msgId, postAttributeMsg));
224 } else if (topicName.startsWith(MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX)) { 218 } else if (topicName.startsWith(MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX)) {
225 - TransportProtos.GetAttributeRequestMsg getAttributeMsg = adaptor.convertToGetAttributes(deviceSessionCtx, mqttMsg); 219 + TransportProtos.GetAttributeRequestMsg getAttributeMsg = payloadAdaptor.convertToGetAttributes(deviceSessionCtx, mqttMsg);
226 transportService.process(deviceSessionCtx.getSessionInfo(), getAttributeMsg, getPubAckCallback(ctx, msgId, getAttributeMsg)); 220 transportService.process(deviceSessionCtx.getSessionInfo(), getAttributeMsg, getPubAckCallback(ctx, msgId, getAttributeMsg));
227 } else if (topicName.startsWith(MqttTopics.DEVICE_RPC_RESPONSE_TOPIC)) { 221 } else if (topicName.startsWith(MqttTopics.DEVICE_RPC_RESPONSE_TOPIC)) {
228 - TransportProtos.ToDeviceRpcResponseMsg rpcResponseMsg = adaptor.convertToDeviceRpcResponse(deviceSessionCtx, mqttMsg); 222 + TransportProtos.ToDeviceRpcResponseMsg rpcResponseMsg = payloadAdaptor.convertToDeviceRpcResponse(deviceSessionCtx, mqttMsg);
229 transportService.process(deviceSessionCtx.getSessionInfo(), rpcResponseMsg, getPubAckCallback(ctx, msgId, rpcResponseMsg)); 223 transportService.process(deviceSessionCtx.getSessionInfo(), rpcResponseMsg, getPubAckCallback(ctx, msgId, rpcResponseMsg));
230 } else if (topicName.startsWith(MqttTopics.DEVICE_RPC_REQUESTS_TOPIC)) { 224 } else if (topicName.startsWith(MqttTopics.DEVICE_RPC_REQUESTS_TOPIC)) {
231 - TransportProtos.ToServerRpcRequestMsg rpcRequestMsg = adaptor.convertToServerRpcRequest(deviceSessionCtx, mqttMsg); 225 + TransportProtos.ToServerRpcRequestMsg rpcRequestMsg = payloadAdaptor.convertToServerRpcRequest(deviceSessionCtx, mqttMsg);
232 transportService.process(deviceSessionCtx.getSessionInfo(), rpcRequestMsg, getPubAckCallback(ctx, msgId, rpcRequestMsg)); 226 transportService.process(deviceSessionCtx.getSessionInfo(), rpcRequestMsg, getPubAckCallback(ctx, msgId, rpcRequestMsg));
233 } else if (topicName.equals(MqttTopics.DEVICE_CLAIM_TOPIC)) { 227 } else if (topicName.equals(MqttTopics.DEVICE_CLAIM_TOPIC)) {
234 - TransportProtos.ClaimDeviceMsg claimDeviceMsg = adaptor.convertToClaimDevice(deviceSessionCtx, mqttMsg); 228 + TransportProtos.ClaimDeviceMsg claimDeviceMsg = payloadAdaptor.convertToClaimDevice(deviceSessionCtx, mqttMsg);
235 transportService.process(deviceSessionCtx.getSessionInfo(), claimDeviceMsg, getPubAckCallback(ctx, msgId, claimDeviceMsg)); 229 transportService.process(deviceSessionCtx.getSessionInfo(), claimDeviceMsg, getPubAckCallback(ctx, msgId, claimDeviceMsg));
236 } else { 230 } else {
237 transportService.reportActivity(deviceSessionCtx.getSessionInfo()); 231 transportService.reportActivity(deviceSessionCtx.getSessionInfo());
@@ -244,7 +238,6 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -244,7 +238,6 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
244 } 238 }
245 239
246 240
247 -  
248 private <T> TransportServiceCallback<Void> getPubAckCallback(final ChannelHandlerContext ctx, final int msgId, final T msg) { 241 private <T> TransportServiceCallback<Void> getPubAckCallback(final ChannelHandlerContext ctx, final int msgId, final T msg) {
249 return new TransportServiceCallback<Void>() { 242 return new TransportServiceCallback<Void>() {
250 @Override 243 @Override
@@ -288,10 +281,10 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -288,10 +281,10 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
288 break; 281 break;
289 } 282 }
290 case MqttTopics.DEVICE_RPC_RESPONSE_SUB_TOPIC: 283 case MqttTopics.DEVICE_RPC_RESPONSE_SUB_TOPIC:
  284 + case MqttTopics.DEVICE_ATTRIBUTES_RESPONSES_TOPIC:
291 case MqttTopics.GATEWAY_ATTRIBUTES_TOPIC: 285 case MqttTopics.GATEWAY_ATTRIBUTES_TOPIC:
292 case MqttTopics.GATEWAY_RPC_TOPIC: 286 case MqttTopics.GATEWAY_RPC_TOPIC:
293 case MqttTopics.GATEWAY_ATTRIBUTES_RESPONSE_TOPIC: 287 case MqttTopics.GATEWAY_ATTRIBUTES_RESPONSE_TOPIC:
294 - case MqttTopics.DEVICE_ATTRIBUTES_RESPONSES_TOPIC:  
295 registerSubQoS(topic, grantedQoSList, reqQoS); 288 registerSubQoS(topic, grantedQoSList, reqQoS);
296 break; 289 break;
297 default: 290 default:
@@ -490,7 +483,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -490,7 +483,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
490 if (infoNode != null) { 483 if (infoNode != null) {
491 JsonNode gatewayNode = infoNode.get("gateway"); 484 JsonNode gatewayNode = infoNode.get("gateway");
492 if (gatewayNode != null && gatewayNode.asBoolean()) { 485 if (gatewayNode != null && gatewayNode.asBoolean()) {
493 - gatewaySessionHandler = new GatewaySessionHandler(context, deviceSessionCtx, sessionId); 486 + gatewaySessionHandler = new GatewaySessionHandler(deviceSessionCtx, sessionId);
494 } 487 }
495 } 488 }
496 } catch (IOException e) { 489 } catch (IOException e) {
@@ -544,7 +537,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -544,7 +537,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
544 @Override 537 @Override
545 public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg response) { 538 public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg response) {
546 try { 539 try {
547 - adaptor.convertToPublish(deviceSessionCtx, response).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush); 540 + deviceSessionCtx.getPayloadAdaptor().convertToPublish(deviceSessionCtx, response).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush);
548 } catch (Exception e) { 541 } catch (Exception e) {
549 log.trace("[{}] Failed to convert device attributes response to MQTT msg", sessionId, e); 542 log.trace("[{}] Failed to convert device attributes response to MQTT msg", sessionId, e);
550 } 543 }
@@ -553,7 +546,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -553,7 +546,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
553 @Override 546 @Override
554 public void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg notification) { 547 public void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg notification) {
555 try { 548 try {
556 - adaptor.convertToPublish(deviceSessionCtx, notification).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush); 549 + deviceSessionCtx.getPayloadAdaptor().convertToPublish(deviceSessionCtx, notification).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush);
557 } catch (Exception e) { 550 } catch (Exception e) {
558 log.trace("[{}] Failed to convert device attributes update to MQTT msg", sessionId, e); 551 log.trace("[{}] Failed to convert device attributes update to MQTT msg", sessionId, e);
559 } 552 }
@@ -569,17 +562,17 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -569,17 +562,17 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
569 public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg rpcRequest) { 562 public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg rpcRequest) {
570 log.trace("[{}] Received RPC command to device", sessionId); 563 log.trace("[{}] Received RPC command to device", sessionId);
571 try { 564 try {
572 - adaptor.convertToPublish(deviceSessionCtx, rpcRequest).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush); 565 + deviceSessionCtx.getPayloadAdaptor().convertToPublish(deviceSessionCtx, rpcRequest).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush);
573 } catch (Exception e) { 566 } catch (Exception e) {
574 - log.trace("[{}] Failed to convert device RPC commandto MQTT msg", sessionId, e); 567 + log.trace("[{}] Failed to convert device RPC command to MQTT msg", sessionId, e);
575 } 568 }
576 } 569 }
577 570
578 @Override 571 @Override
579 public void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg rpcResponse) { 572 public void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg rpcResponse) {
580 - log.trace("[{}] Received RPC command to device", sessionId); 573 + log.trace("[{}] Received RPC command to server", sessionId);
581 try { 574 try {
582 - adaptor.convertToPublish(deviceSessionCtx, rpcResponse).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush); 575 + deviceSessionCtx.getPayloadAdaptor().convertToPublish(deviceSessionCtx, rpcResponse).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush);
583 } catch (Exception e) { 576 } catch (Exception e) {
584 log.trace("[{}] Failed to convert device RPC command to MQTT msg", sessionId, e); 577 log.trace("[{}] Failed to convert device RPC command to MQTT msg", sessionId, e);
585 } 578 }
@@ -38,6 +38,7 @@ import org.thingsboard.server.common.data.device.profile.MqttTopics; @@ -38,6 +38,7 @@ import org.thingsboard.server.common.data.device.profile.MqttTopics;
38 import org.thingsboard.server.transport.mqtt.session.MqttDeviceAwareSessionContext; 38 import org.thingsboard.server.transport.mqtt.session.MqttDeviceAwareSessionContext;
39 39
40 import java.nio.charset.Charset; 40 import java.nio.charset.Charset;
  41 +import java.nio.charset.StandardCharsets;
41 import java.util.Arrays; 42 import java.util.Arrays;
42 import java.util.HashSet; 43 import java.util.HashSet;
43 import java.util.Optional; 44 import java.util.Optional;
@@ -47,12 +48,13 @@ import java.util.UUID; @@ -47,12 +48,13 @@ import java.util.UUID;
47 /** 48 /**
48 * @author Andrew Shvayka 49 * @author Andrew Shvayka
49 */ 50 */
50 -@Component("JsonMqttAdaptor") 51 +@Component
51 @Slf4j 52 @Slf4j
52 public class JsonMqttAdaptor implements MqttTransportAdaptor { 53 public class JsonMqttAdaptor implements MqttTransportAdaptor {
53 54
  55 + protected static final Charset UTF8 = StandardCharsets.UTF_8;
  56 +
54 private static final Gson GSON = new Gson(); 57 private static final Gson GSON = new Gson();
55 - private static final Charset UTF8 = Charset.forName("UTF-8");  
56 private static final ByteBufAllocator ALLOCATOR = new UnpooledByteBufAllocator(false); 58 private static final ByteBufAllocator ALLOCATOR = new UnpooledByteBufAllocator(false);
57 59
58 @Override 60 @Override
@@ -76,11 +78,81 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { @@ -76,11 +78,81 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor {
76 } 78 }
77 79
78 @Override 80 @Override
  81 + public TransportProtos.ClaimDeviceMsg convertToClaimDevice(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException {
  82 + String payload = validatePayload(ctx.getSessionId(), inbound.payload(), true);
  83 + try {
  84 + return JsonConverter.convertToClaimDeviceProto(ctx.getDeviceId(), payload);
  85 + } catch (IllegalStateException | JsonSyntaxException ex) {
  86 + throw new AdaptorException(ex);
  87 + }
  88 + }
  89 +
  90 + @Override
79 public TransportProtos.GetAttributeRequestMsg convertToGetAttributes(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { 91 public TransportProtos.GetAttributeRequestMsg convertToGetAttributes(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException {
  92 + return processGetAttributeRequestMsg(inbound, MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX);
  93 + }
  94 +
  95 + @Override
  96 + public TransportProtos.ToDeviceRpcResponseMsg convertToDeviceRpcResponse(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException {
  97 + return processToDeviceRpcResponseMsg(inbound, MqttTopics.DEVICE_RPC_RESPONSE_TOPIC);
  98 + }
  99 +
  100 + @Override
  101 + public TransportProtos.ToServerRpcRequestMsg convertToServerRpcRequest(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException {
  102 + return processToServerRpcRequestMsg(ctx, inbound, MqttTopics.DEVICE_RPC_REQUESTS_TOPIC);
  103 + }
  104 +
  105 + @Override
  106 + public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException {
  107 + return processConvertFromAttributeResponseMsg(ctx, responseMsg, MqttTopics.DEVICE_ATTRIBUTES_RESPONSE_TOPIC_PREFIX);
  108 + }
  109 +
  110 + @Override
  111 + public Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException {
  112 + return processConvertFromGatewayAttributeResponseMsg(ctx, deviceName, responseMsg, MqttTopics.GATEWAY_ATTRIBUTES_RESPONSE_TOPIC);
  113 + }
  114 +
  115 + @Override
  116 + public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.AttributeUpdateNotificationMsg notificationMsg) {
  117 + return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_ATTRIBUTES_TOPIC, JsonConverter.toJson(notificationMsg)));
  118 + }
  119 +
  120 + @Override
  121 + public Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.AttributeUpdateNotificationMsg notificationMsg) {
  122 + JsonObject result = JsonConverter.getJsonObjectForGateway(deviceName, notificationMsg);
  123 + return Optional.of(createMqttPublishMsg(ctx, MqttTopics.GATEWAY_ATTRIBUTES_TOPIC, result));
  124 + }
  125 +
  126 + @Override
  127 + public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) {
  128 + return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_RPC_REQUESTS_TOPIC + rpcRequest.getRequestId(), JsonConverter.toJson(rpcRequest, false)));
  129 + }
  130 +
  131 + @Override
  132 + public Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) {
  133 + return Optional.of(createMqttPublishMsg(ctx, MqttTopics.GATEWAY_RPC_TOPIC, JsonConverter.toGatewayJson(deviceName, rpcRequest)));
  134 + }
  135 +
  136 + @Override
  137 + public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.ToServerRpcResponseMsg rpcResponse) {
  138 + return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_RPC_RESPONSE_TOPIC + rpcResponse.getRequestId(), JsonConverter.toJson(rpcResponse)));
  139 + }
  140 +
  141 + public static JsonElement validateJsonPayload(UUID sessionId, ByteBuf payloadData) throws AdaptorException {
  142 + String payload = validatePayload(sessionId, payloadData, false);
  143 + try {
  144 + return new JsonParser().parse(payload);
  145 + } catch (JsonSyntaxException ex) {
  146 + log.warn("Payload is in incorrect format: {}", payload);
  147 + throw new AdaptorException(ex);
  148 + }
  149 + }
  150 +
  151 + protected TransportProtos.GetAttributeRequestMsg processGetAttributeRequestMsg(MqttPublishMessage inbound, String topic) throws AdaptorException {
80 String topicName = inbound.variableHeader().topicName(); 152 String topicName = inbound.variableHeader().topicName();
81 try { 153 try {
82 TransportProtos.GetAttributeRequestMsg.Builder result = TransportProtos.GetAttributeRequestMsg.newBuilder(); 154 TransportProtos.GetAttributeRequestMsg.Builder result = TransportProtos.GetAttributeRequestMsg.newBuilder();
83 - result.setRequestId(Integer.valueOf(topicName.substring(MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX.length()))); 155 + result.setRequestId(getRequestId(topicName, topic));
84 String payload = inbound.payload().toString(UTF8); 156 String payload = inbound.payload().toString(UTF8);
85 JsonElement requestBody = new JsonParser().parse(payload); 157 JsonElement requestBody = new JsonParser().parse(payload);
86 Set<String> clientKeys = toStringSet(requestBody, "clientKeys"); 158 Set<String> clientKeys = toStringSet(requestBody, "clientKeys");
@@ -98,93 +170,53 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { @@ -98,93 +170,53 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor {
98 } 170 }
99 } 171 }
100 172
101 - @Override  
102 - public TransportProtos.ToDeviceRpcResponseMsg convertToDeviceRpcResponse(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { 173 + protected TransportProtos.ToDeviceRpcResponseMsg processToDeviceRpcResponseMsg(MqttPublishMessage inbound, String topic) throws AdaptorException {
103 String topicName = inbound.variableHeader().topicName(); 174 String topicName = inbound.variableHeader().topicName();
104 try { 175 try {
105 - Integer requestId = Integer.valueOf(topicName.substring(MqttTopics.DEVICE_RPC_RESPONSE_TOPIC.length())); 176 + int requestId = getRequestId(topicName, topic);
106 String payload = inbound.payload().toString(UTF8); 177 String payload = inbound.payload().toString(UTF8);
107 return TransportProtos.ToDeviceRpcResponseMsg.newBuilder().setRequestId(requestId).setPayload(payload).build(); 178 return TransportProtos.ToDeviceRpcResponseMsg.newBuilder().setRequestId(requestId).setPayload(payload).build();
108 } catch (RuntimeException e) { 179 } catch (RuntimeException e) {
109 - log.warn("Failed to decode get attributes request", e); 180 + log.warn("Failed to decode Rpc response", e);
110 throw new AdaptorException(e); 181 throw new AdaptorException(e);
111 } 182 }
112 } 183 }
113 184
114 - @Override  
115 - public TransportProtos.ToServerRpcRequestMsg convertToServerRpcRequest(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { 185 + protected TransportProtos.ToServerRpcRequestMsg processToServerRpcRequestMsg(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound, String topic) throws AdaptorException {
116 String topicName = inbound.variableHeader().topicName(); 186 String topicName = inbound.variableHeader().topicName();
117 String payload = validatePayload(ctx.getSessionId(), inbound.payload(), false); 187 String payload = validatePayload(ctx.getSessionId(), inbound.payload(), false);
118 try { 188 try {
119 - Integer requestId = Integer.valueOf(topicName.substring(MqttTopics.DEVICE_RPC_REQUESTS_TOPIC.length())); 189 + int requestId = getRequestId(topicName, topic);
120 return JsonConverter.convertToServerRpcRequest(new JsonParser().parse(payload), requestId); 190 return JsonConverter.convertToServerRpcRequest(new JsonParser().parse(payload), requestId);
121 } catch (IllegalStateException | JsonSyntaxException ex) { 191 } catch (IllegalStateException | JsonSyntaxException ex) {
122 throw new AdaptorException(ex); 192 throw new AdaptorException(ex);
123 } 193 }
124 } 194 }
125 195
126 - @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 - }  
134 - }  
135 -  
136 - @Override  
137 - public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException { 196 + protected Optional<MqttMessage> processConvertFromAttributeResponseMsg(MqttDeviceAwareSessionContext ctx, TransportProtos.GetAttributeResponseMsg responseMsg, String topic) throws AdaptorException {
138 if (!StringUtils.isEmpty(responseMsg.getError())) { 197 if (!StringUtils.isEmpty(responseMsg.getError())) {
139 throw new AdaptorException(responseMsg.getError()); 198 throw new AdaptorException(responseMsg.getError());
140 } else { 199 } else {
141 - Integer requestId = responseMsg.getRequestId(); 200 + int requestId = responseMsg.getRequestId();
142 if (requestId >= 0) { 201 if (requestId >= 0) {
143 return Optional.of(createMqttPublishMsg(ctx, 202 return Optional.of(createMqttPublishMsg(ctx,
144 - MqttTopics.DEVICE_ATTRIBUTES_RESPONSE_TOPIC_PREFIX + requestId, 203 + topic + requestId,
145 JsonConverter.toJson(responseMsg))); 204 JsonConverter.toJson(responseMsg)));
146 } 205 }
147 return Optional.empty(); 206 return Optional.empty();
148 } 207 }
149 } 208 }
150 209
151 - @Override  
152 - public Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException { 210 + protected Optional<MqttMessage> processConvertFromGatewayAttributeResponseMsg(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.GetAttributeResponseMsg responseMsg, String topic) throws AdaptorException {
153 if (!StringUtils.isEmpty(responseMsg.getError())) { 211 if (!StringUtils.isEmpty(responseMsg.getError())) {
154 throw new AdaptorException(responseMsg.getError()); 212 throw new AdaptorException(responseMsg.getError());
155 } else { 213 } else {
156 JsonObject result = JsonConverter.getJsonObjectForGateway(deviceName, responseMsg); 214 JsonObject result = JsonConverter.getJsonObjectForGateway(deviceName, responseMsg);
157 - return Optional.of(createMqttPublishMsg(ctx, MqttTopics.GATEWAY_ATTRIBUTES_RESPONSE_TOPIC, result)); 215 + return Optional.of(createMqttPublishMsg(ctx, topic, result));
158 } 216 }
159 } 217 }
160 218
161 - @Override  
162 - public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.AttributeUpdateNotificationMsg notificationMsg) throws AdaptorException {  
163 - return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_ATTRIBUTES_TOPIC, JsonConverter.toJson(notificationMsg)));  
164 - }  
165 -  
166 - @Override  
167 - public Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.AttributeUpdateNotificationMsg notificationMsg) throws AdaptorException {  
168 - JsonObject result = JsonConverter.getJsonObjectForGateway(deviceName, notificationMsg);  
169 - return Optional.of(createMqttPublishMsg(ctx, MqttTopics.GATEWAY_ATTRIBUTES_TOPIC, result));  
170 - }  
171 -  
172 - @Override  
173 - public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) throws AdaptorException {  
174 - return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_RPC_REQUESTS_TOPIC + rpcRequest.getRequestId(), JsonConverter.toJson(rpcRequest, false)));  
175 - }  
176 -  
177 - @Override  
178 - public Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) throws AdaptorException {  
179 - return Optional.of(createMqttPublishMsg(ctx, MqttTopics.GATEWAY_RPC_TOPIC, JsonConverter.toGatewayJson(deviceName, rpcRequest)));  
180 - }  
181 -  
182 - @Override  
183 - public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.ToServerRpcResponseMsg rpcResponse) {  
184 - return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_RPC_RESPONSE_TOPIC + rpcResponse.getRequestId(), JsonConverter.toJson(rpcResponse)));  
185 - }  
186 -  
187 - private MqttPublishMessage createMqttPublishMsg(MqttDeviceAwareSessionContext ctx, String topic, JsonElement json) { 219 + protected MqttPublishMessage createMqttPublishMsg(MqttDeviceAwareSessionContext ctx, String topic, JsonElement json) {
188 MqttFixedHeader mqttFixedHeader = 220 MqttFixedHeader mqttFixedHeader =
189 new MqttFixedHeader(MqttMessageType.PUBLISH, false, ctx.getQoSForTopic(topic), false, 0); 221 new MqttFixedHeader(MqttMessageType.PUBLISH, false, ctx.getQoSForTopic(topic), false, 0);
190 MqttPublishVariableHeader header = new MqttPublishVariableHeader(topic, ctx.nextMsgId()); 222 MqttPublishVariableHeader header = new MqttPublishVariableHeader(topic, ctx.nextMsgId());
@@ -202,16 +234,6 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { @@ -202,16 +234,6 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor {
202 } 234 }
203 } 235 }
204 236
205 - public static JsonElement validateJsonPayload(UUID sessionId, ByteBuf payloadData) throws AdaptorException {  
206 - String payload = validatePayload(sessionId, payloadData, false);  
207 - try {  
208 - return new JsonParser().parse(payload);  
209 - } catch (JsonSyntaxException ex) {  
210 - log.warn("Payload is in incorrect format: {}", payload);  
211 - throw new AdaptorException(ex);  
212 - }  
213 - }  
214 -  
215 private static String validatePayload(UUID sessionId, ByteBuf payloadData, boolean isEmptyPayloadAllowed) throws AdaptorException { 237 private static String validatePayload(UUID sessionId, ByteBuf payloadData, boolean isEmptyPayloadAllowed) throws AdaptorException {
216 String payload = payloadData.toString(UTF8); 238 String payload = payloadData.toString(UTF8);
217 if (payload == null) { 239 if (payload == null) {
@@ -223,4 +245,8 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { @@ -223,4 +245,8 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor {
223 return payload; 245 return payload;
224 } 246 }
225 247
  248 + private int getRequestId(String topicName, String topic) {
  249 + return Integer.parseInt(topicName.substring(topic.length()));
  250 + }
  251 +
226 } 252 }
  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.transport.mqtt.session.MqttDeviceAwareSessionContext;
  36 +
  37 +import java.util.Optional;
  38 +
  39 +@Component
  40 +@Slf4j
  41 +public class ProtoMqttAdaptor implements MqttTransportAdaptor {
  42 +
  43 + private static final ByteBufAllocator ALLOCATOR = new UnpooledByteBufAllocator(false);
  44 +
  45 + @Override
  46 + public TransportProtos.PostTelemetryMsg convertToPostTelemetry(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException {
  47 + byte[] bytes = toBytes(inbound.payload());
  48 + try {
  49 + return ProtoConverter.convertToTelemetryProto(bytes);
  50 + } catch (InvalidProtocolBufferException | IllegalArgumentException e) {
  51 + throw new AdaptorException(e);
  52 + }
  53 + }
  54 +
  55 + @Override
  56 + public TransportProtos.PostAttributeMsg convertToPostAttributes(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException {
  57 + byte[] bytes = toBytes(inbound.payload());
  58 + try {
  59 + return ProtoConverter.validatePostAttributeMsg(bytes);
  60 + } catch (InvalidProtocolBufferException | IllegalArgumentException e) {
  61 + throw new AdaptorException(e);
  62 + }
  63 + }
  64 +
  65 + @Override
  66 + public TransportProtos.ClaimDeviceMsg convertToClaimDevice(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException {
  67 + byte[] bytes = toBytes(inbound.payload());
  68 + try {
  69 + return ProtoConverter.convertToClaimDeviceProto(ctx.getDeviceId(), bytes);
  70 + } catch (InvalidProtocolBufferException e) {
  71 + throw new AdaptorException(e);
  72 + }
  73 + }
  74 +
  75 + @Override
  76 + public TransportProtos.GetAttributeRequestMsg convertToGetAttributes(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException {
  77 + byte[] bytes = toBytes(inbound.payload());
  78 + String topicName = inbound.variableHeader().topicName();
  79 + int requestId = getRequestId(topicName, MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX);
  80 + try {
  81 + return ProtoConverter.convertToGetAttributeRequestMessage(bytes, requestId);
  82 + } catch (InvalidProtocolBufferException e) {
  83 + log.warn("Failed to decode get attributes request", e);
  84 + throw new AdaptorException(e);
  85 + }
  86 + }
  87 +
  88 + @Override
  89 + public TransportProtos.ToDeviceRpcResponseMsg convertToDeviceRpcResponse(MqttDeviceAwareSessionContext ctx, MqttPublishMessage mqttMsg) throws AdaptorException {
  90 + byte[] bytes = toBytes(mqttMsg.payload());
  91 + try {
  92 + return TransportProtos.ToDeviceRpcResponseMsg.parseFrom(bytes);
  93 + } catch (RuntimeException | InvalidProtocolBufferException e) {
  94 + log.warn("Failed to decode Rpc response", e);
  95 + throw new AdaptorException(e);
  96 + }
  97 + }
  98 +
  99 + @Override
  100 + public TransportProtos.ToServerRpcRequestMsg convertToServerRpcRequest(MqttDeviceAwareSessionContext ctx, MqttPublishMessage mqttMsg) throws AdaptorException {
  101 + byte[] bytes = toBytes(mqttMsg.payload());
  102 + String topicName = mqttMsg.variableHeader().topicName();
  103 + try {
  104 + int requestId = getRequestId(topicName, MqttTopics.DEVICE_RPC_REQUESTS_TOPIC);
  105 + return ProtoConverter.convertToServerRpcRequest(bytes, requestId);
  106 + } catch (InvalidProtocolBufferException e) {
  107 + throw new AdaptorException(e);
  108 + }
  109 + }
  110 +
  111 + @Override
  112 + public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException {
  113 + if (!StringUtils.isEmpty(responseMsg.getError())) {
  114 + throw new AdaptorException(responseMsg.getError());
  115 + } else {
  116 + int requestId = responseMsg.getRequestId();
  117 + if (requestId >= 0) {
  118 + return Optional.of(createMqttPublishMsg(ctx,
  119 + MqttTopics.DEVICE_ATTRIBUTES_RESPONSE_TOPIC_PREFIX + requestId,
  120 + responseMsg.toByteArray()));
  121 + }
  122 + return Optional.empty();
  123 + }
  124 + }
  125 +
  126 +
  127 + @Override
  128 + public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) {
  129 + return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_RPC_REQUESTS_TOPIC + rpcRequest.getRequestId(), rpcRequest.toByteArray()));
  130 + }
  131 +
  132 + @Override
  133 + public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.ToServerRpcResponseMsg rpcResponse) {
  134 + return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_RPC_RESPONSE_TOPIC + rpcResponse.getRequestId(), rpcResponse.toByteArray()));
  135 + }
  136 +
  137 + @Override
  138 + public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.AttributeUpdateNotificationMsg notificationMsg) {
  139 + return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_ATTRIBUTES_TOPIC, notificationMsg.toByteArray()));
  140 + }
  141 +
  142 + @Override
  143 + public Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException {
  144 + if (!StringUtils.isEmpty(responseMsg.getError())) {
  145 + throw new AdaptorException(responseMsg.getError());
  146 + } else {
  147 + TransportApiProtos.GatewayAttributeResponseMsg.Builder responseMsgBuilder = TransportApiProtos.GatewayAttributeResponseMsg.newBuilder();
  148 + responseMsgBuilder.setDeviceName(deviceName);
  149 + responseMsgBuilder.setResponseMsg(responseMsg);
  150 + byte[] payloadBytes = responseMsgBuilder.build().toByteArray();
  151 + return Optional.of(createMqttPublishMsg(ctx, MqttTopics.GATEWAY_ATTRIBUTES_RESPONSE_TOPIC, payloadBytes));
  152 + }
  153 + }
  154 +
  155 + @Override
  156 + public Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.AttributeUpdateNotificationMsg notificationMsg) {
  157 + TransportApiProtos.GatewayAttributeUpdateNotificationMsg.Builder builder = TransportApiProtos.GatewayAttributeUpdateNotificationMsg.newBuilder();
  158 + builder.setDeviceName(deviceName);
  159 + builder.setNotificationMsg(notificationMsg);
  160 + byte[] payloadBytes = builder.build().toByteArray();
  161 + return Optional.of(createMqttPublishMsg(ctx, MqttTopics.GATEWAY_ATTRIBUTES_TOPIC, payloadBytes));
  162 + }
  163 +
  164 + @Override
  165 + public Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) {
  166 + TransportApiProtos.GatewayDeviceRpcRequestMsg.Builder builder = TransportApiProtos.GatewayDeviceRpcRequestMsg.newBuilder();
  167 + builder.setDeviceName(deviceName);
  168 + builder.setRpcRequestMsg(rpcRequest);
  169 + byte[] payloadBytes = builder.build().toByteArray();
  170 + return Optional.of(createMqttPublishMsg(ctx, MqttTopics.GATEWAY_RPC_TOPIC, payloadBytes));
  171 + }
  172 +
  173 + public static byte[] toBytes(ByteBuf inbound) {
  174 + byte[] bytes = new byte[inbound.readableBytes()];
  175 + int readerIndex = inbound.readerIndex();
  176 + inbound.getBytes(readerIndex, bytes);
  177 + return bytes;
  178 + }
  179 +
  180 + private MqttPublishMessage createMqttPublishMsg(MqttDeviceAwareSessionContext ctx, String topic, byte[] payloadBytes) {
  181 + MqttFixedHeader mqttFixedHeader =
  182 + new MqttFixedHeader(MqttMessageType.PUBLISH, false, ctx.getQoSForTopic(topic), false, 0);
  183 + MqttPublishVariableHeader header = new MqttPublishVariableHeader(topic, ctx.nextMsgId());
  184 + ByteBuf payload = ALLOCATOR.buffer();
  185 + payload.writeBytes(payloadBytes);
  186 + return new MqttPublishMessage(mqttFixedHeader, header, payload);
  187 + }
  188 +
  189 + private int getRequestId(String topicName, String topic) {
  190 + return Integer.parseInt(topicName.substring(topic.length()));
  191 + }
  192 +
  193 +}
@@ -20,9 +20,11 @@ import lombok.Getter; @@ -20,9 +20,11 @@ import lombok.Getter;
20 import lombok.extern.slf4j.Slf4j; 20 import lombok.extern.slf4j.Slf4j;
21 import org.thingsboard.server.common.data.DeviceProfile; 21 import org.thingsboard.server.common.data.DeviceProfile;
22 import org.thingsboard.server.common.data.DeviceTransportType; 22 import org.thingsboard.server.common.data.DeviceTransportType;
  23 +import org.thingsboard.server.common.data.TransportPayloadType;
23 import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration; 24 import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration;
24 import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration; 25 import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration;
25 -import org.thingsboard.server.common.data.device.profile.MqttTopics; 26 +import org.thingsboard.server.transport.mqtt.MqttTransportContext;
  27 +import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor;
26 import org.thingsboard.server.transport.mqtt.util.MqttTopicFilter; 28 import org.thingsboard.server.transport.mqtt.util.MqttTopicFilter;
27 import org.thingsboard.server.transport.mqtt.util.MqttTopicFilterFactory; 29 import org.thingsboard.server.transport.mqtt.util.MqttTopicFilterFactory;
28 30
@@ -38,14 +40,19 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext { @@ -38,14 +40,19 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext {
38 40
39 @Getter 41 @Getter
40 private ChannelHandlerContext channel; 42 private ChannelHandlerContext channel;
  43 +
  44 + @Getter
  45 + private MqttTransportContext context;
  46 +
41 private final AtomicInteger msgIdSeq = new AtomicInteger(0); 47 private final AtomicInteger msgIdSeq = new AtomicInteger(0);
42 48
43 private volatile MqttTopicFilter telemetryTopicFilter = MqttTopicFilterFactory.getDefaultTelemetryFilter(); 49 private volatile MqttTopicFilter telemetryTopicFilter = MqttTopicFilterFactory.getDefaultTelemetryFilter();
44 private volatile MqttTopicFilter attributesTopicFilter = MqttTopicFilterFactory.getDefaultAttributesFilter(); 50 private volatile MqttTopicFilter attributesTopicFilter = MqttTopicFilterFactory.getDefaultAttributesFilter();
  51 + private volatile TransportPayloadType payloadType = TransportPayloadType.JSON;
45 52
46 -  
47 - public DeviceSessionCtx(UUID sessionId, ConcurrentMap<MqttTopicMatcher, Integer> mqttQoSMap) { 53 + public DeviceSessionCtx(UUID sessionId, ConcurrentMap<MqttTopicMatcher, Integer> mqttQoSMap, MqttTransportContext context) {
48 super(sessionId, mqttQoSMap); 54 super(sessionId, mqttQoSMap);
  55 + this.context = context;
49 } 56 }
50 57
51 public void setChannel(ChannelHandlerContext channel) { 58 public void setChannel(ChannelHandlerContext channel) {
@@ -56,14 +63,20 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext { @@ -56,14 +63,20 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext {
56 return msgIdSeq.incrementAndGet(); 63 return msgIdSeq.incrementAndGet();
57 } 64 }
58 65
59 - public boolean isDeviceTelemetryTopic(String topicName) {  
60 - return telemetryTopicFilter.filter(topicName);  
61 - } 66 + public boolean isDeviceTelemetryTopic(String topicName) { return telemetryTopicFilter.filter(topicName); }
62 67
63 public boolean isDeviceAttributesTopic(String topicName) { 68 public boolean isDeviceAttributesTopic(String topicName) {
64 return attributesTopicFilter.filter(topicName); 69 return attributesTopicFilter.filter(topicName);
65 } 70 }
66 71
  72 + public MqttTransportAdaptor getPayloadAdaptor() {
  73 + return payloadType.equals(TransportPayloadType.JSON) ? context.getJsonMqttAdaptor() : context.getProtoMqttAdaptor();
  74 + }
  75 +
  76 + public boolean isJsonPayloadType() {
  77 + return payloadType.equals(TransportPayloadType.JSON);
  78 + }
  79 +
67 @Override 80 @Override
68 public void setDeviceProfile(DeviceProfile deviceProfile) { 81 public void setDeviceProfile(DeviceProfile deviceProfile) {
69 super.setDeviceProfile(deviceProfile); 82 super.setDeviceProfile(deviceProfile);
@@ -76,11 +89,13 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext { @@ -76,11 +89,13 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext {
76 updateTopicFilters(deviceProfile); 89 updateTopicFilters(deviceProfile);
77 } 90 }
78 91
  92 +
79 private void updateTopicFilters(DeviceProfile deviceProfile) { 93 private void updateTopicFilters(DeviceProfile deviceProfile) {
80 DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration(); 94 DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration();
81 if (transportConfiguration.getType().equals(DeviceTransportType.MQTT) && 95 if (transportConfiguration.getType().equals(DeviceTransportType.MQTT) &&
82 transportConfiguration instanceof MqttDeviceProfileTransportConfiguration) { 96 transportConfiguration instanceof MqttDeviceProfileTransportConfiguration) {
83 MqttDeviceProfileTransportConfiguration mqttConfig = (MqttDeviceProfileTransportConfiguration) transportConfiguration; 97 MqttDeviceProfileTransportConfiguration mqttConfig = (MqttDeviceProfileTransportConfiguration) transportConfiguration;
  98 + payloadType = mqttConfig.getTransportPayloadType();
84 telemetryTopicFilter = MqttTopicFilterFactory.toFilter(mqttConfig.getDeviceTelemetryTopic()); 99 telemetryTopicFilter = MqttTopicFilterFactory.toFilter(mqttConfig.getDeviceTelemetryTopic());
85 attributesTopicFilter = MqttTopicFilterFactory.toFilter(mqttConfig.getDeviceAttributesTopic()); 100 attributesTopicFilter = MqttTopicFilterFactory.toFilter(mqttConfig.getDeviceAttributesTopic());
86 } else { 101 } else {
@@ -88,4 +103,5 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext { @@ -88,4 +103,5 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext {
88 attributesTopicFilter = MqttTopicFilterFactory.getDefaultAttributesFilter(); 103 attributesTopicFilter = MqttTopicFilterFactory.getDefaultAttributesFilter();
89 } 104 }
90 } 105 }
  106 +
91 } 107 }
@@ -20,7 +20,6 @@ import org.thingsboard.server.common.data.DeviceProfile; @@ -20,7 +20,6 @@ import org.thingsboard.server.common.data.DeviceProfile;
20 import org.thingsboard.server.common.transport.SessionMsgListener; 20 import org.thingsboard.server.common.transport.SessionMsgListener;
21 import org.thingsboard.server.common.transport.auth.TransportDeviceInfo; 21 import org.thingsboard.server.common.transport.auth.TransportDeviceInfo;
22 import org.thingsboard.server.gen.transport.TransportProtos; 22 import org.thingsboard.server.gen.transport.TransportProtos;
23 -import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto;  
24 import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; 23 import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
25 24
26 import java.util.UUID; 25 import java.util.UUID;
@@ -70,7 +69,7 @@ public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext imple @@ -70,7 +69,7 @@ public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext imple
70 @Override 69 @Override
71 public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg response) { 70 public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg response) {
72 try { 71 try {
73 - parent.getAdaptor().convertToGatewayPublish(this, getDeviceInfo().getDeviceName(), response).ifPresent(parent::writeAndFlush); 72 + parent.getPayloadAdaptor().convertToGatewayPublish(this, getDeviceInfo().getDeviceName(), response).ifPresent(parent::writeAndFlush);
74 } catch (Exception e) { 73 } catch (Exception e) {
75 log.trace("[{}] Failed to convert device attributes response to MQTT msg", sessionId, e); 74 log.trace("[{}] Failed to convert device attributes response to MQTT msg", sessionId, e);
76 } 75 }
@@ -79,27 +78,27 @@ public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext imple @@ -79,27 +78,27 @@ public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext imple
79 @Override 78 @Override
80 public void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg notification) { 79 public void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg notification) {
81 try { 80 try {
82 - parent.getAdaptor().convertToGatewayPublish(this, getDeviceInfo().getDeviceName(), notification).ifPresent(parent::writeAndFlush); 81 + parent.getPayloadAdaptor().convertToGatewayPublish(this, getDeviceInfo().getDeviceName(), notification).ifPresent(parent::writeAndFlush);
83 } catch (Exception e) { 82 } catch (Exception e) {
84 log.trace("[{}] Failed to convert device attributes response to MQTT msg", sessionId, e); 83 log.trace("[{}] Failed to convert device attributes response to MQTT msg", sessionId, e);
85 } 84 }
86 } 85 }
87 86
88 @Override 87 @Override
89 - public void onRemoteSessionCloseCommand(TransportProtos.SessionCloseNotificationProto sessionCloseNotification) {  
90 - parent.deregisterSession(getDeviceInfo().getDeviceName());  
91 - }  
92 -  
93 - @Override  
94 public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg request) { 88 public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg request) {
95 try { 89 try {
96 - parent.getAdaptor().convertToGatewayPublish(this, getDeviceInfo().getDeviceName(), request).ifPresent(parent::writeAndFlush); 90 + parent.getPayloadAdaptor().convertToGatewayPublish(this, getDeviceInfo().getDeviceName(), request).ifPresent(parent::writeAndFlush);
97 } catch (Exception e) { 91 } catch (Exception e) {
98 log.trace("[{}] Failed to convert device attributes response to MQTT msg", sessionId, e); 92 log.trace("[{}] Failed to convert device attributes response to MQTT msg", sessionId, e);
99 } 93 }
100 } 94 }
101 95
102 @Override 96 @Override
  97 + public void onRemoteSessionCloseCommand(TransportProtos.SessionCloseNotificationProto sessionCloseNotification) {
  98 + parent.deregisterSession(getDeviceInfo().getDeviceName());
  99 + }
  100 +
  101 + @Override
103 public void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg toServerResponse) { 102 public void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg toServerResponse) {
104 // This feature is not supported in the TB IoT Gateway yet. 103 // This feature is not supported in the TB IoT Gateway yet.
105 } 104 }