Commit 9fef6b02a3a3c94066e41ec10ee3b57da85b6f28

Authored by ShvaykaD
Committed by GitHub
1 parent 1b1dac30

[3.2] Feature/Proto Converter (#3423)

* device post-telemetry & post-attributes & claim

* device rpc to/from server & attributes request, attributes updates

* refactoring & added implementation for gateway protos api

* added timeseries/attributes mqtt tests

* fix MqttTimseriesIntegrationTest values asserts

* mqtt attributes tests improvements

* optimized time for telemetry & attributes tests

* update proto files, refactoring converter, attribute requests tests

* added claim tests, attribute request test

* added deleted keys to gateway response on attributes request & refactored tests

* added attribute updates test & refactored attribute requests tests

* added attribute updates tests for gateways

* added tests for RPC

* fix tests & cleanup code

* fix typo & cleanup transport.proto file

* added more timeouts

* revert handleGetAttributesRequest method

* revert package-locks

* fix getJsonObjectForGateway method

* fix validateSharedResponseGateway method in AbstractMqttAttributesRequestIntegrationTest

* fix mqtt topics

* fix license headers

* refactor tests

* remove todo and lck files from pull

* improvements for claiming tests

* update device creation logic from gateway request

* refactoring

* extract TransportService process calls to private methods

* fix duplicates & removed empty lines
Showing 63 changed files with 3694 additions and 432 deletions

Too many changes to show.

To preserve performance only 63 of 75 files are displayed.

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