Browse Source
* 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 linespull/3551/head
committed by
GitHub
75 changed files with 4397 additions and 534 deletions
@ -0,0 +1,215 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt; |
|||
|
|||
import com.fasterxml.jackson.databind.node.ObjectNode; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.eclipse.paho.client.mqttv3.MqttAsyncClient; |
|||
import org.eclipse.paho.client.mqttv3.MqttConnectOptions; |
|||
import org.eclipse.paho.client.mqttv3.MqttException; |
|||
import org.eclipse.paho.client.mqttv3.MqttMessage; |
|||
import org.junit.Assert; |
|||
import org.springframework.util.StringUtils; |
|||
import org.thingsboard.server.common.data.Device; |
|||
import org.thingsboard.server.common.data.DeviceProfile; |
|||
import org.thingsboard.server.common.data.DeviceProfileType; |
|||
import org.thingsboard.server.common.data.DeviceTransportType; |
|||
import org.thingsboard.server.common.data.Tenant; |
|||
import org.thingsboard.server.common.data.TransportPayloadType; |
|||
import org.thingsboard.server.common.data.User; |
|||
import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; |
|||
import org.thingsboard.server.common.data.device.profile.DeviceProfileData; |
|||
import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration; |
|||
import org.thingsboard.server.common.data.security.Authority; |
|||
import org.thingsboard.server.common.data.security.DeviceCredentials; |
|||
import org.thingsboard.server.controller.AbstractControllerTest; |
|||
import org.thingsboard.server.gen.transport.TransportProtos; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
import java.util.concurrent.atomic.AtomicInteger; |
|||
|
|||
import static org.junit.Assert.assertEquals; |
|||
import static org.junit.Assert.assertNotNull; |
|||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
|||
|
|||
@Slf4j |
|||
public abstract class AbstractMqttIntegrationTest extends AbstractControllerTest { |
|||
|
|||
protected static final String MQTT_URL = "tcp://localhost:1883"; |
|||
|
|||
private static final AtomicInteger atomicInteger = new AtomicInteger(2); |
|||
|
|||
protected Tenant savedTenant; |
|||
protected User tenantAdmin; |
|||
|
|||
protected Device savedDevice; |
|||
protected String accessToken; |
|||
|
|||
protected Device savedGateway; |
|||
protected String gatewayAccessToken; |
|||
|
|||
protected void processBeforeTest(String deviceName, String gatewayName, TransportPayloadType payloadType, String telemetryTopic, String attributesTopic) throws Exception { |
|||
loginSysAdmin(); |
|||
|
|||
Tenant tenant = new Tenant(); |
|||
tenant.setTitle("My tenant"); |
|||
savedTenant = doPost("/api/tenant", tenant, Tenant.class); |
|||
Assert.assertNotNull(savedTenant); |
|||
|
|||
tenantAdmin = new User(); |
|||
tenantAdmin.setAuthority(Authority.TENANT_ADMIN); |
|||
tenantAdmin.setTenantId(savedTenant.getId()); |
|||
tenantAdmin.setEmail("tenant" + atomicInteger.getAndIncrement() + "@thingsboard.org"); |
|||
tenantAdmin.setFirstName("Joe"); |
|||
tenantAdmin.setLastName("Downs"); |
|||
|
|||
tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1"); |
|||
|
|||
Device device = new Device(); |
|||
device.setName(deviceName); |
|||
device.setType("default"); |
|||
|
|||
Device gateway = new Device(); |
|||
gateway.setName(gatewayName); |
|||
gateway.setType("default"); |
|||
ObjectNode additionalInfo = mapper.createObjectNode(); |
|||
additionalInfo.put("gateway", true); |
|||
gateway.setAdditionalInfo(additionalInfo); |
|||
|
|||
if (payloadType != null) { |
|||
DeviceProfile mqttDeviceProfile = createMqttDeviceProfile(payloadType, telemetryTopic, attributesTopic); |
|||
DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", mqttDeviceProfile, DeviceProfile.class); |
|||
device.setType(savedDeviceProfile.getName()); |
|||
device.setDeviceProfileId(savedDeviceProfile.getId()); |
|||
gateway.setType(savedDeviceProfile.getName()); |
|||
gateway.setDeviceProfileId(savedDeviceProfile.getId()); |
|||
} |
|||
|
|||
savedDevice = doPost("/api/device", device, Device.class); |
|||
|
|||
DeviceCredentials deviceCredentials = |
|||
doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class); |
|||
|
|||
savedGateway = doPost("/api/device", gateway, Device.class); |
|||
|
|||
DeviceCredentials gatewayCredentials = |
|||
doGet("/api/device/" + savedGateway.getId().getId().toString() + "/credentials", DeviceCredentials.class); |
|||
|
|||
assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId()); |
|||
accessToken = deviceCredentials.getCredentialsId(); |
|||
assertNotNull(accessToken); |
|||
|
|||
assertEquals(savedGateway.getId(), gatewayCredentials.getDeviceId()); |
|||
gatewayAccessToken = gatewayCredentials.getCredentialsId(); |
|||
assertNotNull(gatewayAccessToken); |
|||
|
|||
} |
|||
|
|||
protected void processAfterTest() throws Exception { |
|||
loginSysAdmin(); |
|||
if (savedTenant != null) { |
|||
doDelete("/api/tenant/" + savedTenant.getId().getId().toString()).andExpect(status().isOk()); |
|||
} |
|||
} |
|||
|
|||
protected MqttAsyncClient getMqttAsyncClient(String accessToken) throws MqttException { |
|||
String clientId = MqttAsyncClient.generateClientId(); |
|||
MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId); |
|||
|
|||
MqttConnectOptions options = new MqttConnectOptions(); |
|||
options.setUserName(accessToken); |
|||
client.connect(options).waitForCompletion(); |
|||
return client; |
|||
} |
|||
|
|||
protected void publishMqttMsg(MqttAsyncClient client, byte[] payload, String topic) throws MqttException { |
|||
MqttMessage message = new MqttMessage(); |
|||
message.setPayload(payload); |
|||
client.publish(topic, message); |
|||
} |
|||
|
|||
protected List<TransportProtos.KeyValueProto> getKvProtos(List<String> expectedKeys) { |
|||
List<TransportProtos.KeyValueProto> keyValueProtos = new ArrayList<>(); |
|||
TransportProtos.KeyValueProto strKeyValueProto = getKeyValueProto(expectedKeys.get(0), "value1", TransportProtos.KeyValueType.STRING_V); |
|||
TransportProtos.KeyValueProto boolKeyValueProto = getKeyValueProto(expectedKeys.get(1), "true", TransportProtos.KeyValueType.BOOLEAN_V); |
|||
TransportProtos.KeyValueProto dblKeyValueProto = getKeyValueProto(expectedKeys.get(2), "3.0", TransportProtos.KeyValueType.DOUBLE_V); |
|||
TransportProtos.KeyValueProto longKeyValueProto = getKeyValueProto(expectedKeys.get(3), "4", TransportProtos.KeyValueType.LONG_V); |
|||
TransportProtos.KeyValueProto jsonKeyValueProto = getKeyValueProto(expectedKeys.get(4), "{\"someNumber\": 42, \"someArray\": [1,2,3], \"someNestedObject\": {\"key\": \"value\"}}", TransportProtos.KeyValueType.JSON_V); |
|||
keyValueProtos.add(strKeyValueProto); |
|||
keyValueProtos.add(boolKeyValueProto); |
|||
keyValueProtos.add(dblKeyValueProto); |
|||
keyValueProtos.add(longKeyValueProto); |
|||
keyValueProtos.add(jsonKeyValueProto); |
|||
return keyValueProtos; |
|||
} |
|||
|
|||
protected TransportProtos.KeyValueProto getKeyValueProto(String key, String strValue, TransportProtos.KeyValueType type) { |
|||
TransportProtos.KeyValueProto.Builder keyValueProtoBuilder = TransportProtos.KeyValueProto.newBuilder(); |
|||
keyValueProtoBuilder.setKey(key); |
|||
keyValueProtoBuilder.setType(type); |
|||
switch (type) { |
|||
case BOOLEAN_V: |
|||
keyValueProtoBuilder.setBoolV(Boolean.parseBoolean(strValue)); |
|||
break; |
|||
case LONG_V: |
|||
keyValueProtoBuilder.setLongV(Long.parseLong(strValue)); |
|||
break; |
|||
case DOUBLE_V: |
|||
keyValueProtoBuilder.setDoubleV(Double.parseDouble(strValue)); |
|||
break; |
|||
case STRING_V: |
|||
keyValueProtoBuilder.setStringV(strValue); |
|||
break; |
|||
case JSON_V: |
|||
keyValueProtoBuilder.setJsonV(strValue); |
|||
break; |
|||
} |
|||
return keyValueProtoBuilder.build(); |
|||
} |
|||
|
|||
protected DeviceProfile createMqttDeviceProfile(TransportPayloadType transportPayloadType, String telemetryTopic, String attributesTopic) { |
|||
DeviceProfile deviceProfile = new DeviceProfile(); |
|||
deviceProfile.setName(transportPayloadType.name()); |
|||
deviceProfile.setType(DeviceProfileType.DEFAULT); |
|||
deviceProfile.setTransportType(DeviceTransportType.MQTT); |
|||
deviceProfile.setDescription(transportPayloadType.name() + " Test"); |
|||
DeviceProfileData deviceProfileData = new DeviceProfileData(); |
|||
DefaultDeviceProfileConfiguration configuration = new DefaultDeviceProfileConfiguration(); |
|||
MqttDeviceProfileTransportConfiguration transportConfiguration = new MqttDeviceProfileTransportConfiguration(); |
|||
transportConfiguration.setTransportPayloadType(transportPayloadType); |
|||
if (!StringUtils.isEmpty(telemetryTopic)) { |
|||
transportConfiguration.setDeviceTelemetryTopic(telemetryTopic); |
|||
} |
|||
if (!StringUtils.isEmpty(attributesTopic)) { |
|||
transportConfiguration.setDeviceAttributesTopic(attributesTopic); |
|||
} |
|||
deviceProfileData.setTransportConfiguration(transportConfiguration); |
|||
deviceProfileData.setConfiguration(configuration); |
|||
deviceProfile.setProfileData(deviceProfileData); |
|||
deviceProfile.setDefault(false); |
|||
deviceProfile.setDefaultRuleChainId(null); |
|||
return deviceProfile; |
|||
} |
|||
|
|||
protected TransportProtos.PostAttributeMsg getPostAttributeMsg(List<String> expectedKeys) { |
|||
List<TransportProtos.KeyValueProto> kvProtos = getKvProtos(expectedKeys); |
|||
TransportProtos.PostAttributeMsg.Builder builder = TransportProtos.PostAttributeMsg.newBuilder(); |
|||
builder.addAllKv(kvProtos); |
|||
return builder.build(); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,111 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.attributes; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; |
|||
import org.eclipse.paho.client.mqttv3.MqttCallback; |
|||
import org.eclipse.paho.client.mqttv3.MqttMessage; |
|||
import org.thingsboard.server.common.data.TransportPayloadType; |
|||
import org.thingsboard.server.gen.transport.TransportProtos; |
|||
import org.thingsboard.server.mqtt.AbstractMqttIntegrationTest; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
import java.util.concurrent.CountDownLatch; |
|||
|
|||
@Slf4j |
|||
public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqttIntegrationTest { |
|||
|
|||
protected static final String POST_ATTRIBUTES_PAYLOAD = "{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73," + |
|||
"\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}}"; |
|||
|
|||
protected void processBeforeTest(String deviceName, String gatewayName, TransportPayloadType payloadType, String telemetryTopic, String attributesTopic) throws Exception { |
|||
super.processBeforeTest(deviceName, gatewayName, payloadType, telemetryTopic, attributesTopic); |
|||
} |
|||
|
|||
protected void processAfterTest() throws Exception { |
|||
super.processAfterTest(); |
|||
} |
|||
|
|||
protected List<TransportProtos.TsKvProto> getTsKvProtoList() { |
|||
TransportProtos.TsKvProto tsKvProtoAttribute1 = getTsKvProto("attribute1", "value1", TransportProtos.KeyValueType.STRING_V); |
|||
TransportProtos.TsKvProto tsKvProtoAttribute2 = getTsKvProto("attribute2", "true", TransportProtos.KeyValueType.BOOLEAN_V); |
|||
TransportProtos.TsKvProto tsKvProtoAttribute3 = getTsKvProto("attribute3", "42.0", TransportProtos.KeyValueType.DOUBLE_V); |
|||
TransportProtos.TsKvProto tsKvProtoAttribute4 = getTsKvProto("attribute4", "73", TransportProtos.KeyValueType.LONG_V); |
|||
TransportProtos.TsKvProto tsKvProtoAttribute5 = getTsKvProto("attribute5", "{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}", TransportProtos.KeyValueType.JSON_V); |
|||
List<TransportProtos.TsKvProto> tsKvProtoList = new ArrayList<>(); |
|||
tsKvProtoList.add(tsKvProtoAttribute1); |
|||
tsKvProtoList.add(tsKvProtoAttribute2); |
|||
tsKvProtoList.add(tsKvProtoAttribute3); |
|||
tsKvProtoList.add(tsKvProtoAttribute4); |
|||
tsKvProtoList.add(tsKvProtoAttribute5); |
|||
return tsKvProtoList; |
|||
} |
|||
|
|||
|
|||
protected TransportProtos.TsKvProto getTsKvProto(String key, String value, TransportProtos.KeyValueType keyValueType) { |
|||
TransportProtos.TsKvProto.Builder tsKvProtoBuilder = TransportProtos.TsKvProto.newBuilder(); |
|||
TransportProtos.KeyValueProto keyValueProto = getKeyValueProto(key, value, keyValueType); |
|||
tsKvProtoBuilder.setKv(keyValueProto); |
|||
return tsKvProtoBuilder.build(); |
|||
} |
|||
|
|||
protected TestMqttCallback getTestMqttCallback() { |
|||
CountDownLatch latch = new CountDownLatch(1); |
|||
return new TestMqttCallback(latch); |
|||
} |
|||
|
|||
protected static class TestMqttCallback implements MqttCallback { |
|||
|
|||
private final CountDownLatch latch; |
|||
private Integer qoS; |
|||
private byte[] payloadBytes; |
|||
|
|||
TestMqttCallback(CountDownLatch latch) { |
|||
this.latch = latch; |
|||
} |
|||
|
|||
public int getQoS() { |
|||
return qoS; |
|||
} |
|||
|
|||
public byte[] getPayloadBytes() { |
|||
return payloadBytes; |
|||
} |
|||
|
|||
public CountDownLatch getLatch() { |
|||
return latch; |
|||
} |
|||
|
|||
@Override |
|||
public void connectionLost(Throwable throwable) { |
|||
} |
|||
|
|||
@Override |
|||
public void messageArrived(String requestTopic, MqttMessage mqttMessage) throws Exception { |
|||
qoS = mqttMessage.getQos(); |
|||
payloadBytes = mqttMessage.getPayload(); |
|||
latch.countDown(); |
|||
} |
|||
|
|||
@Override |
|||
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) { |
|||
|
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,150 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.attributes.request; |
|||
|
|||
import com.google.protobuf.InvalidProtocolBufferException; |
|||
import io.netty.handler.codec.mqtt.MqttQoS; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; |
|||
import org.eclipse.paho.client.mqttv3.MqttAsyncClient; |
|||
import org.eclipse.paho.client.mqttv3.MqttCallback; |
|||
import org.eclipse.paho.client.mqttv3.MqttException; |
|||
import org.eclipse.paho.client.mqttv3.MqttMessage; |
|||
import org.junit.After; |
|||
import org.junit.Before; |
|||
import org.junit.Test; |
|||
import org.thingsboard.server.common.data.Device; |
|||
import org.thingsboard.server.common.data.device.profile.MqttTopics; |
|||
import org.thingsboard.server.dao.util.mapping.JacksonUtil; |
|||
import org.thingsboard.server.mqtt.attributes.AbstractMqttAttributesIntegrationTest; |
|||
|
|||
import java.nio.charset.StandardCharsets; |
|||
import java.util.concurrent.CountDownLatch; |
|||
import java.util.concurrent.TimeUnit; |
|||
|
|||
import static org.junit.Assert.assertEquals; |
|||
import static org.junit.Assert.assertFalse; |
|||
import static org.junit.Assert.assertNotNull; |
|||
import static org.junit.Assert.assertTrue; |
|||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
|||
|
|||
@Slf4j |
|||
public abstract class AbstractMqttAttributesRequestIntegrationTest extends AbstractMqttAttributesIntegrationTest { |
|||
|
|||
@Before |
|||
public void beforeTest() throws Exception { |
|||
processBeforeTest("Test Request attribute values from the server", "Gateway Test Request attribute values from the server", null, null, null); |
|||
} |
|||
|
|||
@After |
|||
public void afterTest() throws Exception { |
|||
processAfterTest(); |
|||
} |
|||
|
|||
@Test |
|||
public void testRequestAttributesValuesFromTheServer() throws Exception { |
|||
processTestRequestAttributesValuesFromTheServer(); |
|||
} |
|||
|
|||
@Test |
|||
public void testRequestAttributesValuesFromTheServerGateway() throws Exception { |
|||
processTestGatewayRequestAttributesValuesFromTheServer(); |
|||
} |
|||
|
|||
protected void processTestRequestAttributesValuesFromTheServer() throws Exception { |
|||
|
|||
MqttAsyncClient client = getMqttAsyncClient(accessToken); |
|||
|
|||
postAttributesAndSubscribeToTopic(savedDevice, client); |
|||
|
|||
Thread.sleep(1000); |
|||
|
|||
TestMqttCallback callback = getTestMqttCallback(); |
|||
client.setCallback(callback); |
|||
|
|||
validateResponse(client, callback.getLatch(), callback); |
|||
} |
|||
|
|||
protected void processTestGatewayRequestAttributesValuesFromTheServer() throws Exception { |
|||
|
|||
MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); |
|||
|
|||
postGatewayDeviceClientAttributes(client); |
|||
|
|||
Thread.sleep(1000); |
|||
|
|||
Device savedDevice = doGet("/api/tenant/devices?deviceName=" + "Gateway Device Request Attributes", Device.class); |
|||
assertNotNull(savedDevice); |
|||
doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk()); |
|||
|
|||
Thread.sleep(1000); |
|||
|
|||
client.subscribe(MqttTopics.GATEWAY_ATTRIBUTES_RESPONSE_TOPIC, MqttQoS.AT_LEAST_ONCE.value()); |
|||
|
|||
TestMqttCallback clientAttributesCallback = getTestMqttCallback(); |
|||
client.setCallback(clientAttributesCallback); |
|||
validateClientResponseGateway(client, clientAttributesCallback); |
|||
|
|||
TestMqttCallback sharedAttributesCallback = getTestMqttCallback(); |
|||
client.setCallback(sharedAttributesCallback); |
|||
validateSharedResponseGateway(client, sharedAttributesCallback); |
|||
} |
|||
|
|||
protected void postAttributesAndSubscribeToTopic(Device savedDevice, MqttAsyncClient client) throws Exception { |
|||
doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk()); |
|||
client.publish(MqttTopics.DEVICE_ATTRIBUTES_TOPIC, new MqttMessage(POST_ATTRIBUTES_PAYLOAD.getBytes())); |
|||
client.subscribe(MqttTopics.DEVICE_ATTRIBUTES_RESPONSES_TOPIC, MqttQoS.AT_MOST_ONCE.value()); |
|||
} |
|||
|
|||
protected void postGatewayDeviceClientAttributes(MqttAsyncClient client) throws Exception { |
|||
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\"}}}}"; |
|||
client.publish(MqttTopics.GATEWAY_ATTRIBUTES_TOPIC, new MqttMessage(postClientAttributes.getBytes())); |
|||
} |
|||
|
|||
protected void validateResponse(MqttAsyncClient client, CountDownLatch latch, TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException { |
|||
String keys = "attribute1,attribute2,attribute3,attribute4,attribute5"; |
|||
String payloadStr = "{\"clientKeys\":\"" + keys + "\", \"sharedKeys\":\"" + keys + "\"}"; |
|||
MqttMessage mqttMessage = new MqttMessage(); |
|||
mqttMessage.setPayload(payloadStr.getBytes()); |
|||
client.publish(MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX + "1", mqttMessage); |
|||
latch.await(3, TimeUnit.SECONDS); |
|||
assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS()); |
|||
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\"}}}}"; |
|||
assertEquals(JacksonUtil.toJsonNode(expectedRequestPayload), JacksonUtil.toJsonNode(new String(callback.getPayloadBytes(), StandardCharsets.UTF_8))); |
|||
} |
|||
|
|||
protected void validateClientResponseGateway(MqttAsyncClient client, TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException { |
|||
String payloadStr = "{\"id\": 1, \"device\": \"" + "Gateway Device Request Attributes" + "\", \"client\": true, \"keys\": [\"attribute1\", \"attribute2\", \"attribute3\", \"attribute4\", \"attribute5\"]}"; |
|||
MqttMessage mqttMessage = new MqttMessage(); |
|||
mqttMessage.setPayload(payloadStr.getBytes()); |
|||
client.publish(MqttTopics.GATEWAY_ATTRIBUTES_REQUEST_TOPIC, mqttMessage); |
|||
callback.getLatch().await(3, TimeUnit.SECONDS); |
|||
assertEquals(MqttQoS.AT_LEAST_ONCE.value(), callback.getQoS()); |
|||
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\"}}}}"; |
|||
assertEquals(JacksonUtil.toJsonNode(expectedRequestPayload), JacksonUtil.toJsonNode(new String(callback.getPayloadBytes(), StandardCharsets.UTF_8))); |
|||
} |
|||
|
|||
protected void validateSharedResponseGateway(MqttAsyncClient client, TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException { |
|||
String payloadStr = "{\"id\": 1, \"device\": \"" + "Gateway Device Request Attributes" + "\", \"client\": false, \"keys\": [\"attribute1\", \"attribute2\", \"attribute3\", \"attribute4\", \"attribute5\"]}"; |
|||
MqttMessage mqttMessage = new MqttMessage(); |
|||
mqttMessage.setPayload(payloadStr.getBytes()); |
|||
client.publish(MqttTopics.GATEWAY_ATTRIBUTES_REQUEST_TOPIC, mqttMessage); |
|||
callback.getLatch().await(3, TimeUnit.SECONDS); |
|||
assertEquals(MqttQoS.AT_LEAST_ONCE.value(), callback.getQoS()); |
|||
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\"}}}}"; |
|||
assertEquals(JacksonUtil.toJsonNode(expectedRequestPayload), JacksonUtil.toJsonNode(new String(callback.getPayloadBytes(), StandardCharsets.UTF_8))); |
|||
} |
|||
} |
|||
@ -0,0 +1,51 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.attributes.request; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.junit.After; |
|||
import org.junit.Before; |
|||
import org.junit.Ignore; |
|||
import org.junit.Test; |
|||
import org.thingsboard.server.common.data.TransportPayloadType; |
|||
|
|||
import static org.junit.Assert.assertEquals; |
|||
import static org.junit.Assert.assertNotNull; |
|||
import static org.junit.Assert.assertTrue; |
|||
|
|||
@Slf4j |
|||
public abstract class AbstractMqttAttributesRequestJsonIntegrationTest extends AbstractMqttAttributesRequestIntegrationTest { |
|||
|
|||
@Before |
|||
public void beforeTest() throws Exception { |
|||
processBeforeTest("Test Request attribute values from the server json", "Gateway Test Request attribute values from the server json", TransportPayloadType.JSON, null, null); |
|||
} |
|||
|
|||
@After |
|||
public void afterTest() throws Exception { |
|||
processAfterTest(); |
|||
} |
|||
|
|||
@Test |
|||
public void testRequestAttributesValuesFromTheServer() throws Exception { |
|||
processTestRequestAttributesValuesFromTheServer(); |
|||
} |
|||
|
|||
@Test |
|||
public void testRequestAttributesValuesFromTheServerGateway() throws Exception { |
|||
processTestGatewayRequestAttributesValuesFromTheServer(); |
|||
} |
|||
} |
|||
@ -0,0 +1,201 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.attributes.request; |
|||
|
|||
import com.google.protobuf.InvalidProtocolBufferException; |
|||
import io.netty.handler.codec.mqtt.MqttQoS; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.eclipse.paho.client.mqttv3.MqttAsyncClient; |
|||
import org.eclipse.paho.client.mqttv3.MqttException; |
|||
import org.eclipse.paho.client.mqttv3.MqttMessage; |
|||
import org.junit.After; |
|||
import org.junit.Before; |
|||
import org.junit.Test; |
|||
import org.thingsboard.server.common.data.Device; |
|||
import org.thingsboard.server.common.data.TransportPayloadType; |
|||
import org.thingsboard.server.common.data.device.profile.MqttTopics; |
|||
import org.thingsboard.server.gen.transport.TransportApiProtos; |
|||
import org.thingsboard.server.gen.transport.TransportProtos; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.Arrays; |
|||
import java.util.List; |
|||
import java.util.concurrent.CountDownLatch; |
|||
import java.util.concurrent.TimeUnit; |
|||
import java.util.stream.Collectors; |
|||
|
|||
import static org.junit.Assert.assertEquals; |
|||
import static org.junit.Assert.assertTrue; |
|||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
|||
|
|||
@Slf4j |
|||
public abstract class AbstractMqttAttributesRequestProtoIntegrationTest extends AbstractMqttAttributesRequestIntegrationTest { |
|||
|
|||
@Before |
|||
public void beforeTest() throws Exception { |
|||
processBeforeTest("Test Request attribute values from the server proto", "Gateway Test Request attribute values from the server proto", TransportPayloadType.PROTOBUF, null, null); |
|||
} |
|||
|
|||
@After |
|||
public void afterTest() throws Exception { |
|||
processAfterTest(); |
|||
} |
|||
|
|||
@Test |
|||
public void testRequestAttributesValuesFromTheServer() throws Exception { |
|||
processTestRequestAttributesValuesFromTheServer(); |
|||
} |
|||
|
|||
|
|||
@Test |
|||
public void testRequestAttributesValuesFromTheServerGateway() throws Exception { |
|||
processTestGatewayRequestAttributesValuesFromTheServer(); |
|||
} |
|||
|
|||
protected void postAttributesAndSubscribeToTopic(Device savedDevice, MqttAsyncClient client) throws Exception { |
|||
doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk()); |
|||
String keys = "attribute1,attribute2,attribute3,attribute4,attribute5"; |
|||
List<String> expectedKeys = Arrays.asList(keys.split(",")); |
|||
TransportProtos.PostAttributeMsg postAttributeMsg = getPostAttributeMsg(expectedKeys); |
|||
byte[] payload = postAttributeMsg.toByteArray(); |
|||
client.publish(MqttTopics.DEVICE_ATTRIBUTES_TOPIC, new MqttMessage(payload)); |
|||
client.subscribe(MqttTopics.DEVICE_ATTRIBUTES_RESPONSES_TOPIC, MqttQoS.AT_MOST_ONCE.value()); |
|||
} |
|||
|
|||
protected void postGatewayDeviceClientAttributes(MqttAsyncClient client) throws Exception { |
|||
String keys = "attribute1,attribute2,attribute3,attribute4,attribute5"; |
|||
List<String> expectedKeys = Arrays.asList(keys.split(",")); |
|||
TransportProtos.PostAttributeMsg postAttributeMsg = getPostAttributeMsg(expectedKeys); |
|||
TransportApiProtos.AttributesMsg.Builder attributesMsgBuilder = TransportApiProtos.AttributesMsg.newBuilder(); |
|||
attributesMsgBuilder.setDeviceName("Gateway Device Request Attributes"); |
|||
attributesMsgBuilder.setMsg(postAttributeMsg); |
|||
TransportApiProtos.AttributesMsg attributesMsg = attributesMsgBuilder.build(); |
|||
TransportApiProtos.GatewayAttributesMsg.Builder gatewayAttributeMsgBuilder = TransportApiProtos.GatewayAttributesMsg.newBuilder(); |
|||
gatewayAttributeMsgBuilder.addMsg(attributesMsg); |
|||
byte[] bytes = gatewayAttributeMsgBuilder.build().toByteArray(); |
|||
client.publish(MqttTopics.GATEWAY_ATTRIBUTES_TOPIC, new MqttMessage(bytes)); |
|||
} |
|||
|
|||
protected void validateResponse(MqttAsyncClient client, CountDownLatch latch, TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException { |
|||
String keys = "attribute1,attribute2,attribute3,attribute4,attribute5"; |
|||
TransportApiProtos.AttributesRequest.Builder attributesRequestBuilder = TransportApiProtos.AttributesRequest.newBuilder(); |
|||
attributesRequestBuilder.setClientKeys(keys); |
|||
attributesRequestBuilder.setSharedKeys(keys); |
|||
TransportApiProtos.AttributesRequest attributesRequest = attributesRequestBuilder.build(); |
|||
MqttMessage mqttMessage = new MqttMessage(); |
|||
mqttMessage.setPayload(attributesRequest.toByteArray()); |
|||
client.publish(MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX + "1", mqttMessage); |
|||
latch.await(3, TimeUnit.SECONDS); |
|||
assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS()); |
|||
TransportProtos.GetAttributeResponseMsg expectedAttributesResponse = getExpectedAttributeResponseMsg(); |
|||
TransportProtos.GetAttributeResponseMsg actualAttributesResponse = TransportProtos.GetAttributeResponseMsg.parseFrom(callback.getPayloadBytes()); |
|||
assertEquals(expectedAttributesResponse.getRequestId(), actualAttributesResponse.getRequestId()); |
|||
List<TransportProtos.KeyValueProto> expectedClientKeyValueProtos = expectedAttributesResponse.getClientAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); |
|||
List<TransportProtos.KeyValueProto> expectedSharedKeyValueProtos = expectedAttributesResponse.getSharedAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); |
|||
List<TransportProtos.KeyValueProto> actualClientKeyValueProtos = actualAttributesResponse.getClientAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); |
|||
List<TransportProtos.KeyValueProto> actualSharedKeyValueProtos = actualAttributesResponse.getSharedAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); |
|||
assertTrue(actualClientKeyValueProtos.containsAll(expectedClientKeyValueProtos)); |
|||
assertTrue(actualSharedKeyValueProtos.containsAll(expectedSharedKeyValueProtos)); |
|||
} |
|||
|
|||
protected void validateClientResponseGateway(MqttAsyncClient client, TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException { |
|||
String keys = "attribute1,attribute2,attribute3,attribute4,attribute5"; |
|||
TransportApiProtos.GatewayAttributesRequestMsg gatewayAttributesRequestMsg = getGatewayAttributesRequestMsg(keys, true); |
|||
client.publish(MqttTopics.GATEWAY_ATTRIBUTES_REQUEST_TOPIC, new MqttMessage(gatewayAttributesRequestMsg.toByteArray())); |
|||
callback.getLatch().await(3, TimeUnit.SECONDS); |
|||
assertEquals(MqttQoS.AT_LEAST_ONCE.value(), callback.getQoS()); |
|||
TransportApiProtos.GatewayAttributeResponseMsg expectedGatewayAttributeResponseMsg = getExpectedGatewayAttributeResponseMsg(true); |
|||
TransportApiProtos.GatewayAttributeResponseMsg actualGatewayAttributeResponseMsg = TransportApiProtos.GatewayAttributeResponseMsg.parseFrom(callback.getPayloadBytes()); |
|||
assertEquals(expectedGatewayAttributeResponseMsg.getDeviceName(), actualGatewayAttributeResponseMsg.getDeviceName()); |
|||
|
|||
TransportProtos.GetAttributeResponseMsg expectedResponseMsg = expectedGatewayAttributeResponseMsg.getResponseMsg(); |
|||
TransportProtos.GetAttributeResponseMsg actualResponseMsg = actualGatewayAttributeResponseMsg.getResponseMsg(); |
|||
assertEquals(expectedResponseMsg.getRequestId(), actualResponseMsg.getRequestId()); |
|||
|
|||
List<TransportProtos.KeyValueProto> expectedClientKeyValueProtos = expectedResponseMsg.getClientAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); |
|||
List<TransportProtos.KeyValueProto> actualClientKeyValueProtos = actualResponseMsg.getClientAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); |
|||
assertTrue(actualClientKeyValueProtos.containsAll(expectedClientKeyValueProtos)); |
|||
} |
|||
|
|||
protected void validateSharedResponseGateway(MqttAsyncClient client, TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException { |
|||
String keys = "attribute1,attribute2,attribute3,attribute4,attribute5"; |
|||
TransportApiProtos.GatewayAttributesRequestMsg gatewayAttributesRequestMsg = getGatewayAttributesRequestMsg(keys, false); |
|||
client.publish(MqttTopics.GATEWAY_ATTRIBUTES_REQUEST_TOPIC, new MqttMessage(gatewayAttributesRequestMsg.toByteArray())); |
|||
callback.getLatch().await(3, TimeUnit.SECONDS); |
|||
assertEquals(MqttQoS.AT_LEAST_ONCE.value(), callback.getQoS()); |
|||
TransportApiProtos.GatewayAttributeResponseMsg expectedGatewayAttributeResponseMsg = getExpectedGatewayAttributeResponseMsg(false); |
|||
TransportApiProtos.GatewayAttributeResponseMsg actualGatewayAttributeResponseMsg = TransportApiProtos.GatewayAttributeResponseMsg.parseFrom(callback.getPayloadBytes()); |
|||
assertEquals(expectedGatewayAttributeResponseMsg.getDeviceName(), actualGatewayAttributeResponseMsg.getDeviceName()); |
|||
|
|||
TransportProtos.GetAttributeResponseMsg expectedResponseMsg = expectedGatewayAttributeResponseMsg.getResponseMsg(); |
|||
TransportProtos.GetAttributeResponseMsg actualResponseMsg = actualGatewayAttributeResponseMsg.getResponseMsg(); |
|||
assertEquals(expectedResponseMsg.getRequestId(), actualResponseMsg.getRequestId()); |
|||
|
|||
List<TransportProtos.KeyValueProto> expectedSharedKeyValueProtos = expectedResponseMsg.getSharedAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); |
|||
List<TransportProtos.KeyValueProto> actualSharedKeyValueProtos = actualResponseMsg.getSharedAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); |
|||
|
|||
assertTrue(actualSharedKeyValueProtos.containsAll(expectedSharedKeyValueProtos)); |
|||
} |
|||
|
|||
private TransportApiProtos.GatewayAttributesRequestMsg getGatewayAttributesRequestMsg(String keys, boolean client) { |
|||
return TransportApiProtos.GatewayAttributesRequestMsg.newBuilder() |
|||
.setClient(client) |
|||
.addAllKeys(Arrays.asList(keys.split(","))) |
|||
.setDeviceName("Gateway Device Request Attributes") |
|||
.setId(1).build(); |
|||
} |
|||
|
|||
private TransportProtos.GetAttributeResponseMsg getExpectedAttributeResponseMsg() { |
|||
TransportProtos.GetAttributeResponseMsg.Builder result = TransportProtos.GetAttributeResponseMsg.newBuilder(); |
|||
List<TransportProtos.TsKvProto> tsKvProtoList = getTsKvProtoList(); |
|||
result.addAllClientAttributeList(tsKvProtoList); |
|||
result.addAllSharedAttributeList(tsKvProtoList); |
|||
result.setRequestId(1); |
|||
return result.build(); |
|||
} |
|||
|
|||
private TransportApiProtos.GatewayAttributeResponseMsg getExpectedGatewayAttributeResponseMsg(boolean client) { |
|||
TransportApiProtos.GatewayAttributeResponseMsg.Builder gatewayAttributeResponseMsg = TransportApiProtos.GatewayAttributeResponseMsg.newBuilder(); |
|||
TransportProtos.GetAttributeResponseMsg.Builder getAttributeResponseMsgBuilder = TransportProtos.GetAttributeResponseMsg.newBuilder(); |
|||
List<TransportProtos.TsKvProto> tsKvProtoList = getTsKvProtoList(); |
|||
if (client) { |
|||
getAttributeResponseMsgBuilder.addAllClientAttributeList(tsKvProtoList); |
|||
} else { |
|||
getAttributeResponseMsgBuilder.addAllSharedAttributeList(tsKvProtoList); |
|||
} |
|||
getAttributeResponseMsgBuilder.setRequestId(1); |
|||
TransportProtos.GetAttributeResponseMsg getAttributeResponseMsg = getAttributeResponseMsgBuilder.build(); |
|||
gatewayAttributeResponseMsg.setDeviceName("Gateway Device Request Attributes"); |
|||
gatewayAttributeResponseMsg.setResponseMsg(getAttributeResponseMsg); |
|||
return gatewayAttributeResponseMsg.build(); |
|||
} |
|||
|
|||
protected List<TransportProtos.KeyValueProto> getKvProtos(List<String> expectedKeys) { |
|||
List<TransportProtos.KeyValueProto> keyValueProtos = new ArrayList<>(); |
|||
TransportProtos.KeyValueProto strKeyValueProto = getKeyValueProto(expectedKeys.get(0), "value1", TransportProtos.KeyValueType.STRING_V); |
|||
TransportProtos.KeyValueProto boolKeyValueProto = getKeyValueProto(expectedKeys.get(1), "true", TransportProtos.KeyValueType.BOOLEAN_V); |
|||
TransportProtos.KeyValueProto dblKeyValueProto = getKeyValueProto(expectedKeys.get(2), "42.0", TransportProtos.KeyValueType.DOUBLE_V); |
|||
TransportProtos.KeyValueProto longKeyValueProto = getKeyValueProto(expectedKeys.get(3), "73", TransportProtos.KeyValueType.LONG_V); |
|||
TransportProtos.KeyValueProto jsonKeyValueProto = getKeyValueProto(expectedKeys.get(4), "{\"someNumber\": 42, \"someArray\": [1,2,3], \"someNestedObject\": {\"key\": \"value\"}}", TransportProtos.KeyValueType.JSON_V); |
|||
keyValueProtos.add(strKeyValueProto); |
|||
keyValueProtos.add(boolKeyValueProto); |
|||
keyValueProtos.add(dblKeyValueProto); |
|||
keyValueProtos.add(longKeyValueProto); |
|||
keyValueProtos.add(jsonKeyValueProto); |
|||
return keyValueProtos; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.attributes.request.nosql; |
|||
|
|||
import org.thingsboard.server.dao.service.DaoNoSqlTest; |
|||
import org.thingsboard.server.mqtt.attributes.request.AbstractMqttAttributesRequestIntegrationTest; |
|||
|
|||
|
|||
@DaoNoSqlTest |
|||
public class MqttAttributesRequestNoSqlIntegrationTest extends AbstractMqttAttributesRequestIntegrationTest { |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.attributes.request.sql; |
|||
|
|||
import org.thingsboard.server.dao.service.DaoSqlTest; |
|||
import org.thingsboard.server.mqtt.attributes.request.AbstractMqttAttributesRequestIntegrationTest; |
|||
import org.thingsboard.server.mqtt.attributes.request.AbstractMqttAttributesRequestJsonIntegrationTest; |
|||
|
|||
@DaoSqlTest |
|||
public class MqttAttributesRequestJsonSqlIntegrationTest extends AbstractMqttAttributesRequestJsonIntegrationTest { |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.attributes.request.sql; |
|||
|
|||
import org.thingsboard.server.dao.service.DaoSqlTest; |
|||
import org.thingsboard.server.mqtt.attributes.request.AbstractMqttAttributesRequestJsonIntegrationTest; |
|||
import org.thingsboard.server.mqtt.attributes.request.AbstractMqttAttributesRequestProtoIntegrationTest; |
|||
|
|||
@DaoSqlTest |
|||
public class MqttAttributesRequestProtoSqlIntegrationTest extends AbstractMqttAttributesRequestProtoIntegrationTest { |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.attributes.request.sql; |
|||
|
|||
import org.thingsboard.server.dao.service.DaoSqlTest; |
|||
import org.thingsboard.server.mqtt.attributes.request.AbstractMqttAttributesRequestIntegrationTest; |
|||
|
|||
@DaoSqlTest |
|||
public class MqttAttributesRequestSqlIntegrationTest extends AbstractMqttAttributesRequestIntegrationTest { |
|||
} |
|||
@ -0,0 +1,170 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.attributes.updates; |
|||
|
|||
import com.google.protobuf.InvalidProtocolBufferException; |
|||
import io.netty.handler.codec.mqtt.MqttQoS; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; |
|||
import org.eclipse.paho.client.mqttv3.MqttAsyncClient; |
|||
import org.eclipse.paho.client.mqttv3.MqttCallback; |
|||
import org.eclipse.paho.client.mqttv3.MqttException; |
|||
import org.eclipse.paho.client.mqttv3.MqttMessage; |
|||
import org.junit.After; |
|||
import org.junit.Before; |
|||
import org.junit.Test; |
|||
import org.thingsboard.server.common.data.Device; |
|||
import org.thingsboard.server.common.data.TransportPayloadType; |
|||
import org.thingsboard.server.common.data.device.profile.MqttTopics; |
|||
import org.thingsboard.server.dao.util.mapping.JacksonUtil; |
|||
import org.thingsboard.server.mqtt.attributes.AbstractMqttAttributesIntegrationTest; |
|||
|
|||
import java.nio.charset.StandardCharsets; |
|||
import java.util.concurrent.CountDownLatch; |
|||
import java.util.concurrent.TimeUnit; |
|||
|
|||
import static org.junit.Assert.assertEquals; |
|||
import static org.junit.Assert.assertNotNull; |
|||
import static org.junit.Assert.assertTrue; |
|||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
|||
|
|||
@Slf4j |
|||
public abstract class AbstractMqttAttributesUpdatesIntegrationTest extends AbstractMqttAttributesIntegrationTest { |
|||
|
|||
private static final String RESPONSE_ATTRIBUTES_PAYLOAD_DELETED = "{\"deleted\":[\"attribute5\"]}"; |
|||
|
|||
private static String getResponseGatewayAttributesUpdatedPayload() { |
|||
return "{\"device\":\"" + "Gateway Device Subscribe to attribute updates" + "\"," + |
|||
"\"data\":{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73,\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}}}"; |
|||
} |
|||
|
|||
private static String getResponseGatewayAttributesDeletedPayload() { |
|||
return "{\"device\":\"" + "Gateway Device Subscribe to attribute updates" + "\",\"data\":{\"deleted\":[\"attribute5\"]}}"; |
|||
} |
|||
|
|||
@Before |
|||
public void beforeTest() throws Exception { |
|||
processBeforeTest("Test Subscribe to attribute updates", "Gateway Test Subscribe to attribute updates", TransportPayloadType.JSON, null, null); |
|||
} |
|||
|
|||
@After |
|||
public void afterTest() throws Exception { |
|||
processAfterTest(); |
|||
} |
|||
|
|||
@Test |
|||
public void testSubscribeToAttributesUpdatesFromTheServer() throws Exception { |
|||
processTestSubscribeToAttributesUpdates(); |
|||
} |
|||
|
|||
@Test |
|||
public void testSubscribeToAttributesUpdatesFromTheServerGateway() throws Exception { |
|||
processGatewayTestSubscribeToAttributesUpdates(); |
|||
} |
|||
|
|||
protected void processTestSubscribeToAttributesUpdates() throws Exception { |
|||
|
|||
MqttAsyncClient client = getMqttAsyncClient(accessToken); |
|||
|
|||
TestMqttCallback onUpdateCallback = getTestMqttCallback(); |
|||
client.setCallback(onUpdateCallback); |
|||
|
|||
client.subscribe(MqttTopics.DEVICE_ATTRIBUTES_TOPIC, MqttQoS.AT_MOST_ONCE.value()); |
|||
|
|||
Thread.sleep(2000); |
|||
|
|||
doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk()); |
|||
onUpdateCallback.getLatch().await(3, TimeUnit.SECONDS); |
|||
|
|||
validateUpdateAttributesResponse(onUpdateCallback); |
|||
|
|||
TestMqttCallback onDeleteCallback = getTestMqttCallback(); |
|||
client.setCallback(onDeleteCallback); |
|||
|
|||
doDelete("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/SHARED_SCOPE?keys=attribute5", String.class); |
|||
onDeleteCallback.getLatch().await(3, TimeUnit.SECONDS); |
|||
|
|||
validateDeleteAttributesResponse(onDeleteCallback); |
|||
} |
|||
|
|||
protected void validateUpdateAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException { |
|||
assertNotNull(callback.getPayloadBytes()); |
|||
String response = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8); |
|||
assertEquals(JacksonUtil.toJsonNode(POST_ATTRIBUTES_PAYLOAD), JacksonUtil.toJsonNode(response)); |
|||
} |
|||
|
|||
protected void validateDeleteAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException { |
|||
assertNotNull(callback.getPayloadBytes()); |
|||
String response = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8); |
|||
assertEquals(JacksonUtil.toJsonNode(RESPONSE_ATTRIBUTES_PAYLOAD_DELETED), JacksonUtil.toJsonNode(response)); |
|||
} |
|||
|
|||
protected void processGatewayTestSubscribeToAttributesUpdates() throws Exception { |
|||
|
|||
MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); |
|||
|
|||
TestMqttCallback onUpdateCallback = getTestMqttCallback(); |
|||
client.setCallback(onUpdateCallback); |
|||
|
|||
Device device = new Device(); |
|||
device.setName("Gateway Device Subscribe to attribute updates"); |
|||
device.setType("default"); |
|||
|
|||
byte[] connectPayloadBytes = getConnectPayloadBytes(); |
|||
|
|||
publishMqttMsg(client, connectPayloadBytes, MqttTopics.GATEWAY_CONNECT_TOPIC); |
|||
|
|||
Thread.sleep(1000); |
|||
|
|||
Device savedDevice = doGet("/api/tenant/devices?deviceName=" + "Gateway Device Subscribe to attribute updates", Device.class); |
|||
assertNotNull(savedDevice); |
|||
|
|||
client.subscribe(MqttTopics.GATEWAY_ATTRIBUTES_TOPIC, MqttQoS.AT_MOST_ONCE.value()); |
|||
|
|||
Thread.sleep(2000); |
|||
|
|||
doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk()); |
|||
onUpdateCallback.getLatch().await(3, TimeUnit.SECONDS); |
|||
|
|||
validateGatewayUpdateAttributesResponse(onUpdateCallback); |
|||
|
|||
TestMqttCallback onDeleteCallback = getTestMqttCallback(); |
|||
client.setCallback(onDeleteCallback); |
|||
|
|||
doDelete("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/SHARED_SCOPE?keys=attribute5", String.class); |
|||
onDeleteCallback.getLatch().await(3, TimeUnit.SECONDS); |
|||
|
|||
validateGatewayDeleteAttributesResponse(onDeleteCallback); |
|||
|
|||
} |
|||
|
|||
protected void validateGatewayUpdateAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException { |
|||
assertNotNull(callback.getPayloadBytes()); |
|||
String s = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8); |
|||
assertEquals(getResponseGatewayAttributesUpdatedPayload(), s); |
|||
} |
|||
|
|||
protected void validateGatewayDeleteAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException { |
|||
assertNotNull(callback.getPayloadBytes()); |
|||
String s = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8); |
|||
assertEquals(s, getResponseGatewayAttributesDeletedPayload()); |
|||
} |
|||
|
|||
protected byte[] getConnectPayloadBytes() { |
|||
String connectPayload = "{\"device\": \"Gateway Device Subscribe to attribute updates\", \"type\": \"" + TransportPayloadType.JSON.name() + "\"}"; |
|||
return connectPayload.getBytes(); |
|||
} |
|||
} |
|||
@ -0,0 +1,51 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.attributes.updates; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.junit.After; |
|||
import org.junit.Before; |
|||
import org.junit.Test; |
|||
import org.thingsboard.server.common.data.TransportPayloadType; |
|||
|
|||
import static org.junit.Assert.assertEquals; |
|||
import static org.junit.Assert.assertFalse; |
|||
import static org.junit.Assert.assertNotNull; |
|||
import static org.junit.Assert.assertTrue; |
|||
|
|||
@Slf4j |
|||
public abstract class AbstractMqttAttributesUpdatesJsonIntegrationTest extends AbstractMqttAttributesUpdatesIntegrationTest { |
|||
|
|||
@Before |
|||
public void beforeTest() throws Exception { |
|||
processBeforeTest("Test Subscribe to attribute updates", "Gateway Test Subscribe to attribute updates", TransportPayloadType.JSON, null, null); |
|||
} |
|||
|
|||
@After |
|||
public void afterTest() throws Exception { |
|||
processAfterTest(); |
|||
} |
|||
|
|||
@Test |
|||
public void testSubscribeToAttributesUpdatesFromTheServer() throws Exception { |
|||
processTestSubscribeToAttributesUpdates(); |
|||
} |
|||
|
|||
@Test |
|||
public void testSubscribeToAttributesUpdatesFromTheServerGateway() throws Exception { |
|||
processGatewayTestSubscribeToAttributesUpdates(); |
|||
} |
|||
} |
|||
@ -0,0 +1,149 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.attributes.updates; |
|||
|
|||
import com.google.protobuf.InvalidProtocolBufferException; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.junit.After; |
|||
import org.junit.Before; |
|||
import org.junit.Test; |
|||
import org.thingsboard.server.common.data.TransportPayloadType; |
|||
import org.thingsboard.server.common.data.device.profile.MqttTopics; |
|||
import org.thingsboard.server.gen.transport.TransportApiProtos; |
|||
import org.thingsboard.server.gen.transport.TransportProtos; |
|||
|
|||
import java.nio.charset.StandardCharsets; |
|||
import java.util.List; |
|||
import java.util.stream.Collectors; |
|||
|
|||
import static org.junit.Assert.assertEquals; |
|||
import static org.junit.Assert.assertNotNull; |
|||
import static org.junit.Assert.assertTrue; |
|||
|
|||
@Slf4j |
|||
public abstract class AbstractMqttAttributesUpdatesProtoIntegrationTest extends AbstractMqttAttributesUpdatesIntegrationTest { |
|||
|
|||
@Before |
|||
public void beforeTest() throws Exception { |
|||
processBeforeTest("Test Subscribe to attribute updates", "Gateway Test Subscribe to attribute updates", TransportPayloadType.PROTOBUF, null, null); |
|||
} |
|||
|
|||
@After |
|||
public void afterTest() throws Exception { |
|||
processAfterTest(); |
|||
} |
|||
|
|||
@Test |
|||
public void testSubscribeToAttributesUpdatesFromTheServer() throws Exception { |
|||
processTestSubscribeToAttributesUpdates(); |
|||
} |
|||
|
|||
@Test |
|||
public void testSubscribeToAttributesUpdatesFromTheServerGateway() throws Exception { |
|||
processGatewayTestSubscribeToAttributesUpdates(); |
|||
} |
|||
|
|||
protected void validateUpdateAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException { |
|||
assertNotNull(callback.getPayloadBytes()); |
|||
TransportProtos.AttributeUpdateNotificationMsg.Builder attributeUpdateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder(); |
|||
List<TransportProtos.TsKvProto> tsKvProtoList = getTsKvProtoList(); |
|||
attributeUpdateNotificationMsgBuilder.addAllSharedUpdated(tsKvProtoList); |
|||
|
|||
TransportProtos.AttributeUpdateNotificationMsg expectedAttributeUpdateNotificationMsg = attributeUpdateNotificationMsgBuilder.build(); |
|||
TransportProtos.AttributeUpdateNotificationMsg actualAttributeUpdateNotificationMsg = TransportProtos.AttributeUpdateNotificationMsg.parseFrom(callback.getPayloadBytes()); |
|||
|
|||
List<TransportProtos.KeyValueProto> actualSharedUpdatedList = actualAttributeUpdateNotificationMsg.getSharedUpdatedList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); |
|||
List<TransportProtos.KeyValueProto> expectedSharedUpdatedList = expectedAttributeUpdateNotificationMsg.getSharedUpdatedList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); |
|||
|
|||
assertEquals(expectedSharedUpdatedList.size(), actualSharedUpdatedList.size()); |
|||
assertTrue(actualSharedUpdatedList.containsAll(expectedSharedUpdatedList)); |
|||
|
|||
} |
|||
|
|||
protected void validateDeleteAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException { |
|||
assertNotNull(callback.getPayloadBytes()); |
|||
TransportProtos.AttributeUpdateNotificationMsg.Builder attributeUpdateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder(); |
|||
attributeUpdateNotificationMsgBuilder.addSharedDeleted("attribute5"); |
|||
|
|||
TransportProtos.AttributeUpdateNotificationMsg expectedAttributeUpdateNotificationMsg = attributeUpdateNotificationMsgBuilder.build(); |
|||
TransportProtos.AttributeUpdateNotificationMsg actualAttributeUpdateNotificationMsg = TransportProtos.AttributeUpdateNotificationMsg.parseFrom(callback.getPayloadBytes()); |
|||
|
|||
assertEquals(expectedAttributeUpdateNotificationMsg.getSharedDeletedList().size(), actualAttributeUpdateNotificationMsg.getSharedDeletedList().size()); |
|||
assertEquals("attribute5", actualAttributeUpdateNotificationMsg.getSharedDeletedList().get(0)); |
|||
|
|||
} |
|||
|
|||
protected void validateGatewayUpdateAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException { |
|||
assertNotNull(callback.getPayloadBytes()); |
|||
|
|||
TransportProtos.AttributeUpdateNotificationMsg.Builder attributeUpdateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder(); |
|||
List<TransportProtos.TsKvProto> tsKvProtoList = getTsKvProtoList(); |
|||
attributeUpdateNotificationMsgBuilder.addAllSharedUpdated(tsKvProtoList); |
|||
TransportProtos.AttributeUpdateNotificationMsg expectedAttributeUpdateNotificationMsg = attributeUpdateNotificationMsgBuilder.build(); |
|||
|
|||
TransportApiProtos.GatewayAttributeUpdateNotificationMsg.Builder gatewayAttributeUpdateNotificationMsgBuilder = TransportApiProtos.GatewayAttributeUpdateNotificationMsg.newBuilder(); |
|||
gatewayAttributeUpdateNotificationMsgBuilder.setDeviceName("Gateway Device Subscribe to attribute updates"); |
|||
gatewayAttributeUpdateNotificationMsgBuilder.setNotificationMsg(expectedAttributeUpdateNotificationMsg); |
|||
|
|||
TransportApiProtos.GatewayAttributeUpdateNotificationMsg expectedGatewayAttributeUpdateNotificationMsg = gatewayAttributeUpdateNotificationMsgBuilder.build(); |
|||
TransportApiProtos.GatewayAttributeUpdateNotificationMsg actualGatewayAttributeUpdateNotificationMsg = TransportApiProtos.GatewayAttributeUpdateNotificationMsg.parseFrom(callback.getPayloadBytes()); |
|||
|
|||
assertEquals(expectedGatewayAttributeUpdateNotificationMsg.getDeviceName(), actualGatewayAttributeUpdateNotificationMsg.getDeviceName()); |
|||
|
|||
List<TransportProtos.KeyValueProto> actualSharedUpdatedList = actualGatewayAttributeUpdateNotificationMsg.getNotificationMsg().getSharedUpdatedList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); |
|||
List<TransportProtos.KeyValueProto> expectedSharedUpdatedList = expectedGatewayAttributeUpdateNotificationMsg.getNotificationMsg().getSharedUpdatedList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); |
|||
|
|||
assertEquals(expectedSharedUpdatedList.size(), actualSharedUpdatedList.size()); |
|||
assertTrue(actualSharedUpdatedList.containsAll(expectedSharedUpdatedList)); |
|||
|
|||
} |
|||
|
|||
protected void validateGatewayDeleteAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException { |
|||
assertNotNull(callback.getPayloadBytes()); |
|||
TransportProtos.AttributeUpdateNotificationMsg.Builder attributeUpdateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder(); |
|||
attributeUpdateNotificationMsgBuilder.addSharedDeleted("attribute5"); |
|||
TransportProtos.AttributeUpdateNotificationMsg attributeUpdateNotificationMsg = attributeUpdateNotificationMsgBuilder.build(); |
|||
|
|||
TransportApiProtos.GatewayAttributeUpdateNotificationMsg.Builder gatewayAttributeUpdateNotificationMsgBuilder = TransportApiProtos.GatewayAttributeUpdateNotificationMsg.newBuilder(); |
|||
gatewayAttributeUpdateNotificationMsgBuilder.setDeviceName("Gateway Device Subscribe to attribute updates"); |
|||
gatewayAttributeUpdateNotificationMsgBuilder.setNotificationMsg(attributeUpdateNotificationMsg); |
|||
|
|||
TransportApiProtos.GatewayAttributeUpdateNotificationMsg expectedGatewayAttributeUpdateNotificationMsg = gatewayAttributeUpdateNotificationMsgBuilder.build(); |
|||
TransportApiProtos.GatewayAttributeUpdateNotificationMsg actualGatewayAttributeUpdateNotificationMsg = TransportApiProtos.GatewayAttributeUpdateNotificationMsg.parseFrom(callback.getPayloadBytes()); |
|||
|
|||
assertEquals(expectedGatewayAttributeUpdateNotificationMsg.getDeviceName(), actualGatewayAttributeUpdateNotificationMsg.getDeviceName()); |
|||
|
|||
TransportProtos.AttributeUpdateNotificationMsg expectedAttributeUpdateNotificationMsg = expectedGatewayAttributeUpdateNotificationMsg.getNotificationMsg(); |
|||
TransportProtos.AttributeUpdateNotificationMsg actualAttributeUpdateNotificationMsg = actualGatewayAttributeUpdateNotificationMsg.getNotificationMsg(); |
|||
|
|||
assertEquals(expectedAttributeUpdateNotificationMsg.getSharedDeletedList().size(), actualAttributeUpdateNotificationMsg.getSharedDeletedList().size()); |
|||
assertEquals("attribute5", actualAttributeUpdateNotificationMsg.getSharedDeletedList().get(0)); |
|||
|
|||
} |
|||
|
|||
protected byte[] getConnectPayloadBytes() { |
|||
TransportApiProtos.ConnectMsg connectProto = getConnectProto(); |
|||
return connectProto.toByteArray(); |
|||
} |
|||
|
|||
private TransportApiProtos.ConnectMsg getConnectProto() { |
|||
TransportApiProtos.ConnectMsg.Builder builder = TransportApiProtos.ConnectMsg.newBuilder(); |
|||
builder.setDeviceName("Gateway Device Subscribe to attribute updates"); |
|||
builder.setDeviceType(TransportPayloadType.PROTOBUF.name()); |
|||
return builder.build(); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.attributes.updates.nosql; |
|||
|
|||
import org.thingsboard.server.dao.service.DaoNoSqlTest; |
|||
import org.thingsboard.server.mqtt.attributes.updates.AbstractMqttAttributesUpdatesJsonIntegrationTest; |
|||
|
|||
|
|||
@DaoNoSqlTest |
|||
public class MqttAttributesUpdatesNoSqlIntegrationTest extends AbstractMqttAttributesUpdatesJsonIntegrationTest { |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.attributes.updates.sql; |
|||
|
|||
import org.thingsboard.server.dao.service.DaoSqlTest; |
|||
import org.thingsboard.server.mqtt.attributes.updates.AbstractMqttAttributesUpdatesIntegrationTest; |
|||
|
|||
@DaoSqlTest |
|||
public class MqttAttributesUpdatesSqlIntegrationTest extends AbstractMqttAttributesUpdatesIntegrationTest { |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.attributes.updates.sql; |
|||
|
|||
import org.thingsboard.server.dao.service.DaoSqlTest; |
|||
import org.thingsboard.server.mqtt.attributes.updates.AbstractMqttAttributesUpdatesIntegrationTest; |
|||
import org.thingsboard.server.mqtt.attributes.updates.AbstractMqttAttributesUpdatesJsonIntegrationTest; |
|||
|
|||
@DaoSqlTest |
|||
public class MqttAttributesUpdatesSqlJsonIntegrationTest extends AbstractMqttAttributesUpdatesJsonIntegrationTest { |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.attributes.updates.sql; |
|||
|
|||
import org.thingsboard.server.dao.service.DaoSqlTest; |
|||
import org.thingsboard.server.mqtt.attributes.updates.AbstractMqttAttributesUpdatesJsonIntegrationTest; |
|||
import org.thingsboard.server.mqtt.attributes.updates.AbstractMqttAttributesUpdatesProtoIntegrationTest; |
|||
|
|||
@DaoSqlTest |
|||
public class MqttAttributesUpdatesSqlProtoIntegrationTest extends AbstractMqttAttributesUpdatesProtoIntegrationTest { |
|||
} |
|||
@ -0,0 +1,194 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.claim; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.eclipse.paho.client.mqttv3.MqttAsyncClient; |
|||
import org.eclipse.paho.client.mqttv3.MqttMessage; |
|||
import org.junit.After; |
|||
import org.junit.Before; |
|||
import org.junit.Test; |
|||
import org.thingsboard.server.common.data.ClaimRequest; |
|||
import org.thingsboard.server.common.data.Customer; |
|||
import org.thingsboard.server.common.data.Device; |
|||
import org.thingsboard.server.common.data.User; |
|||
import org.thingsboard.server.common.data.device.profile.MqttTopics; |
|||
import org.thingsboard.server.common.data.security.Authority; |
|||
import org.thingsboard.server.dao.device.claim.ClaimResponse; |
|||
import org.thingsboard.server.dao.device.claim.ClaimResult; |
|||
import org.thingsboard.server.mqtt.AbstractMqttIntegrationTest; |
|||
|
|||
import static org.junit.Assert.assertEquals; |
|||
import static org.junit.Assert.assertNotNull; |
|||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
|||
|
|||
@Slf4j |
|||
public abstract class AbstractMqttClaimDeviceTest extends AbstractMqttIntegrationTest { |
|||
|
|||
protected static final String CUSTOMER_USER_PASSWORD = "customerUser123!"; |
|||
|
|||
protected User customerAdmin; |
|||
protected Customer savedCustomer; |
|||
|
|||
@Before |
|||
public void beforeTest() throws Exception { |
|||
super.processBeforeTest("Test Claim device", "Test Claim gateway", null, null, null); |
|||
createCustomerAndUser(); |
|||
} |
|||
|
|||
protected void createCustomerAndUser() throws Exception { |
|||
Customer customer = new Customer(); |
|||
customer.setTenantId(savedTenant.getId()); |
|||
customer.setTitle("Test Claiming Customer"); |
|||
savedCustomer = doPost("/api/customer", customer, Customer.class); |
|||
assertNotNull(savedCustomer); |
|||
assertEquals(savedTenant.getId(), savedCustomer.getTenantId()); |
|||
|
|||
User user = new User(); |
|||
user.setAuthority(Authority.CUSTOMER_USER); |
|||
user.setTenantId(savedTenant.getId()); |
|||
user.setCustomerId(savedCustomer.getId()); |
|||
user.setEmail("customer@thingsboard.org"); |
|||
|
|||
customerAdmin = createUser(user, CUSTOMER_USER_PASSWORD); |
|||
assertNotNull(customerAdmin); |
|||
assertEquals(customerAdmin.getCustomerId(), savedCustomer.getId()); |
|||
} |
|||
|
|||
@After |
|||
public void afterTest() throws Exception { |
|||
super.processAfterTest(); |
|||
} |
|||
|
|||
@Test |
|||
public void testClaimingDevice() throws Exception { |
|||
processTestClaimingDevice(false); |
|||
} |
|||
|
|||
@Test |
|||
public void testClaimingDeviceWithoutSecretAndDuration() throws Exception { |
|||
processTestClaimingDevice(true); |
|||
} |
|||
|
|||
@Test |
|||
public void testGatewayClaimingDevice() throws Exception { |
|||
processTestGatewayClaimingDevice("Test claiming gateway device", false); |
|||
} |
|||
|
|||
@Test |
|||
public void testGatewayClaimingDeviceWithoutSecretAndDuration() throws Exception { |
|||
processTestGatewayClaimingDevice("Test claiming gateway device empty payload", true); |
|||
} |
|||
|
|||
|
|||
protected void processTestClaimingDevice(boolean emptyPayload) throws Exception { |
|||
MqttAsyncClient client = getMqttAsyncClient(accessToken); |
|||
byte[] payloadBytes; |
|||
byte[] failurePayloadBytes; |
|||
if (emptyPayload) { |
|||
payloadBytes = "{}".getBytes(); |
|||
failurePayloadBytes = "{\"durationMs\":1}".getBytes(); |
|||
} else { |
|||
payloadBytes = "{\"secretKey\":\"value\", \"durationMs\":60000}".getBytes(); |
|||
failurePayloadBytes = "{\"secretKey\":\"value\", \"durationMs\":1}".getBytes(); |
|||
} |
|||
validateClaimResponse(emptyPayload, client, payloadBytes, failurePayloadBytes); |
|||
} |
|||
|
|||
protected void validateClaimResponse(boolean emptyPayload, MqttAsyncClient client, byte[] payloadBytes, byte[] failurePayloadBytes) throws Exception { |
|||
client.publish(MqttTopics.DEVICE_CLAIM_TOPIC, new MqttMessage(failurePayloadBytes)); |
|||
|
|||
Thread.sleep(2000); |
|||
|
|||
loginUser(customerAdmin.getName(), CUSTOMER_USER_PASSWORD); |
|||
ClaimRequest claimRequest; |
|||
if (!emptyPayload) { |
|||
claimRequest = new ClaimRequest("value"); |
|||
} else { |
|||
claimRequest = new ClaimRequest(null); |
|||
} |
|||
|
|||
ClaimResponse claimResponse = doPostClaimAsync("/api/customer/device/" + savedDevice.getName() + "/claim", claimRequest, ClaimResponse.class, status().isBadRequest()); |
|||
assertEquals(claimResponse, ClaimResponse.FAILURE); |
|||
|
|||
client.publish(MqttTopics.DEVICE_CLAIM_TOPIC, new MqttMessage(payloadBytes)); |
|||
|
|||
Thread.sleep(2000); |
|||
|
|||
ClaimResult claimResult = doPostClaimAsync("/api/customer/device/" + savedDevice.getName() + "/claim", claimRequest, ClaimResult.class, status().isOk()); |
|||
assertEquals(claimResult.getResponse(), ClaimResponse.SUCCESS); |
|||
Device claimedDevice = claimResult.getDevice(); |
|||
assertNotNull(claimedDevice); |
|||
assertNotNull(claimedDevice.getCustomerId()); |
|||
assertEquals(customerAdmin.getCustomerId(), claimedDevice.getCustomerId()); |
|||
|
|||
claimResponse = doPostClaimAsync("/api/customer/device/" + savedDevice.getName() + "/claim", claimRequest, ClaimResponse.class, status().isBadRequest()); |
|||
assertEquals(claimResponse, ClaimResponse.CLAIMED); |
|||
} |
|||
|
|||
protected void validateGatewayClaimResponse(String deviceName, boolean emptyPayload, MqttAsyncClient client, byte[] failurePayloadBytes, byte[] payloadBytes) throws Exception { |
|||
client.publish(MqttTopics.GATEWAY_CLAIM_TOPIC, new MqttMessage(failurePayloadBytes)); |
|||
|
|||
Thread.sleep(2000); |
|||
|
|||
Device savedDevice = doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class); |
|||
assertNotNull(savedDevice); |
|||
|
|||
loginUser(customerAdmin.getName(), CUSTOMER_USER_PASSWORD); |
|||
ClaimRequest claimRequest; |
|||
if (!emptyPayload) { |
|||
claimRequest = new ClaimRequest("value"); |
|||
} else { |
|||
claimRequest = new ClaimRequest(null); |
|||
} |
|||
|
|||
ClaimResponse claimResponse = doPostClaimAsync("/api/customer/device/" + deviceName + "/claim", claimRequest, ClaimResponse.class, status().isBadRequest()); |
|||
assertEquals(claimResponse, ClaimResponse.FAILURE); |
|||
|
|||
client.publish(MqttTopics.GATEWAY_CLAIM_TOPIC, new MqttMessage(payloadBytes)); |
|||
|
|||
Thread.sleep(2000); |
|||
|
|||
ClaimResult claimResult = doPostClaimAsync("/api/customer/device/" + deviceName + "/claim", claimRequest, ClaimResult.class, status().isOk()); |
|||
assertEquals(claimResult.getResponse(), ClaimResponse.SUCCESS); |
|||
Device claimedDevice = claimResult.getDevice(); |
|||
assertNotNull(claimedDevice); |
|||
assertNotNull(claimedDevice.getCustomerId()); |
|||
assertEquals(customerAdmin.getCustomerId(), claimedDevice.getCustomerId()); |
|||
|
|||
claimResponse = doPostClaimAsync("/api/customer/device/" + deviceName + "/claim", claimRequest, ClaimResponse.class, status().isBadRequest()); |
|||
assertEquals(claimResponse, ClaimResponse.CLAIMED); |
|||
} |
|||
|
|||
protected void processTestGatewayClaimingDevice(String deviceName, boolean emptyPayload) throws Exception { |
|||
MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); |
|||
byte[] failurePayloadBytes; |
|||
byte[] payloadBytes; |
|||
String failurePayload; |
|||
String payload; |
|||
if (emptyPayload) { |
|||
failurePayload = "{\"" + deviceName + "\": " + "{\"durationMs\":1}" + "}"; |
|||
payload = "{\"" + deviceName + "\": " + "{}" + "}"; |
|||
} else { |
|||
failurePayload = "{\"" + deviceName + "\": " + "{\"secretKey\":\"value\", \"durationMs\":1}" + "}"; |
|||
payload = "{\"" + deviceName + "\": " + "{\"secretKey\":\"value\", \"durationMs\":60000}" + "}"; |
|||
} |
|||
payloadBytes = payload.getBytes(); |
|||
failurePayloadBytes = failurePayload.getBytes(); |
|||
validateGatewayClaimResponse(deviceName, emptyPayload, client, failurePayloadBytes, payloadBytes); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,57 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.claim; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.junit.After; |
|||
import org.junit.Before; |
|||
import org.junit.Test; |
|||
import org.thingsboard.server.common.data.TransportPayloadType; |
|||
|
|||
@Slf4j |
|||
public abstract class AbstractMqttClaimJsonDeviceTest extends AbstractMqttClaimDeviceTest { |
|||
|
|||
@Before |
|||
public void beforeTest() throws Exception { |
|||
super.processBeforeTest("Test Claim device", "Test Claim gateway", TransportPayloadType.JSON, null, null); |
|||
createCustomerAndUser(); |
|||
} |
|||
|
|||
@After |
|||
public void afterTest() throws Exception { |
|||
super.afterTest(); |
|||
} |
|||
|
|||
@Test |
|||
public void testClaimingDevice() throws Exception { |
|||
processTestClaimingDevice(false); |
|||
} |
|||
|
|||
@Test |
|||
public void testClaimingDeviceWithoutSecretAndDuration() throws Exception { |
|||
processTestClaimingDevice(true); |
|||
} |
|||
|
|||
@Test |
|||
public void testGatewayClaimingDevice() throws Exception { |
|||
processTestGatewayClaimingDevice("Test claiming gateway device Json", false); |
|||
} |
|||
|
|||
@Test |
|||
public void testGatewayClaimingDeviceWithoutSecretAndDuration() throws Exception { |
|||
processTestGatewayClaimingDevice("Test claiming gateway device empty payload Json", true); |
|||
} |
|||
} |
|||
@ -0,0 +1,115 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.claim; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.eclipse.paho.client.mqttv3.MqttAsyncClient; |
|||
import org.junit.After; |
|||
import org.junit.Before; |
|||
import org.junit.Test; |
|||
import org.thingsboard.server.common.data.TransportPayloadType; |
|||
import org.thingsboard.server.gen.transport.TransportApiProtos; |
|||
|
|||
@Slf4j |
|||
public abstract class AbstractMqttClaimProtoDeviceTest extends AbstractMqttClaimDeviceTest { |
|||
|
|||
@Before |
|||
public void beforeTest() throws Exception { |
|||
processBeforeTest("Test Claim device", "Test Claim gateway", TransportPayloadType.PROTOBUF, null, null); |
|||
createCustomerAndUser(); |
|||
} |
|||
|
|||
@After |
|||
public void afterTest() throws Exception { super.afterTest(); } |
|||
|
|||
@Test |
|||
public void testClaimingDevice() throws Exception { |
|||
processTestClaimingDevice(false); |
|||
} |
|||
|
|||
@Test |
|||
public void testClaimingDeviceWithoutSecretAndDuration() throws Exception { |
|||
processTestClaimingDevice(true); |
|||
} |
|||
|
|||
@Test |
|||
public void testGatewayClaimingDevice() throws Exception { |
|||
processTestGatewayClaimingDevice("Test claiming gateway device Proto", false); |
|||
} |
|||
|
|||
@Test |
|||
public void testGatewayClaimingDeviceWithoutSecretAndDuration() throws Exception { |
|||
processTestGatewayClaimingDevice("Test claiming gateway device empty payload Proto", true); |
|||
} |
|||
|
|||
protected void processTestClaimingDevice(boolean emptyPayload) throws Exception { |
|||
MqttAsyncClient client = getMqttAsyncClient(accessToken); |
|||
byte[] payloadBytes; |
|||
if (emptyPayload) { |
|||
payloadBytes = getClaimDevice(0, emptyPayload).toByteArray(); |
|||
} else { |
|||
payloadBytes = getClaimDevice(60000, emptyPayload).toByteArray(); |
|||
} |
|||
byte[] failurePayloadBytes = getClaimDevice(1, emptyPayload).toByteArray(); |
|||
validateClaimResponse(emptyPayload, client, payloadBytes, failurePayloadBytes); |
|||
} |
|||
|
|||
protected void processTestGatewayClaimingDevice(String deviceName, boolean emptyPayload) throws Exception { |
|||
MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); |
|||
byte[] failurePayloadBytes; |
|||
byte[] payloadBytes; |
|||
if (emptyPayload) { |
|||
payloadBytes = getGatewayClaimMsg(deviceName, 0, emptyPayload).toByteArray(); |
|||
} else { |
|||
payloadBytes = getGatewayClaimMsg(deviceName, 60000, emptyPayload).toByteArray(); |
|||
} |
|||
failurePayloadBytes = getGatewayClaimMsg(deviceName, 1, emptyPayload).toByteArray(); |
|||
|
|||
validateGatewayClaimResponse(deviceName, emptyPayload, client, failurePayloadBytes, payloadBytes); |
|||
} |
|||
|
|||
private TransportApiProtos.GatewayClaimMsg getGatewayClaimMsg(String deviceName, long duration, boolean emptyPayload) { |
|||
TransportApiProtos.GatewayClaimMsg.Builder gatewayClaimMsgBuilder = TransportApiProtos.GatewayClaimMsg.newBuilder(); |
|||
TransportApiProtos.ClaimDeviceMsg.Builder claimDeviceMsgBuilder = TransportApiProtos.ClaimDeviceMsg.newBuilder(); |
|||
TransportApiProtos.ClaimDevice.Builder claimDeviceBuilder = TransportApiProtos.ClaimDevice.newBuilder(); |
|||
if (!emptyPayload) { |
|||
claimDeviceBuilder.setSecretKey("value"); |
|||
} |
|||
if (duration > 0) { |
|||
claimDeviceBuilder.setDurationMs(duration); |
|||
} |
|||
TransportApiProtos.ClaimDevice claimDevice = claimDeviceBuilder.build(); |
|||
claimDeviceMsgBuilder.setClaimRequest(claimDevice); |
|||
claimDeviceMsgBuilder.setDeviceName(deviceName); |
|||
TransportApiProtos.ClaimDeviceMsg claimDeviceMsg = claimDeviceMsgBuilder.build(); |
|||
gatewayClaimMsgBuilder.addMsg(claimDeviceMsg); |
|||
return gatewayClaimMsgBuilder.build(); |
|||
} |
|||
|
|||
private TransportApiProtos.ClaimDevice getClaimDevice(long duration, boolean emptyPayload) { |
|||
TransportApiProtos.ClaimDevice.Builder claimDeviceBuilder = TransportApiProtos.ClaimDevice.newBuilder(); |
|||
if (!emptyPayload) { |
|||
claimDeviceBuilder.setSecretKey("value"); |
|||
} |
|||
if (duration > 0) { |
|||
claimDeviceBuilder.setSecretKey("value"); |
|||
claimDeviceBuilder.setDurationMs(duration); |
|||
} |
|||
return claimDeviceBuilder.build(); |
|||
} |
|||
|
|||
|
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.claim.nosql; |
|||
|
|||
import org.thingsboard.server.dao.service.DaoNoSqlTest; |
|||
import org.thingsboard.server.mqtt.claim.AbstractMqttClaimDeviceTest; |
|||
|
|||
|
|||
@DaoNoSqlTest |
|||
public class MqttClaimDeviceNoSqlTest extends AbstractMqttClaimDeviceTest { |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.claim.sql; |
|||
|
|||
import org.thingsboard.server.dao.service.DaoSqlTest; |
|||
import org.thingsboard.server.mqtt.claim.AbstractMqttClaimDeviceTest; |
|||
import org.thingsboard.server.mqtt.claim.AbstractMqttClaimJsonDeviceTest; |
|||
|
|||
@DaoSqlTest |
|||
public class MqttClaimDeviceJsonSqlTest extends AbstractMqttClaimJsonDeviceTest { |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.claim.sql; |
|||
|
|||
import org.thingsboard.server.dao.service.DaoSqlTest; |
|||
import org.thingsboard.server.mqtt.claim.AbstractMqttClaimJsonDeviceTest; |
|||
import org.thingsboard.server.mqtt.claim.AbstractMqttClaimProtoDeviceTest; |
|||
|
|||
@DaoSqlTest |
|||
public class MqttClaimDeviceProtoSqlTest extends AbstractMqttClaimProtoDeviceTest { |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.claim.sql; |
|||
|
|||
import org.thingsboard.server.dao.service.DaoSqlTest; |
|||
import org.thingsboard.server.mqtt.claim.AbstractMqttClaimDeviceTest; |
|||
|
|||
@DaoSqlTest |
|||
public class MqttClaimDeviceSqlTest extends AbstractMqttClaimDeviceTest { |
|||
} |
|||
@ -0,0 +1,137 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.rpc; |
|||
|
|||
import com.fasterxml.jackson.databind.JsonNode; |
|||
import com.fasterxml.jackson.databind.node.ObjectNode; |
|||
import com.google.protobuf.InvalidProtocolBufferException; |
|||
import com.nimbusds.jose.util.StandardCharset; |
|||
import com.datastax.oss.driver.api.core.uuid.Uuids; |
|||
import io.netty.handler.codec.mqtt.MqttQoS; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; |
|||
import org.eclipse.paho.client.mqttv3.MqttAsyncClient; |
|||
import org.eclipse.paho.client.mqttv3.MqttCallback; |
|||
import org.eclipse.paho.client.mqttv3.MqttConnectOptions; |
|||
import org.eclipse.paho.client.mqttv3.MqttException; |
|||
import org.eclipse.paho.client.mqttv3.MqttMessage; |
|||
import org.junit.After; |
|||
import org.junit.Assert; |
|||
import org.junit.Before; |
|||
import org.junit.Ignore; |
|||
import org.junit.Test; |
|||
import org.thingsboard.server.common.data.Device; |
|||
import org.thingsboard.server.common.data.DeviceProfile; |
|||
import org.thingsboard.server.common.data.DeviceProfileType; |
|||
import org.thingsboard.server.common.data.DeviceTransportType; |
|||
import org.thingsboard.server.common.data.Tenant; |
|||
import org.thingsboard.server.common.data.TransportPayloadType; |
|||
import org.thingsboard.server.common.data.User; |
|||
import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; |
|||
import org.thingsboard.server.common.data.device.profile.DeviceProfileData; |
|||
import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration; |
|||
import org.thingsboard.server.common.data.device.profile.MqttTopics; |
|||
import org.thingsboard.server.common.data.security.Authority; |
|||
import org.thingsboard.server.common.data.security.DeviceCredentials; |
|||
import org.thingsboard.server.controller.AbstractControllerTest; |
|||
import org.thingsboard.server.dao.util.mapping.JacksonUtil; |
|||
import org.thingsboard.server.service.security.AccessValidator; |
|||
|
|||
import java.util.Arrays; |
|||
import java.util.concurrent.CountDownLatch; |
|||
import java.util.concurrent.TimeUnit; |
|||
import java.util.concurrent.atomic.AtomicInteger; |
|||
|
|||
import static org.junit.Assert.assertEquals; |
|||
import static org.junit.Assert.assertNotNull; |
|||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
|||
|
|||
/** |
|||
* @author Valerii Sosliuk |
|||
*/ |
|||
@Slf4j |
|||
public abstract class AbstractMqttServerSideRpcDefaultIntegrationTest extends AbstractMqttServerSideRpcIntegrationTest { |
|||
|
|||
@Before |
|||
public void beforeTest() throws Exception { |
|||
processBeforeTest("RPC test device", "RPC test gateway", null, null, null); |
|||
} |
|||
|
|||
@After |
|||
public void afterTest() throws Exception { |
|||
super.processAfterTest(); |
|||
} |
|||
|
|||
@Test |
|||
public void testServerMqttOneWayRpcDeviceOffline() throws Exception { |
|||
String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"24\",\"value\": 1},\"timeout\": 6000}"; |
|||
String deviceId = savedDevice.getId().getId().toString(); |
|||
|
|||
doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().is(409), |
|||
asyncContextTimeoutToUseRpcPlugin); |
|||
} |
|||
|
|||
@Test |
|||
public void testServerMqttOneWayRpcDeviceDoesNotExist() throws Exception { |
|||
String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"25\",\"value\": 1}}"; |
|||
String nonExistentDeviceId = Uuids.timeBased().toString(); |
|||
|
|||
String result = doPostAsync("/api/plugins/rpc/oneway/" + nonExistentDeviceId, setGpioRequest, String.class, |
|||
status().isNotFound()); |
|||
Assert.assertEquals(AccessValidator.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result); |
|||
} |
|||
|
|||
@Test |
|||
public void testServerMqttTwoWayRpcDeviceOffline() throws Exception { |
|||
String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"27\",\"value\": 1},\"timeout\": 6000}"; |
|||
String deviceId = savedDevice.getId().getId().toString(); |
|||
|
|||
doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().is(409), |
|||
asyncContextTimeoutToUseRpcPlugin); |
|||
} |
|||
|
|||
@Test |
|||
public void testServerMqttTwoWayRpcDeviceDoesNotExist() throws Exception { |
|||
String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"28\",\"value\": 1}}"; |
|||
String nonExistentDeviceId = Uuids.timeBased().toString(); |
|||
|
|||
String result = doPostAsync("/api/plugins/rpc/twoway/" + nonExistentDeviceId, setGpioRequest, String.class, |
|||
status().isNotFound()); |
|||
Assert.assertEquals(AccessValidator.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result); |
|||
} |
|||
|
|||
@Test |
|||
public void testServerMqttOneWayRpc() throws Exception { |
|||
processOneWayRpcTest(); |
|||
} |
|||
|
|||
@Test |
|||
public void testServerMqttTwoWayRpc() throws Exception { |
|||
processTwoWayRpcTest(); |
|||
} |
|||
|
|||
@Test |
|||
public void testGatewayServerMqttOneWayRpc() throws Exception { |
|||
processOneWayRpcTestGateway("Gateway Device OneWay RPC"); |
|||
} |
|||
|
|||
@Test |
|||
public void testGatewayServerMqttTwoWayRpc() throws Exception { |
|||
processTwoWayRpcTestGateway("Gateway Device TwoWay RPC"); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,66 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.rpc; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.eclipse.paho.client.mqttv3.MqttAsyncClient; |
|||
import org.junit.After; |
|||
import org.junit.Before; |
|||
import org.junit.Ignore; |
|||
import org.junit.Test; |
|||
import org.thingsboard.server.common.data.TransportPayloadType; |
|||
|
|||
@Slf4j |
|||
public abstract class AbstractMqttServerSideRpcJsonIntegrationTest extends AbstractMqttServerSideRpcIntegrationTest { |
|||
|
|||
@Before |
|||
public void beforeTest() throws Exception { |
|||
processBeforeTest("RPC test device", "RPC test gateway", TransportPayloadType.JSON, null, null); |
|||
} |
|||
|
|||
@After |
|||
public void afterTest() throws Exception { |
|||
super.processAfterTest(); |
|||
} |
|||
|
|||
@Test |
|||
public void testServerMqttOneWayRpc() throws Exception { |
|||
processOneWayRpcTest(); |
|||
} |
|||
|
|||
@Test |
|||
public void testServerMqttTwoWayRpc() throws Exception { |
|||
processTwoWayRpcTest(); |
|||
} |
|||
|
|||
@Test |
|||
public void testGatewayServerMqttOneWayRpc() throws Exception { |
|||
processOneWayRpcTestGateway("Gateway Device OneWay RPC Json"); |
|||
} |
|||
|
|||
@Test |
|||
public void testGatewayServerMqttTwoWayRpc() throws Exception { |
|||
processTwoWayRpcTestGateway("Gateway Device TwoWay RPC Json"); |
|||
} |
|||
|
|||
protected void processOneWayRpcTestGateway(String deviceName) throws Exception { |
|||
MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); |
|||
String payload = "{\"device\": \"" + deviceName + "\", \"type\": \"" + TransportPayloadType.JSON.name() + "\"}"; |
|||
byte[] payloadBytes = payload.getBytes(); |
|||
validateOneWayRpcGatewayResponse(deviceName, client, payloadBytes); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,114 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.rpc; |
|||
|
|||
import com.google.protobuf.InvalidProtocolBufferException; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.eclipse.paho.client.mqttv3.MqttAsyncClient; |
|||
import org.eclipse.paho.client.mqttv3.MqttException; |
|||
import org.eclipse.paho.client.mqttv3.MqttMessage; |
|||
import org.junit.After; |
|||
import org.junit.Before; |
|||
import org.junit.Ignore; |
|||
import org.junit.Test; |
|||
import org.thingsboard.server.common.data.TransportPayloadType; |
|||
import org.thingsboard.server.common.data.device.profile.MqttTopics; |
|||
import org.thingsboard.server.gen.transport.TransportApiProtos; |
|||
import org.thingsboard.server.gen.transport.TransportProtos; |
|||
|
|||
import static org.junit.Assert.assertEquals; |
|||
import static org.junit.Assert.assertNotNull; |
|||
|
|||
@Slf4j |
|||
public abstract class AbstractMqttServerSideRpcProtoIntegrationTest extends AbstractMqttServerSideRpcIntegrationTest { |
|||
|
|||
@Before |
|||
public void beforeTest() throws Exception { |
|||
processBeforeTest("RPC test device", "RPC test gateway", TransportPayloadType.PROTOBUF, null, null); |
|||
} |
|||
|
|||
@After |
|||
public void afterTest() throws Exception { |
|||
super.processAfterTest(); |
|||
} |
|||
|
|||
@Test |
|||
public void testServerMqttOneWayRpc() throws Exception { |
|||
processOneWayRpcTest(); |
|||
} |
|||
|
|||
@Test |
|||
public void testServerMqttTwoWayRpc() throws Exception { |
|||
processTwoWayRpcTest(); |
|||
} |
|||
|
|||
@Test |
|||
public void testGatewayServerMqttOneWayRpc() throws Exception { |
|||
processOneWayRpcTestGateway("Gateway Device OneWay RPC Proto"); |
|||
} |
|||
|
|||
@Test |
|||
public void testGatewayServerMqttTwoWayRpc() throws Exception { |
|||
processTwoWayRpcTestGateway("Gateway Device TwoWay RPC Proto"); |
|||
} |
|||
|
|||
protected void processTwoWayRpcTestGateway(String deviceName) throws Exception { |
|||
MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); |
|||
TransportApiProtos.ConnectMsg connectMsgProto = getConnectProto(deviceName); |
|||
byte[] payloadBytes = connectMsgProto.toByteArray(); |
|||
validateTwoWayRpcGateway(deviceName, client, payloadBytes); |
|||
} |
|||
|
|||
protected void processOneWayRpcTestGateway(String deviceName) throws Exception { |
|||
MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); |
|||
TransportApiProtos.ConnectMsg connectMsgProto = getConnectProto(deviceName); |
|||
byte[] payloadBytes = connectMsgProto.toByteArray(); |
|||
validateOneWayRpcGatewayResponse(deviceName, client, payloadBytes); |
|||
} |
|||
|
|||
|
|||
private TransportApiProtos.ConnectMsg getConnectProto(String deviceName) { |
|||
TransportApiProtos.ConnectMsg.Builder builder = TransportApiProtos.ConnectMsg.newBuilder(); |
|||
builder.setDeviceName(deviceName); |
|||
builder.setDeviceType(TransportPayloadType.PROTOBUF.name()); |
|||
return builder.build(); |
|||
} |
|||
|
|||
protected MqttMessage processMessageArrived(String requestTopic, MqttMessage mqttMessage) throws MqttException, InvalidProtocolBufferException { |
|||
MqttMessage message = new MqttMessage(); |
|||
if (requestTopic.startsWith(MqttTopics.BASE_DEVICE_API_TOPIC)) { |
|||
TransportProtos.ToDeviceRpcResponseMsg toDeviceRpcResponseMsg = TransportProtos.ToDeviceRpcResponseMsg.newBuilder() |
|||
.setPayload(DEVICE_RESPONSE) |
|||
.setRequestId(0) |
|||
.build(); |
|||
message.setPayload(toDeviceRpcResponseMsg.toByteArray()); |
|||
} else { |
|||
TransportApiProtos.GatewayDeviceRpcRequestMsg msg = TransportApiProtos.GatewayDeviceRpcRequestMsg.parseFrom(mqttMessage.getPayload()); |
|||
String deviceName = msg.getDeviceName(); |
|||
int requestId = msg.getRpcRequestMsg().getRequestId(); |
|||
TransportApiProtos.GatewayRpcResponseMsg gatewayRpcResponseMsg = TransportApiProtos.GatewayRpcResponseMsg.newBuilder() |
|||
.setDeviceName(deviceName) |
|||
.setId(requestId) |
|||
.setData("{\"success\": true}") |
|||
.build(); |
|||
message.setPayload(gatewayRpcResponseMsg.toByteArray()); |
|||
} |
|||
return message; |
|||
} |
|||
|
|||
|
|||
|
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.rpc.sql; |
|||
|
|||
import org.thingsboard.server.dao.service.DaoSqlTest; |
|||
import org.thingsboard.server.mqtt.rpc.AbstractMqttServerSideRpcJsonIntegrationTest; |
|||
|
|||
@DaoSqlTest |
|||
public class MqttServerSideRpcJsonSqlIntegrationTest extends AbstractMqttServerSideRpcJsonIntegrationTest { |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.rpc.sql; |
|||
|
|||
import org.thingsboard.server.dao.service.DaoSqlTest; |
|||
import org.thingsboard.server.mqtt.rpc.AbstractMqttServerSideRpcProtoIntegrationTest; |
|||
|
|||
|
|||
@DaoSqlTest |
|||
public class MqttServerSideRpcProtoSqlIntegrationTest extends AbstractMqttServerSideRpcProtoIntegrationTest { |
|||
} |
|||
@ -1,163 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.telemetry; |
|||
|
|||
import io.netty.handler.codec.mqtt.MqttQoS; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.eclipse.paho.client.mqttv3.*; |
|||
import org.junit.Before; |
|||
import org.junit.Ignore; |
|||
import org.junit.Test; |
|||
import org.springframework.web.util.UriComponentsBuilder; |
|||
import org.thingsboard.server.common.data.Device; |
|||
import org.thingsboard.server.common.data.security.DeviceCredentials; |
|||
import org.thingsboard.server.controller.AbstractControllerTest; |
|||
import org.thingsboard.server.dao.service.DaoNoSqlTest; |
|||
|
|||
import java.net.URI; |
|||
import java.util.*; |
|||
import java.util.concurrent.CountDownLatch; |
|||
import java.util.concurrent.TimeUnit; |
|||
|
|||
import static org.junit.Assert.assertEquals; |
|||
import static org.junit.Assert.assertNotNull; |
|||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
|||
|
|||
/** |
|||
* @author Valerii Sosliuk |
|||
*/ |
|||
@Slf4j |
|||
public abstract class AbstractMqttTelemetryIntegrationTest extends AbstractControllerTest { |
|||
|
|||
private static final String MQTT_URL = "tcp://localhost:1883"; |
|||
|
|||
private Device savedDevice; |
|||
private String accessToken; |
|||
|
|||
@Before |
|||
public void beforeTest() throws Exception { |
|||
loginTenantAdmin(); |
|||
|
|||
Device device = new Device(); |
|||
device.setName("Test device"); |
|||
device.setType("default"); |
|||
savedDevice = doPost("/api/device", device, Device.class); |
|||
|
|||
DeviceCredentials deviceCredentials = |
|||
doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class); |
|||
|
|||
assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId()); |
|||
accessToken = deviceCredentials.getCredentialsId(); |
|||
assertNotNull(accessToken); |
|||
} |
|||
|
|||
@Test |
|||
public void testPushMqttRpcData() throws Exception { |
|||
String clientId = MqttAsyncClient.generateClientId(); |
|||
MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId); |
|||
|
|||
MqttConnectOptions options = new MqttConnectOptions(); |
|||
options.setUserName(accessToken); |
|||
client.connect(options); |
|||
Thread.sleep(3000); |
|||
MqttMessage message = new MqttMessage(); |
|||
message.setPayload("{\"key1\":\"value1\", \"key2\":true, \"key3\": 3.0, \"key4\": 4}".getBytes()); |
|||
client.publish("v1/devices/me/telemetry", message); |
|||
|
|||
String deviceId = savedDevice.getId().getId().toString(); |
|||
|
|||
Thread.sleep(2000); |
|||
List<String> actualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + deviceId + "/keys/timeseries", List.class); |
|||
Set<String> actualKeySet = new HashSet<>(actualKeys); |
|||
|
|||
List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4"); |
|||
Set<String> expectedKeySet = new HashSet<>(expectedKeys); |
|||
|
|||
assertEquals(expectedKeySet, actualKeySet); |
|||
|
|||
String getTelemetryValuesUrl = "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/timeseries?keys=" + String.join(",", actualKeySet); |
|||
Map<String, List<Map<String, String>>> values = doGetAsync(getTelemetryValuesUrl, Map.class); |
|||
|
|||
assertEquals("value1", values.get("key1").get(0).get("value")); |
|||
assertEquals("true", values.get("key2").get(0).get("value")); |
|||
assertEquals("3.0", values.get("key3").get(0).get("value")); |
|||
assertEquals("4", values.get("key4").get(0).get("value")); |
|||
} |
|||
|
|||
|
|||
// @Test - Unstable
|
|||
public void testMqttQoSLevel() throws Exception { |
|||
String clientId = MqttAsyncClient.generateClientId(); |
|||
MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId); |
|||
|
|||
MqttConnectOptions options = new MqttConnectOptions(); |
|||
options.setUserName(accessToken); |
|||
CountDownLatch latch = new CountDownLatch(1); |
|||
TestMqttCallback callback = new TestMqttCallback(client, latch); |
|||
client.setCallback(callback); |
|||
client.connect(options).waitForCompletion(5000); |
|||
client.subscribe("v1/devices/me/attributes", MqttQoS.AT_MOST_ONCE.value()); |
|||
String payload = "{\"key\":\"uniqueValue\"}"; |
|||
// 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.
|
|||
// MqttClient -> SUB REQUEST -> Transport -> Kafka -> Device Actor (subscribed)
|
|||
// MqttClient <- SUB_ACK <- Transport
|
|||
Thread.sleep(5000); |
|||
doPostAsync("/api/plugins/telemetry/" + savedDevice.getId() + "/SHARED_SCOPE", payload, String.class, status().isOk()); |
|||
latch.await(10, TimeUnit.SECONDS); |
|||
assertEquals(payload, callback.getPayload()); |
|||
assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS()); |
|||
} |
|||
|
|||
private static class TestMqttCallback implements MqttCallback { |
|||
|
|||
private final MqttAsyncClient client; |
|||
private final CountDownLatch latch; |
|||
private volatile Integer qoS; |
|||
private volatile String payload; |
|||
|
|||
String getPayload() { |
|||
return payload; |
|||
} |
|||
|
|||
TestMqttCallback(MqttAsyncClient client, CountDownLatch latch) { |
|||
this.client = client; |
|||
this.latch = latch; |
|||
} |
|||
|
|||
int getQoS() { |
|||
return qoS; |
|||
} |
|||
|
|||
@Override |
|||
public void connectionLost(Throwable throwable) { |
|||
log.error("Client connection lost", throwable); |
|||
} |
|||
|
|||
@Override |
|||
public void messageArrived(String requestTopic, MqttMessage mqttMessage) { |
|||
payload = new String(mqttMessage.getPayload()); |
|||
qoS = mqttMessage.getQos(); |
|||
latch.countDown(); |
|||
} |
|||
|
|||
@Override |
|||
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) { |
|||
|
|||
} |
|||
} |
|||
|
|||
|
|||
} |
|||
@ -0,0 +1,175 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.telemetry.attributes; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.eclipse.paho.client.mqttv3.MqttAsyncClient; |
|||
import org.junit.After; |
|||
import org.junit.Before; |
|||
import org.junit.Test; |
|||
import org.thingsboard.server.common.data.Device; |
|||
import org.thingsboard.server.common.data.device.profile.MqttTopics; |
|||
import org.thingsboard.server.common.data.id.DeviceId; |
|||
import org.thingsboard.server.mqtt.AbstractMqttIntegrationTest; |
|||
|
|||
import java.util.Arrays; |
|||
import java.util.HashSet; |
|||
import java.util.LinkedHashMap; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
import java.util.Set; |
|||
|
|||
import static org.junit.Assert.assertEquals; |
|||
import static org.junit.Assert.assertNotNull; |
|||
import static org.junit.Assert.assertTrue; |
|||
|
|||
@Slf4j |
|||
public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqttIntegrationTest { |
|||
|
|||
protected static final String PAYLOAD_VALUES_STR = "{\"key1\":\"value1\", \"key2\":true, \"key3\": 3.0, \"key4\": 4," + |
|||
" \"key5\": {\"someNumber\": 42, \"someArray\": [1,2,3], \"someNestedObject\": {\"key\": \"value\"}}}"; |
|||
|
|||
@Before |
|||
public void beforeTest() throws Exception { |
|||
processBeforeTest("Test Post Attributes device", "Test Post Attributes gateway", null, null, null); |
|||
} |
|||
|
|||
@After |
|||
public void afterTest() throws Exception { |
|||
processAfterTest(); |
|||
} |
|||
|
|||
@Test |
|||
public void testPushMqttAttributes() throws Exception { |
|||
List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); |
|||
processAttributesTest(MqttTopics.DEVICE_ATTRIBUTES_TOPIC, expectedKeys, PAYLOAD_VALUES_STR.getBytes()); |
|||
} |
|||
|
|||
@Test |
|||
public void testPushMqttAttributesGateway() throws Exception { |
|||
List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); |
|||
String deviceName1 = "Device A"; |
|||
String deviceName2 = "Device B"; |
|||
String payload = getGatewayAttributesJsonPayload(deviceName1, deviceName2); |
|||
processGatewayAttributesTest(expectedKeys, payload.getBytes(), deviceName1, deviceName2); |
|||
} |
|||
|
|||
protected void processAttributesTest(String topic, List<String> expectedKeys, byte[] payload) throws Exception { |
|||
MqttAsyncClient client = getMqttAsyncClient(accessToken); |
|||
|
|||
publishMqttMsg(client, payload, topic); |
|||
|
|||
DeviceId deviceId = savedDevice.getId(); |
|||
|
|||
long start = System.currentTimeMillis(); |
|||
long end = System.currentTimeMillis() + 2000; |
|||
|
|||
List<String> actualKeys = null; |
|||
while (start <= end) { |
|||
actualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + deviceId + "/keys/attributes/CLIENT_SCOPE", List.class); |
|||
if (actualKeys.size() == expectedKeys.size()) { |
|||
break; |
|||
} |
|||
Thread.sleep(100); |
|||
start += 100; |
|||
} |
|||
assertNotNull(actualKeys); |
|||
|
|||
Set<String> actualKeySet = new HashSet<>(actualKeys); |
|||
|
|||
Set<String> expectedKeySet = new HashSet<>(expectedKeys); |
|||
|
|||
assertEquals(expectedKeySet, actualKeySet); |
|||
|
|||
String getAttributesValuesUrl = getAttributesValuesUrl(deviceId, actualKeySet); |
|||
List<Map<String, Object>> values = doGetAsync(getAttributesValuesUrl, List.class); |
|||
assertAttributesValues(values, expectedKeySet); |
|||
String deleteAttributesUrl = "/api/plugins/telemetry/DEVICE/" + deviceId + "/CLIENT_SCOPE?keys=" + String.join(",", actualKeySet); |
|||
doDelete(deleteAttributesUrl); |
|||
} |
|||
|
|||
protected void processGatewayAttributesTest(List<String> expectedKeys, byte[] payload, String firstDeviceName, String secondDeviceName) throws Exception { |
|||
MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); |
|||
|
|||
publishMqttMsg(client, payload, MqttTopics.GATEWAY_ATTRIBUTES_TOPIC); |
|||
|
|||
Thread.sleep(2000); |
|||
|
|||
Device firstDevice = doGet("/api/tenant/devices?deviceName=" + firstDeviceName, Device.class); |
|||
assertNotNull(firstDevice); |
|||
Device secondDevice = doGet("/api/tenant/devices?deviceName=" + secondDeviceName, Device.class); |
|||
assertNotNull(secondDevice); |
|||
|
|||
List<String> firstDeviceActualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + firstDevice.getId() + "/keys/attributes/CLIENT_SCOPE", List.class); |
|||
Set<String> firstDeviceActualKeySet = new HashSet<>(firstDeviceActualKeys); |
|||
|
|||
List<String> secondDeviceActualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + secondDevice.getId() + "/keys/attributes/CLIENT_SCOPE", List.class); |
|||
Set<String> secondDeviceActualKeySet = new HashSet<>(secondDeviceActualKeys); |
|||
|
|||
Set<String> expectedKeySet = new HashSet<>(expectedKeys); |
|||
|
|||
assertEquals(expectedKeySet, firstDeviceActualKeySet); |
|||
assertEquals(expectedKeySet, secondDeviceActualKeySet); |
|||
|
|||
String getAttributesValuesUrlFirstDevice = getAttributesValuesUrl(firstDevice.getId(), firstDeviceActualKeySet); |
|||
String getAttributesValuesUrlSecondDevice = getAttributesValuesUrl(firstDevice.getId(), secondDeviceActualKeySet); |
|||
|
|||
List<Map<String, Object>> firstDeviceValues = doGetAsync(getAttributesValuesUrlFirstDevice, List.class); |
|||
List<Map<String, Object>> secondDeviceValues = doGetAsync(getAttributesValuesUrlSecondDevice, List.class); |
|||
|
|||
assertAttributesValues(firstDeviceValues, expectedKeySet); |
|||
assertAttributesValues(secondDeviceValues, expectedKeySet); |
|||
|
|||
} |
|||
|
|||
protected void assertAttributesValues(List<Map<String, Object>> deviceValues, Set<String> expectedKeySet) { |
|||
for (Map<String, Object> map : deviceValues) { |
|||
String key = (String) map.get("key"); |
|||
Object value = map.get("value"); |
|||
assertTrue(expectedKeySet.contains(key)); |
|||
switch (key) { |
|||
case "key1": |
|||
assertEquals("value1", value); |
|||
break; |
|||
case "key2": |
|||
assertEquals(true, value); |
|||
break; |
|||
case "key3": |
|||
assertEquals(3.0, value); |
|||
break; |
|||
case "key4": |
|||
assertEquals(4, value); |
|||
break; |
|||
case "key5": |
|||
assertNotNull(value); |
|||
assertEquals(3, ((LinkedHashMap) value).size()); |
|||
assertEquals(42, ((LinkedHashMap) value).get("someNumber")); |
|||
assertEquals(Arrays.asList(1, 2, 3), ((LinkedHashMap) value).get("someArray")); |
|||
LinkedHashMap<String, String> someNestedObject = (LinkedHashMap) ((LinkedHashMap) value).get("someNestedObject"); |
|||
assertEquals("value", someNestedObject.get("key")); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
protected String getGatewayAttributesJsonPayload(String deviceA, String deviceB) { |
|||
return "{\"" + deviceA + "\": " + PAYLOAD_VALUES_STR + ", \"" + deviceB + "\": " + PAYLOAD_VALUES_STR + "}"; |
|||
} |
|||
|
|||
private String getAttributesValuesUrl(DeviceId deviceId, Set<String> actualKeySet) { |
|||
return "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/attributes/CLIENT_SCOPE?keys=" + String.join(",", actualKeySet); |
|||
} |
|||
} |
|||
@ -0,0 +1,56 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.telemetry.attributes; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.junit.After; |
|||
import org.junit.Before; |
|||
import org.junit.Test; |
|||
import org.thingsboard.server.common.data.TransportPayloadType; |
|||
|
|||
import java.util.Arrays; |
|||
import java.util.List; |
|||
|
|||
@Slf4j |
|||
public abstract class AbstractMqttAttributesJsonIntegrationTest extends AbstractMqttAttributesIntegrationTest { |
|||
|
|||
private static final String POST_DATA_ATTRIBUTES_TOPIC = "data/attributes"; |
|||
|
|||
@Before |
|||
public void beforeTest() throws Exception { |
|||
processBeforeTest("Test Post Attributes device", "Test Post Attributes gateway", TransportPayloadType.JSON, null, POST_DATA_ATTRIBUTES_TOPIC); |
|||
} |
|||
|
|||
@After |
|||
public void afterTest() throws Exception { |
|||
processAfterTest(); |
|||
} |
|||
|
|||
@Test |
|||
public void testPushMqttAttributes() throws Exception { |
|||
List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); |
|||
processAttributesTest(POST_DATA_ATTRIBUTES_TOPIC, expectedKeys, PAYLOAD_VALUES_STR.getBytes()); |
|||
} |
|||
|
|||
@Test |
|||
public void testPushMqttAttributesGateway() throws Exception { |
|||
List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); |
|||
String deviceName1 = "Device A"; |
|||
String deviceName2 = "Device B"; |
|||
String payload = getGatewayAttributesJsonPayload(deviceName1, deviceName2); |
|||
processGatewayAttributesTest(expectedKeys, payload.getBytes(), deviceName1, deviceName2); |
|||
} |
|||
} |
|||
@ -0,0 +1,75 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.telemetry.attributes; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.junit.After; |
|||
import org.junit.Before; |
|||
import org.junit.Test; |
|||
import org.thingsboard.server.common.data.TransportPayloadType; |
|||
import org.thingsboard.server.gen.transport.TransportApiProtos; |
|||
import org.thingsboard.server.gen.transport.TransportProtos; |
|||
|
|||
import java.util.Arrays; |
|||
import java.util.List; |
|||
|
|||
import static org.junit.Assert.assertEquals; |
|||
import static org.junit.Assert.assertNotNull; |
|||
import static org.junit.Assert.assertTrue; |
|||
|
|||
@Slf4j |
|||
public abstract class AbstractMqttAttributesProtoIntegrationTest extends AbstractMqttAttributesIntegrationTest { |
|||
|
|||
private static final String POST_DATA_ATTRIBUTES_TOPIC = "proto/attributes"; |
|||
|
|||
@Before |
|||
public void beforeTest() throws Exception { |
|||
processBeforeTest("Test Post Attributes device", "Test Post Attributes gateway", TransportPayloadType.PROTOBUF, null, POST_DATA_ATTRIBUTES_TOPIC); |
|||
} |
|||
|
|||
@After |
|||
public void afterTest() throws Exception { |
|||
processAfterTest(); |
|||
} |
|||
|
|||
@Test |
|||
public void testPushMqttAttributes() throws Exception { |
|||
List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); |
|||
TransportProtos.PostAttributeMsg msg = getPostAttributeMsg(expectedKeys); |
|||
processAttributesTest(POST_DATA_ATTRIBUTES_TOPIC, expectedKeys, msg.toByteArray()); |
|||
} |
|||
|
|||
@Test |
|||
public void testPushMqttAttributesGateway() throws Exception { |
|||
TransportApiProtos.GatewayAttributesMsg.Builder gatewayAttributesMsgProtoBuilder = TransportApiProtos.GatewayAttributesMsg.newBuilder(); |
|||
List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); |
|||
String deviceName1 = "Device A"; |
|||
String deviceName2 = "Device B"; |
|||
TransportApiProtos.AttributesMsg firstDeviceAttributesMsgProto = getDeviceAttributesMsgProto(deviceName1, expectedKeys); |
|||
TransportApiProtos.AttributesMsg secondDeviceAttributesMsgProto = getDeviceAttributesMsgProto(deviceName2, expectedKeys); |
|||
gatewayAttributesMsgProtoBuilder.addAllMsg(Arrays.asList(firstDeviceAttributesMsgProto, secondDeviceAttributesMsgProto)); |
|||
TransportApiProtos.GatewayAttributesMsg gatewayAttributesMsg = gatewayAttributesMsgProtoBuilder.build(); |
|||
processGatewayAttributesTest(expectedKeys, gatewayAttributesMsg.toByteArray(), deviceName1, deviceName2); |
|||
} |
|||
|
|||
private TransportApiProtos.AttributesMsg getDeviceAttributesMsgProto(String deviceName, List<String> expectedKeys) { |
|||
TransportApiProtos.AttributesMsg.Builder deviceAttributesMsgBuilder = TransportApiProtos.AttributesMsg.newBuilder(); |
|||
TransportProtos.PostAttributeMsg msg = getPostAttributeMsg(expectedKeys); |
|||
deviceAttributesMsgBuilder.setDeviceName(deviceName); |
|||
deviceAttributesMsgBuilder.setMsg(msg); |
|||
return deviceAttributesMsgBuilder.build(); |
|||
} |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.telemetry.attributes.nosql; |
|||
|
|||
import org.thingsboard.server.dao.service.DaoNoSqlTest; |
|||
import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesIntegrationTest; |
|||
|
|||
@DaoNoSqlTest |
|||
public class MqttAttributesNoSqlIntegrationTest extends AbstractMqttAttributesIntegrationTest { |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.telemetry.attributes.nosql; |
|||
|
|||
import org.thingsboard.server.dao.service.DaoNoSqlTest; |
|||
import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesIntegrationTest; |
|||
import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesJsonIntegrationTest; |
|||
|
|||
@DaoNoSqlTest |
|||
public class MqttAttributesNoSqlJsonIntegrationTest extends AbstractMqttAttributesJsonIntegrationTest { |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.telemetry.attributes.nosql; |
|||
|
|||
import org.thingsboard.server.dao.service.DaoNoSqlTest; |
|||
import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesIntegrationTest; |
|||
import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesProtoIntegrationTest; |
|||
|
|||
@DaoNoSqlTest |
|||
public class MqttAttributesNoSqlProtoIntegrationTest extends AbstractMqttAttributesProtoIntegrationTest { |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.telemetry.attributes.sql; |
|||
|
|||
import org.thingsboard.server.dao.service.DaoSqlTest; |
|||
import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesIntegrationTest; |
|||
|
|||
@DaoSqlTest |
|||
public class MqttAttributesSqlIntegrationTest extends AbstractMqttAttributesIntegrationTest { |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.telemetry.attributes.sql; |
|||
|
|||
import org.thingsboard.server.dao.service.DaoSqlTest; |
|||
import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesJsonIntegrationTest; |
|||
|
|||
@DaoSqlTest |
|||
public class MqttAttributesSqlJsonIntegrationTest extends AbstractMqttAttributesJsonIntegrationTest { |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.telemetry.attributes.sql; |
|||
|
|||
import org.thingsboard.server.dao.service.DaoSqlTest; |
|||
import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesJsonIntegrationTest; |
|||
import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesProtoIntegrationTest; |
|||
|
|||
@DaoSqlTest |
|||
public class MqttAttributesSqlProtoIntegrationTest extends AbstractMqttAttributesProtoIntegrationTest { |
|||
} |
|||
@ -0,0 +1,290 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.telemetry.timeseries; |
|||
|
|||
import io.netty.handler.codec.mqtt.MqttQoS; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; |
|||
import org.eclipse.paho.client.mqttv3.MqttAsyncClient; |
|||
import org.eclipse.paho.client.mqttv3.MqttCallback; |
|||
import org.eclipse.paho.client.mqttv3.MqttConnectOptions; |
|||
import org.eclipse.paho.client.mqttv3.MqttMessage; |
|||
import org.junit.After; |
|||
import org.junit.Before; |
|||
import org.junit.Test; |
|||
import org.thingsboard.server.common.data.Device; |
|||
import org.thingsboard.server.common.data.TransportPayloadType; |
|||
import org.thingsboard.server.common.data.device.profile.MqttTopics; |
|||
import org.thingsboard.server.common.data.id.DeviceId; |
|||
import org.thingsboard.server.mqtt.AbstractMqttIntegrationTest; |
|||
|
|||
import java.util.Arrays; |
|||
import java.util.HashSet; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
import java.util.Set; |
|||
import java.util.concurrent.CountDownLatch; |
|||
import java.util.concurrent.TimeUnit; |
|||
|
|||
import static org.junit.Assert.assertEquals; |
|||
import static org.junit.Assert.assertNotNull; |
|||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
|||
|
|||
@Slf4j |
|||
public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqttIntegrationTest { |
|||
|
|||
protected static final String PAYLOAD_VALUES_STR = "{\"key1\":\"value1\", \"key2\":true, \"key3\": 3.0, \"key4\": 4," + |
|||
" \"key5\": {\"someNumber\": 42, \"someArray\": [1,2,3], \"someNestedObject\": {\"key\": \"value\"}}}"; |
|||
|
|||
@Before |
|||
public void beforeTest() throws Exception { |
|||
processBeforeTest("Test Post Telemetry device", "Test Post Telemetry gateway", null, null, null); |
|||
} |
|||
|
|||
@After |
|||
public void afterTest() throws Exception { |
|||
processAfterTest(); |
|||
} |
|||
|
|||
@Test |
|||
public void testPushMqttTelemetry() throws Exception { |
|||
List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); |
|||
processTelemetryTest(MqttTopics.DEVICE_TELEMETRY_TOPIC, expectedKeys, PAYLOAD_VALUES_STR.getBytes(), false); |
|||
} |
|||
|
|||
@Test |
|||
public void testPushMqttTelemetryWithTs() throws Exception { |
|||
String payloadStr = "{\"ts\": 10000, \"values\": " + PAYLOAD_VALUES_STR + "}"; |
|||
List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); |
|||
processTelemetryTest(MqttTopics.DEVICE_TELEMETRY_TOPIC, expectedKeys, payloadStr.getBytes(), true); |
|||
} |
|||
|
|||
@Test |
|||
public void testPushMqttTelemetryGateway() throws Exception { |
|||
List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); |
|||
String deviceName1 = "Device A"; |
|||
String deviceName2 = "Device B"; |
|||
String payload = getGatewayTelemetryJsonPayload(deviceName1, deviceName2, "10000", "20000"); |
|||
processGatewayTelemetryTest(MqttTopics.GATEWAY_TELEMETRY_TOPIC, expectedKeys, payload.getBytes(), deviceName1, deviceName2); |
|||
} |
|||
|
|||
@Test |
|||
public void testGatewayConnect() throws Exception { |
|||
String payload = "{\"device\":\"Device A\"}"; |
|||
MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); |
|||
publishMqttMsg(client, payload.getBytes(), MqttTopics.GATEWAY_CONNECT_TOPIC); |
|||
|
|||
Thread.sleep(2000); |
|||
|
|||
String deviceName = "Device A"; |
|||
Device device = doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class); |
|||
assertNotNull(device); |
|||
} |
|||
|
|||
protected void processTelemetryTest(String topic, List<String> expectedKeys, byte[] payload, boolean withTs) throws Exception { |
|||
MqttAsyncClient client = getMqttAsyncClient(accessToken); |
|||
publishMqttMsg(client, payload, topic); |
|||
|
|||
String deviceId = savedDevice.getId().getId().toString(); |
|||
|
|||
long start = System.currentTimeMillis(); |
|||
long end = System.currentTimeMillis() + 2000; |
|||
|
|||
List<String> actualKeys = null; |
|||
while (start <= end) { |
|||
actualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + deviceId + "/keys/timeseries", List.class); |
|||
if (actualKeys.size() == expectedKeys.size()) { |
|||
break; |
|||
} |
|||
Thread.sleep(100); |
|||
start += 100; |
|||
} |
|||
assertNotNull(actualKeys); |
|||
|
|||
Set<String> actualKeySet = new HashSet<>(actualKeys); |
|||
Set<String> expectedKeySet = new HashSet<>(expectedKeys); |
|||
|
|||
assertEquals(expectedKeySet, actualKeySet); |
|||
|
|||
String getTelemetryValuesUrl; |
|||
if (withTs) { |
|||
getTelemetryValuesUrl = "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/timeseries?startTs=0&endTs=15000&keys=" + String.join(",", actualKeySet); |
|||
} else { |
|||
getTelemetryValuesUrl = "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/timeseries?keys=" + String.join(",", actualKeySet); |
|||
} |
|||
Map<String, List<Map<String, String>>> values = doGetAsync(getTelemetryValuesUrl, Map.class); |
|||
|
|||
if (withTs) { |
|||
assertTs(values, expectedKeys, 10000, 0); |
|||
} |
|||
assertValues(values, 0); |
|||
} |
|||
|
|||
protected void processGatewayTelemetryTest(String topic, List<String> expectedKeys, byte[] payload, String firstDeviceName, String secondDeviceName) throws Exception { |
|||
MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); |
|||
|
|||
publishMqttMsg(client, payload, topic); |
|||
|
|||
Thread.sleep(2000); |
|||
|
|||
Device firstDevice = doGet("/api/tenant/devices?deviceName=" + firstDeviceName, Device.class); |
|||
assertNotNull(firstDevice); |
|||
Device secondDevice = doGet("/api/tenant/devices?deviceName=" + secondDeviceName, Device.class); |
|||
assertNotNull(secondDevice); |
|||
|
|||
List<String> firstDeviceActualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + firstDevice.getId() + "/keys/timeseries", List.class); |
|||
Set<String> firstDeviceActualKeySet = new HashSet<>(firstDeviceActualKeys); |
|||
|
|||
List<String> secondDeviceActualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + secondDevice.getId() + "/keys/timeseries", List.class); |
|||
Set<String> secondDeviceActualKeySet = new HashSet<>(secondDeviceActualKeys); |
|||
|
|||
Set<String> expectedKeySet = new HashSet<>(expectedKeys); |
|||
|
|||
assertEquals(expectedKeySet, firstDeviceActualKeySet); |
|||
assertEquals(expectedKeySet, secondDeviceActualKeySet); |
|||
|
|||
String getTelemetryValuesUrlFirstDevice = getTelemetryValuesUrl(firstDevice.getId(), firstDeviceActualKeySet); |
|||
String getTelemetryValuesUrlSecondDevice = getTelemetryValuesUrl(firstDevice.getId(), secondDeviceActualKeySet); |
|||
|
|||
Map<String, List<Map<String, String>>> firstDeviceValues = doGetAsync(getTelemetryValuesUrlFirstDevice, Map.class); |
|||
Map<String, List<Map<String, String>>> secondDeviceValues = doGetAsync(getTelemetryValuesUrlSecondDevice, Map.class); |
|||
|
|||
assertGatewayDeviceData(firstDeviceValues, expectedKeys); |
|||
assertGatewayDeviceData(secondDeviceValues, expectedKeys); |
|||
} |
|||
|
|||
protected String getGatewayTelemetryJsonPayload(String deviceA, String deviceB, String firstTsValue, String secondTsValue) { |
|||
String payload = "[{\"ts\": " + firstTsValue + ", \"values\": " + PAYLOAD_VALUES_STR + "}, " + |
|||
"{\"ts\": " + secondTsValue + ", \"values\": " + PAYLOAD_VALUES_STR + "}]"; |
|||
return "{\"" + deviceA + "\": " + payload + ", \"" + deviceB + "\": " + payload + "}"; |
|||
} |
|||
|
|||
private String getTelemetryValuesUrl(DeviceId deviceId, Set<String> actualKeySet) { |
|||
return "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/timeseries?startTs=0&endTs=25000&keys=" + String.join(",", actualKeySet); |
|||
} |
|||
|
|||
private void assertGatewayDeviceData(Map<String, List<Map<String, String>>> deviceValues, List<String> expectedKeys) { |
|||
|
|||
assertEquals(2, deviceValues.get(expectedKeys.get(0)).size()); |
|||
assertEquals(2, deviceValues.get(expectedKeys.get(1)).size()); |
|||
assertEquals(2, deviceValues.get(expectedKeys.get(2)).size()); |
|||
assertEquals(2, deviceValues.get(expectedKeys.get(3)).size()); |
|||
assertEquals(2, deviceValues.get(expectedKeys.get(4)).size()); |
|||
|
|||
assertTs(deviceValues, expectedKeys, 20000, 0); |
|||
assertTs(deviceValues, expectedKeys, 10000, 1); |
|||
|
|||
assertValues(deviceValues, 0); |
|||
assertValues(deviceValues, 1); |
|||
|
|||
} |
|||
|
|||
private void assertValues(Map<String, List<Map<String, String>>> deviceValues, int arrayIndex) { |
|||
for (Map.Entry<String, List<Map<String, String>>> entry : deviceValues.entrySet()) { |
|||
String key = entry.getKey(); |
|||
List<Map<String, String>> tsKv = entry.getValue(); |
|||
String value = tsKv.get(arrayIndex).get("value"); |
|||
switch (key) { |
|||
case "key1": |
|||
assertEquals("value1", value); |
|||
break; |
|||
case "key2": |
|||
assertEquals("true", value); |
|||
break; |
|||
case "key3": |
|||
assertEquals("3.0", value); |
|||
break; |
|||
case "key4": |
|||
assertEquals("4", value); |
|||
break; |
|||
case "key5": |
|||
assertEquals("{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}", value); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
private void assertTs(Map<String, List<Map<String, String>>> deviceValues, List<String> expectedKeys, int ts, int arrayIndex) { |
|||
assertEquals(ts, deviceValues.get(expectedKeys.get(0)).get(arrayIndex).get("ts")); |
|||
assertEquals(ts, deviceValues.get(expectedKeys.get(1)).get(arrayIndex).get("ts")); |
|||
assertEquals(ts, deviceValues.get(expectedKeys.get(2)).get(arrayIndex).get("ts")); |
|||
assertEquals(ts, deviceValues.get(expectedKeys.get(3)).get(arrayIndex).get("ts")); |
|||
assertEquals(ts, deviceValues.get(expectedKeys.get(4)).get(arrayIndex).get("ts")); |
|||
} |
|||
|
|||
// @Test - Unstable
|
|||
public void testMqttQoSLevel() throws Exception { |
|||
String clientId = MqttAsyncClient.generateClientId(); |
|||
MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId); |
|||
|
|||
MqttConnectOptions options = new MqttConnectOptions(); |
|||
options.setUserName(accessToken); |
|||
CountDownLatch latch = new CountDownLatch(1); |
|||
TestMqttCallback callback = new TestMqttCallback(client, latch); |
|||
client.setCallback(callback); |
|||
client.connect(options).waitForCompletion(5000); |
|||
client.subscribe("v1/devices/me/attributes", MqttQoS.AT_MOST_ONCE.value()); |
|||
String payload = "{\"key\":\"uniqueValue\"}"; |
|||
// 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.
|
|||
// MqttClient -> SUB REQUEST -> Transport -> Kafka -> Device Actor (subscribed)
|
|||
// MqttClient <- SUB_ACK <- Transport
|
|||
Thread.sleep(5000); |
|||
doPostAsync("/api/plugins/telemetry/" + savedDevice.getId() + "/SHARED_SCOPE", payload, String.class, status().isOk()); |
|||
latch.await(10, TimeUnit.SECONDS); |
|||
assertEquals(payload, callback.getPayload()); |
|||
assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS()); |
|||
} |
|||
|
|||
private static class TestMqttCallback implements MqttCallback { |
|||
|
|||
private final MqttAsyncClient client; |
|||
private final CountDownLatch latch; |
|||
private volatile Integer qoS; |
|||
private volatile String payload; |
|||
|
|||
String getPayload() { |
|||
return payload; |
|||
} |
|||
|
|||
TestMqttCallback(MqttAsyncClient client, CountDownLatch latch) { |
|||
this.client = client; |
|||
this.latch = latch; |
|||
} |
|||
|
|||
int getQoS() { |
|||
return qoS; |
|||
} |
|||
|
|||
@Override |
|||
public void connectionLost(Throwable throwable) { |
|||
log.error("Client connection lost", throwable); |
|||
} |
|||
|
|||
@Override |
|||
public void messageArrived(String requestTopic, MqttMessage mqttMessage) { |
|||
payload = new String(mqttMessage.getPayload()); |
|||
qoS = mqttMessage.getQos(); |
|||
latch.countDown(); |
|||
} |
|||
|
|||
@Override |
|||
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) { |
|||
|
|||
} |
|||
} |
|||
|
|||
|
|||
} |
|||
@ -0,0 +1,82 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.telemetry.timeseries; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.eclipse.paho.client.mqttv3.MqttAsyncClient; |
|||
import org.junit.After; |
|||
import org.junit.Before; |
|||
import org.junit.Test; |
|||
import org.thingsboard.server.common.data.Device; |
|||
import org.thingsboard.server.common.data.TransportPayloadType; |
|||
import org.thingsboard.server.common.data.device.profile.MqttTopics; |
|||
|
|||
import java.util.Arrays; |
|||
import java.util.List; |
|||
|
|||
import static org.junit.Assert.assertEquals; |
|||
import static org.junit.Assert.assertNotNull; |
|||
|
|||
@Slf4j |
|||
public abstract class AbstractMqttTimeseriesJsonIntegrationTest extends AbstractMqttTimeseriesIntegrationTest { |
|||
|
|||
private static final String POST_DATA_TELEMETRY_TOPIC = "data/telemetry"; |
|||
|
|||
@Before |
|||
public void beforeTest() throws Exception { |
|||
processBeforeTest("Test Post Telemetry device json payload", "Test Post Telemetry gateway json payload", TransportPayloadType.JSON, POST_DATA_TELEMETRY_TOPIC, null); |
|||
} |
|||
|
|||
@After |
|||
public void afterTest() throws Exception { |
|||
processAfterTest(); |
|||
} |
|||
|
|||
@Test |
|||
public void testPushMqttTelemetry() throws Exception { |
|||
List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); |
|||
processTelemetryTest(POST_DATA_TELEMETRY_TOPIC, expectedKeys, PAYLOAD_VALUES_STR.getBytes(), false); |
|||
} |
|||
|
|||
@Test |
|||
public void testPushMqttTelemetryWithTs() throws Exception { |
|||
String payloadStr = "{\"ts\": 10000, \"values\": " + PAYLOAD_VALUES_STR + "}"; |
|||
List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); |
|||
processTelemetryTest(POST_DATA_TELEMETRY_TOPIC, expectedKeys, payloadStr.getBytes(), true); |
|||
} |
|||
|
|||
@Test |
|||
public void testPushMqttTelemetryGateway() throws Exception { |
|||
List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); |
|||
String deviceName1 = "Device A"; |
|||
String deviceName2 = "Device B"; |
|||
String payload = getGatewayTelemetryJsonPayload(deviceName1, deviceName2, "10000", "20000"); |
|||
processGatewayTelemetryTest(MqttTopics.GATEWAY_TELEMETRY_TOPIC, expectedKeys, payload.getBytes(), deviceName1, deviceName2); |
|||
} |
|||
|
|||
@Test |
|||
public void testGatewayConnect() throws Exception { |
|||
String payload = "{\"device\":\"Device A\", \"type\": \"" + TransportPayloadType.JSON.name() + "\"}"; |
|||
MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); |
|||
publishMqttMsg(client, payload.getBytes(), MqttTopics.GATEWAY_CONNECT_TOPIC); |
|||
|
|||
Thread.sleep(2000); |
|||
|
|||
String deviceName = "Device A"; |
|||
Device device = doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class); |
|||
assertNotNull(device); |
|||
} |
|||
} |
|||
@ -0,0 +1,115 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.telemetry.timeseries; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.eclipse.paho.client.mqttv3.MqttAsyncClient; |
|||
import org.junit.After; |
|||
import org.junit.Before; |
|||
import org.junit.Test; |
|||
import org.thingsboard.server.common.data.Device; |
|||
import org.thingsboard.server.common.data.TransportPayloadType; |
|||
import org.thingsboard.server.common.data.device.profile.MqttTopics; |
|||
import org.thingsboard.server.gen.transport.TransportApiProtos; |
|||
import org.thingsboard.server.gen.transport.TransportProtos; |
|||
|
|||
import java.util.Arrays; |
|||
import java.util.List; |
|||
|
|||
import static org.junit.Assert.assertEquals; |
|||
import static org.junit.Assert.assertNotNull; |
|||
|
|||
@Slf4j |
|||
public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends AbstractMqttTimeseriesIntegrationTest { |
|||
|
|||
private static final String POST_DATA_TELEMETRY_TOPIC = "proto/telemetry"; |
|||
|
|||
@Before |
|||
public void beforeTest() throws Exception { |
|||
processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, POST_DATA_TELEMETRY_TOPIC, null); |
|||
} |
|||
|
|||
@After |
|||
public void afterTest() throws Exception { |
|||
processAfterTest(); |
|||
} |
|||
|
|||
@Test |
|||
public void testPushMqttTelemetry() throws Exception { |
|||
List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); |
|||
TransportProtos.TsKvListProto tsKvListProto = getTsKvListProto(expectedKeys, 0); |
|||
processTelemetryTest(POST_DATA_TELEMETRY_TOPIC, expectedKeys, tsKvListProto.toByteArray(), false); |
|||
} |
|||
|
|||
@Test |
|||
public void testPushMqttTelemetryWithTs() throws Exception { |
|||
List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); |
|||
TransportProtos.TsKvListProto tsKvListProto = getTsKvListProto(expectedKeys, 10000); |
|||
processTelemetryTest(POST_DATA_TELEMETRY_TOPIC, expectedKeys, tsKvListProto.toByteArray(), true); |
|||
} |
|||
|
|||
@Test |
|||
public void testPushMqttTelemetryGateway() throws Exception { |
|||
TransportApiProtos.GatewayTelemetryMsg.Builder gatewayTelemetryMsgProtoBuilder = TransportApiProtos.GatewayTelemetryMsg.newBuilder(); |
|||
List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); |
|||
String deviceName1 = "Device A"; |
|||
String deviceName2 = "Device B"; |
|||
TransportApiProtos.TelemetryMsg deviceATelemetryMsgProto = getDeviceTelemetryMsgProto(deviceName1, expectedKeys, 10000, 20000); |
|||
TransportApiProtos.TelemetryMsg deviceBTelemetryMsgProto = getDeviceTelemetryMsgProto(deviceName2, expectedKeys, 10000, 20000); |
|||
gatewayTelemetryMsgProtoBuilder.addAllMsg(Arrays.asList(deviceATelemetryMsgProto, deviceBTelemetryMsgProto)); |
|||
TransportApiProtos.GatewayTelemetryMsg gatewayTelemetryMsg = gatewayTelemetryMsgProtoBuilder.build(); |
|||
processGatewayTelemetryTest(MqttTopics.GATEWAY_TELEMETRY_TOPIC, expectedKeys, gatewayTelemetryMsg.toByteArray(), deviceName1, deviceName2); |
|||
} |
|||
|
|||
@Test |
|||
public void testGatewayConnect() throws Exception { |
|||
String deviceName = "Device A"; |
|||
TransportApiProtos.ConnectMsg connectMsgProto = getConnectProto(deviceName); |
|||
MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); |
|||
publishMqttMsg(client, connectMsgProto.toByteArray(), MqttTopics.GATEWAY_CONNECT_TOPIC); |
|||
|
|||
Thread.sleep(2000); |
|||
|
|||
Device device = doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class); |
|||
assertNotNull(device); |
|||
} |
|||
|
|||
private TransportApiProtos.ConnectMsg getConnectProto(String deviceName) { |
|||
TransportApiProtos.ConnectMsg.Builder builder = TransportApiProtos.ConnectMsg.newBuilder(); |
|||
builder.setDeviceName(deviceName); |
|||
builder.setDeviceType(TransportPayloadType.PROTOBUF.name()); |
|||
return builder.build(); |
|||
} |
|||
|
|||
private TransportApiProtos.TelemetryMsg getDeviceTelemetryMsgProto(String deviceName, List<String> expectedKeys, long firstTs, long secondTs) { |
|||
TransportApiProtos.TelemetryMsg.Builder deviceTelemetryMsgBuilder = TransportApiProtos.TelemetryMsg.newBuilder(); |
|||
TransportProtos.TsKvListProto tsKvListProto1 = getTsKvListProto(expectedKeys, firstTs); |
|||
TransportProtos.TsKvListProto tsKvListProto2 = getTsKvListProto(expectedKeys, secondTs); |
|||
TransportProtos.PostTelemetryMsg.Builder msg = TransportProtos.PostTelemetryMsg.newBuilder(); |
|||
msg.addAllTsKvList(Arrays.asList(tsKvListProto1, tsKvListProto2)); |
|||
deviceTelemetryMsgBuilder.setDeviceName(deviceName); |
|||
deviceTelemetryMsgBuilder.setMsg(msg); |
|||
return deviceTelemetryMsgBuilder.build(); |
|||
} |
|||
|
|||
private TransportProtos.TsKvListProto getTsKvListProto(List<String> expectedKeys, long ts) { |
|||
List<TransportProtos.KeyValueProto> kvProtos = getKvProtos(expectedKeys); |
|||
TransportProtos.TsKvListProto.Builder builder = TransportProtos.TsKvListProto.newBuilder(); |
|||
builder.addAllKv(kvProtos); |
|||
builder.setTs(ts); |
|||
return builder.build(); |
|||
} |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.telemetry.timeseries.nosql; |
|||
|
|||
import org.thingsboard.server.dao.service.DaoNoSqlTest; |
|||
import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesJsonIntegrationTest; |
|||
|
|||
@DaoNoSqlTest |
|||
public class MqttTimeseriesNoSqlJsonIntegrationTest extends AbstractMqttTimeseriesJsonIntegrationTest { |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.telemetry.timeseries.nosql; |
|||
|
|||
import org.thingsboard.server.dao.service.DaoNoSqlTest; |
|||
import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesProtoIntegrationTest; |
|||
|
|||
@DaoNoSqlTest |
|||
public class MqttTimeseriesNoSqlProtoIntegrationTest extends AbstractMqttTimeseriesProtoIntegrationTest { |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.telemetry.timeseries.sql; |
|||
|
|||
import org.thingsboard.server.dao.service.DaoSqlTest; |
|||
import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesIntegrationTest; |
|||
import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesJsonIntegrationTest; |
|||
|
|||
/** |
|||
* Created by Valerii Sosliuk on 8/22/2017. |
|||
*/ |
|||
@DaoSqlTest |
|||
public class MqttTimeseriesSqlJsonIntegrationTest extends AbstractMqttTimeseriesJsonIntegrationTest { |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.mqtt.telemetry.timeseries.sql; |
|||
|
|||
import org.thingsboard.server.dao.service.DaoSqlTest; |
|||
import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesJsonIntegrationTest; |
|||
import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesProtoIntegrationTest; |
|||
|
|||
/** |
|||
* Created by Valerii Sosliuk on 8/22/2017. |
|||
*/ |
|||
@DaoSqlTest |
|||
public class MqttTimeseriesSqlProtoIntegrationTest extends AbstractMqttTimeseriesProtoIntegrationTest { |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data; |
|||
|
|||
public enum TransportPayloadType { |
|||
JSON, |
|||
PROTOBUF |
|||
} |
|||
@ -0,0 +1,193 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.transport.mqtt.adaptors; |
|||
|
|||
import com.google.protobuf.InvalidProtocolBufferException; |
|||
import io.netty.buffer.ByteBuf; |
|||
import io.netty.buffer.ByteBufAllocator; |
|||
import io.netty.buffer.UnpooledByteBufAllocator; |
|||
import io.netty.handler.codec.mqtt.MqttFixedHeader; |
|||
import io.netty.handler.codec.mqtt.MqttMessage; |
|||
import io.netty.handler.codec.mqtt.MqttMessageType; |
|||
import io.netty.handler.codec.mqtt.MqttPublishMessage; |
|||
import io.netty.handler.codec.mqtt.MqttPublishVariableHeader; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.stereotype.Component; |
|||
import org.springframework.util.StringUtils; |
|||
import org.thingsboard.server.common.data.device.profile.MqttTopics; |
|||
import org.thingsboard.server.common.transport.adaptor.AdaptorException; |
|||
import org.thingsboard.server.common.transport.adaptor.ProtoConverter; |
|||
import org.thingsboard.server.gen.transport.TransportApiProtos; |
|||
import org.thingsboard.server.gen.transport.TransportProtos; |
|||
import org.thingsboard.server.transport.mqtt.session.MqttDeviceAwareSessionContext; |
|||
|
|||
import java.util.Optional; |
|||
|
|||
@Component |
|||
@Slf4j |
|||
public class ProtoMqttAdaptor implements MqttTransportAdaptor { |
|||
|
|||
private static final ByteBufAllocator ALLOCATOR = new UnpooledByteBufAllocator(false); |
|||
|
|||
@Override |
|||
public TransportProtos.PostTelemetryMsg convertToPostTelemetry(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { |
|||
byte[] bytes = toBytes(inbound.payload()); |
|||
try { |
|||
return ProtoConverter.convertToTelemetryProto(bytes); |
|||
} catch (InvalidProtocolBufferException | IllegalArgumentException e) { |
|||
throw new AdaptorException(e); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public TransportProtos.PostAttributeMsg convertToPostAttributes(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { |
|||
byte[] bytes = toBytes(inbound.payload()); |
|||
try { |
|||
return ProtoConverter.validatePostAttributeMsg(bytes); |
|||
} catch (InvalidProtocolBufferException | IllegalArgumentException e) { |
|||
throw new AdaptorException(e); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public TransportProtos.ClaimDeviceMsg convertToClaimDevice(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { |
|||
byte[] bytes = toBytes(inbound.payload()); |
|||
try { |
|||
return ProtoConverter.convertToClaimDeviceProto(ctx.getDeviceId(), bytes); |
|||
} catch (InvalidProtocolBufferException e) { |
|||
throw new AdaptorException(e); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public TransportProtos.GetAttributeRequestMsg convertToGetAttributes(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { |
|||
byte[] bytes = toBytes(inbound.payload()); |
|||
String topicName = inbound.variableHeader().topicName(); |
|||
int requestId = getRequestId(topicName, MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX); |
|||
try { |
|||
return ProtoConverter.convertToGetAttributeRequestMessage(bytes, requestId); |
|||
} catch (InvalidProtocolBufferException e) { |
|||
log.warn("Failed to decode get attributes request", e); |
|||
throw new AdaptorException(e); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public TransportProtos.ToDeviceRpcResponseMsg convertToDeviceRpcResponse(MqttDeviceAwareSessionContext ctx, MqttPublishMessage mqttMsg) throws AdaptorException { |
|||
byte[] bytes = toBytes(mqttMsg.payload()); |
|||
try { |
|||
return TransportProtos.ToDeviceRpcResponseMsg.parseFrom(bytes); |
|||
} catch (RuntimeException | InvalidProtocolBufferException e) { |
|||
log.warn("Failed to decode Rpc response", e); |
|||
throw new AdaptorException(e); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public TransportProtos.ToServerRpcRequestMsg convertToServerRpcRequest(MqttDeviceAwareSessionContext ctx, MqttPublishMessage mqttMsg) throws AdaptorException { |
|||
byte[] bytes = toBytes(mqttMsg.payload()); |
|||
String topicName = mqttMsg.variableHeader().topicName(); |
|||
try { |
|||
int requestId = getRequestId(topicName, MqttTopics.DEVICE_RPC_REQUESTS_TOPIC); |
|||
return ProtoConverter.convertToServerRpcRequest(bytes, requestId); |
|||
} catch (InvalidProtocolBufferException e) { |
|||
throw new AdaptorException(e); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException { |
|||
if (!StringUtils.isEmpty(responseMsg.getError())) { |
|||
throw new AdaptorException(responseMsg.getError()); |
|||
} else { |
|||
int requestId = responseMsg.getRequestId(); |
|||
if (requestId >= 0) { |
|||
return Optional.of(createMqttPublishMsg(ctx, |
|||
MqttTopics.DEVICE_ATTRIBUTES_RESPONSE_TOPIC_PREFIX + requestId, |
|||
responseMsg.toByteArray())); |
|||
} |
|||
return Optional.empty(); |
|||
} |
|||
} |
|||
|
|||
|
|||
@Override |
|||
public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) { |
|||
return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_RPC_REQUESTS_TOPIC + rpcRequest.getRequestId(), rpcRequest.toByteArray())); |
|||
} |
|||
|
|||
@Override |
|||
public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.ToServerRpcResponseMsg rpcResponse) { |
|||
return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_RPC_RESPONSE_TOPIC + rpcResponse.getRequestId(), rpcResponse.toByteArray())); |
|||
} |
|||
|
|||
@Override |
|||
public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.AttributeUpdateNotificationMsg notificationMsg) { |
|||
return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_ATTRIBUTES_TOPIC, notificationMsg.toByteArray())); |
|||
} |
|||
|
|||
@Override |
|||
public Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException { |
|||
if (!StringUtils.isEmpty(responseMsg.getError())) { |
|||
throw new AdaptorException(responseMsg.getError()); |
|||
} else { |
|||
TransportApiProtos.GatewayAttributeResponseMsg.Builder responseMsgBuilder = TransportApiProtos.GatewayAttributeResponseMsg.newBuilder(); |
|||
responseMsgBuilder.setDeviceName(deviceName); |
|||
responseMsgBuilder.setResponseMsg(responseMsg); |
|||
byte[] payloadBytes = responseMsgBuilder.build().toByteArray(); |
|||
return Optional.of(createMqttPublishMsg(ctx, MqttTopics.GATEWAY_ATTRIBUTES_RESPONSE_TOPIC, payloadBytes)); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.AttributeUpdateNotificationMsg notificationMsg) { |
|||
TransportApiProtos.GatewayAttributeUpdateNotificationMsg.Builder builder = TransportApiProtos.GatewayAttributeUpdateNotificationMsg.newBuilder(); |
|||
builder.setDeviceName(deviceName); |
|||
builder.setNotificationMsg(notificationMsg); |
|||
byte[] payloadBytes = builder.build().toByteArray(); |
|||
return Optional.of(createMqttPublishMsg(ctx, MqttTopics.GATEWAY_ATTRIBUTES_TOPIC, payloadBytes)); |
|||
} |
|||
|
|||
@Override |
|||
public Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) { |
|||
TransportApiProtos.GatewayDeviceRpcRequestMsg.Builder builder = TransportApiProtos.GatewayDeviceRpcRequestMsg.newBuilder(); |
|||
builder.setDeviceName(deviceName); |
|||
builder.setRpcRequestMsg(rpcRequest); |
|||
byte[] payloadBytes = builder.build().toByteArray(); |
|||
return Optional.of(createMqttPublishMsg(ctx, MqttTopics.GATEWAY_RPC_TOPIC, payloadBytes)); |
|||
} |
|||
|
|||
public static byte[] toBytes(ByteBuf inbound) { |
|||
byte[] bytes = new byte[inbound.readableBytes()]; |
|||
int readerIndex = inbound.readerIndex(); |
|||
inbound.getBytes(readerIndex, bytes); |
|||
return bytes; |
|||
} |
|||
|
|||
private MqttPublishMessage createMqttPublishMsg(MqttDeviceAwareSessionContext ctx, String topic, byte[] payloadBytes) { |
|||
MqttFixedHeader mqttFixedHeader = |
|||
new MqttFixedHeader(MqttMessageType.PUBLISH, false, ctx.getQoSForTopic(topic), false, 0); |
|||
MqttPublishVariableHeader header = new MqttPublishVariableHeader(topic, ctx.nextMsgId()); |
|||
ByteBuf payload = ALLOCATOR.buffer(); |
|||
payload.writeBytes(payloadBytes); |
|||
return new MqttPublishMessage(mqttFixedHeader, header, payload); |
|||
} |
|||
|
|||
private int getRequestId(String topicName, String topic) { |
|||
return Integer.parseInt(topicName.substring(topic.length())); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,164 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.transport.adaptor; |
|||
|
|||
import com.google.gson.JsonParser; |
|||
import com.google.protobuf.InvalidProtocolBufferException; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.util.CollectionUtils; |
|||
import org.springframework.util.StringUtils; |
|||
import org.thingsboard.server.common.data.DataConstants; |
|||
import org.thingsboard.server.common.data.id.DeviceId; |
|||
import org.thingsboard.server.gen.transport.TransportApiProtos; |
|||
import org.thingsboard.server.gen.transport.TransportProtos; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.Arrays; |
|||
import java.util.List; |
|||
|
|||
@Slf4j |
|||
public class ProtoConverter { |
|||
|
|||
public static final JsonParser JSON_PARSER = new JsonParser(); |
|||
|
|||
public static TransportProtos.PostTelemetryMsg convertToTelemetryProto(byte[] payload) throws InvalidProtocolBufferException, IllegalArgumentException { |
|||
TransportProtos.TsKvListProto protoPayload = TransportProtos.TsKvListProto.parseFrom(payload); |
|||
TransportProtos.PostTelemetryMsg.Builder postTelemetryMsgBuilder = TransportProtos.PostTelemetryMsg.newBuilder(); |
|||
TransportProtos.TsKvListProto tsKvListProto = validateTsKvListProto(protoPayload); |
|||
postTelemetryMsgBuilder.addTsKvList(tsKvListProto); |
|||
return postTelemetryMsgBuilder.build(); |
|||
} |
|||
|
|||
public static TransportProtos.PostTelemetryMsg validatePostTelemetryMsg(byte[] payload) throws InvalidProtocolBufferException, IllegalArgumentException { |
|||
TransportProtos.PostTelemetryMsg msg = TransportProtos.PostTelemetryMsg.parseFrom(payload); |
|||
TransportProtos.PostTelemetryMsg.Builder postTelemetryMsgBuilder = TransportProtos.PostTelemetryMsg.newBuilder(); |
|||
List<TransportProtos.TsKvListProto> tsKvListProtoList = msg.getTsKvListList(); |
|||
if (!CollectionUtils.isEmpty(tsKvListProtoList)) { |
|||
List<TransportProtos.TsKvListProto> tsKvListProtos = new ArrayList<>(); |
|||
tsKvListProtoList.forEach(tsKvListProto -> { |
|||
TransportProtos.TsKvListProto transportTsKvListProto = validateTsKvListProto(tsKvListProto); |
|||
tsKvListProtos.add(transportTsKvListProto); |
|||
}); |
|||
postTelemetryMsgBuilder.addAllTsKvList(tsKvListProtos); |
|||
return postTelemetryMsgBuilder.build(); |
|||
} else { |
|||
throw new IllegalArgumentException("TsKv list is empty!"); |
|||
} |
|||
} |
|||
|
|||
public static TransportProtos.PostAttributeMsg validatePostAttributeMsg(byte[] bytes) throws IllegalArgumentException, InvalidProtocolBufferException { |
|||
TransportProtos.PostAttributeMsg proto = TransportProtos.PostAttributeMsg.parseFrom(bytes); |
|||
List<TransportProtos.KeyValueProto> kvList = proto.getKvList(); |
|||
if (!CollectionUtils.isEmpty(kvList)) { |
|||
List<TransportProtos.KeyValueProto> keyValueProtos = validateKeyValueProtos(kvList); |
|||
TransportProtos.PostAttributeMsg.Builder result = TransportProtos.PostAttributeMsg.newBuilder(); |
|||
result.addAllKv(keyValueProtos); |
|||
return result.build(); |
|||
} else { |
|||
throw new IllegalArgumentException("KeyValue list is empty!"); |
|||
} |
|||
} |
|||
|
|||
public static TransportProtos.ClaimDeviceMsg convertToClaimDeviceProto(DeviceId deviceId, byte[] bytes) throws InvalidProtocolBufferException { |
|||
TransportApiProtos.ClaimDevice proto = TransportApiProtos.ClaimDevice.parseFrom(bytes); |
|||
String secretKey = proto.getSecretKey() != null ? proto.getSecretKey() : DataConstants.DEFAULT_SECRET_KEY; |
|||
long durationMs = proto.getDurationMs(); |
|||
return buildClaimDeviceMsg(deviceId, secretKey, durationMs); |
|||
} |
|||
|
|||
public static TransportProtos.GetAttributeRequestMsg convertToGetAttributeRequestMessage(byte[] bytes, int requestId) throws InvalidProtocolBufferException, RuntimeException { |
|||
TransportApiProtos.AttributesRequest proto = TransportApiProtos.AttributesRequest.parseFrom(bytes); |
|||
TransportProtos.GetAttributeRequestMsg.Builder result = TransportProtos.GetAttributeRequestMsg.newBuilder(); |
|||
result.setRequestId(requestId); |
|||
String clientKeys = proto.getClientKeys(); |
|||
String sharedKeys = proto.getSharedKeys(); |
|||
if (!StringUtils.isEmpty(clientKeys)) { |
|||
List<String> clientKeysList = Arrays.asList(clientKeys.split(",")); |
|||
result.addAllClientAttributeNames(clientKeysList); |
|||
} |
|||
if (!StringUtils.isEmpty(sharedKeys)) { |
|||
List<String> sharedKeysList = Arrays.asList(sharedKeys.split(",")); |
|||
result.addAllSharedAttributeNames(sharedKeysList); |
|||
} |
|||
return result.build(); |
|||
} |
|||
|
|||
public static TransportProtos.ToServerRpcRequestMsg convertToServerRpcRequest(byte[] bytes, int requestId) throws InvalidProtocolBufferException { |
|||
TransportApiProtos.RpcRequest proto = TransportApiProtos.RpcRequest.parseFrom(bytes); |
|||
String method = proto.getMethod(); |
|||
String params = proto.getParams(); |
|||
return TransportProtos.ToServerRpcRequestMsg.newBuilder().setRequestId(requestId).setMethodName(method).setParams(params).build(); |
|||
} |
|||
|
|||
private static TransportProtos.ClaimDeviceMsg buildClaimDeviceMsg(DeviceId deviceId, String secretKey, long durationMs) { |
|||
TransportProtos.ClaimDeviceMsg.Builder result = TransportProtos.ClaimDeviceMsg.newBuilder(); |
|||
return result |
|||
.setDeviceIdMSB(deviceId.getId().getMostSignificantBits()) |
|||
.setDeviceIdLSB(deviceId.getId().getLeastSignificantBits()) |
|||
.setSecretKey(secretKey) |
|||
.setDurationMs(durationMs) |
|||
.build(); |
|||
} |
|||
|
|||
private static TransportProtos.TsKvListProto validateTsKvListProto(TransportProtos.TsKvListProto tsKvListProto) { |
|||
TransportProtos.TsKvListProto.Builder tsKvListBuilder = TransportProtos.TsKvListProto.newBuilder(); |
|||
long ts = tsKvListProto.getTs(); |
|||
if (ts == 0) { |
|||
ts = System.currentTimeMillis(); |
|||
} |
|||
tsKvListBuilder.setTs(ts); |
|||
List<TransportProtos.KeyValueProto> kvList = tsKvListProto.getKvList(); |
|||
if (!CollectionUtils.isEmpty(kvList)) { |
|||
List<TransportProtos.KeyValueProto> keyValueListProtos = validateKeyValueProtos(kvList); |
|||
tsKvListBuilder.addAllKv(keyValueListProtos); |
|||
return tsKvListBuilder.build(); |
|||
} else { |
|||
throw new IllegalArgumentException("KeyValue list is empty!"); |
|||
} |
|||
} |
|||
|
|||
|
|||
private static List<TransportProtos.KeyValueProto> validateKeyValueProtos(List<TransportProtos.KeyValueProto> kvList) { |
|||
kvList.forEach(keyValueProto -> { |
|||
String key = keyValueProto.getKey(); |
|||
if (StringUtils.isEmpty(key)) { |
|||
throw new IllegalArgumentException("Invalid key value: " + key + "!"); |
|||
} |
|||
TransportProtos.KeyValueType type = keyValueProto.getType(); |
|||
switch (type) { |
|||
case BOOLEAN_V: |
|||
case LONG_V: |
|||
case DOUBLE_V: |
|||
break; |
|||
case STRING_V: |
|||
if (StringUtils.isEmpty(keyValueProto.getStringV())) { |
|||
throw new IllegalArgumentException("Value is empty for key: " + key + "!"); |
|||
} |
|||
break; |
|||
case JSON_V: |
|||
try { |
|||
JSON_PARSER.parse(keyValueProto.getJsonV()); |
|||
} catch (Exception e) { |
|||
throw new IllegalArgumentException("Can't parse value: " + keyValueProto.getJsonV() + " for key: " + key + "!"); |
|||
} |
|||
break; |
|||
case UNRECOGNIZED: |
|||
throw new IllegalArgumentException("Unsupported keyValueType: " + type + "!"); |
|||
} |
|||
}); |
|||
return kvList; |
|||
} |
|||
} |
|||
@ -0,0 +1,101 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0 |
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
syntax = "proto3"; |
|||
package transportapi; |
|||
|
|||
option java_package = "org.thingsboard.server.gen.transport"; |
|||
option java_outer_classname = "TransportApiProtos"; |
|||
|
|||
import "queue.proto"; |
|||
|
|||
message ClaimDevice { |
|||
string secretKey = 1; |
|||
int64 durationMs = 2; |
|||
} |
|||
|
|||
message AttributesRequest { |
|||
string clientKeys = 1; |
|||
string sharedKeys = 2; |
|||
} |
|||
|
|||
message RpcRequest { |
|||
string method = 1; |
|||
string params = 2; |
|||
} |
|||
|
|||
message DisconnectMsg { |
|||
string deviceName = 1; |
|||
} |
|||
|
|||
message ConnectMsg { |
|||
string deviceName = 1; |
|||
string deviceType = 2; |
|||
} |
|||
|
|||
message TelemetryMsg { |
|||
string deviceName = 1; |
|||
transport.PostTelemetryMsg msg = 3; |
|||
} |
|||
|
|||
message AttributesMsg { |
|||
string deviceName = 1; |
|||
transport.PostAttributeMsg msg = 2; |
|||
} |
|||
|
|||
message ClaimDeviceMsg { |
|||
string deviceName = 1; |
|||
ClaimDevice claimRequest = 2; |
|||
} |
|||
|
|||
message GatewayTelemetryMsg { |
|||
repeated TelemetryMsg msg = 1; |
|||
} |
|||
|
|||
message GatewayClaimMsg { |
|||
repeated ClaimDeviceMsg msg = 1; |
|||
} |
|||
|
|||
message GatewayAttributesMsg { |
|||
repeated AttributesMsg msg = 1; |
|||
} |
|||
|
|||
message GatewayRpcResponseMsg { |
|||
string deviceName = 1; |
|||
int32 id = 2; |
|||
string data = 3; |
|||
} |
|||
|
|||
message GatewayAttributeResponseMsg { |
|||
string deviceName = 1; |
|||
transport.GetAttributeResponseMsg responseMsg = 2; |
|||
} |
|||
|
|||
message GatewayAttributeUpdateNotificationMsg { |
|||
string deviceName = 1; |
|||
transport.AttributeUpdateNotificationMsg notificationMsg = 2; |
|||
} |
|||
|
|||
message GatewayDeviceRpcRequestMsg { |
|||
string deviceName = 1; |
|||
transport.ToDeviceRpcRequestMsg rpcRequestMsg = 2; |
|||
} |
|||
|
|||
message GatewayAttributesRequestMsg { |
|||
int32 id = 1; |
|||
string deviceName = 2; |
|||
bool client = 3; |
|||
repeated string keys = 4; |
|||
} |
|||
Loading…
Reference in new issue