225 changed files with 9908 additions and 2542 deletions
@ -0,0 +1,246 @@ |
|||||
|
/** |
||||
|
* 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.eclipse.paho.client.mqttv3.persist.MemoryPersistence; |
||||
|
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 java.util.function.Supplier; |
||||
|
|
||||
|
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, new MemoryPersistence()); |
||||
|
|
||||
|
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(); |
||||
|
} |
||||
|
|
||||
|
protected <T> T doExecuteWithRetriesAndInterval(SupplierWithThrowable<T> supplier, int retries, int intervalMs) throws Exception { |
||||
|
int count = 0; |
||||
|
T result = null; |
||||
|
Throwable lastException = null; |
||||
|
while (count < retries) { |
||||
|
try { |
||||
|
result = supplier.get(); |
||||
|
if (result != null) { |
||||
|
return result; |
||||
|
} |
||||
|
} catch (Throwable e) { |
||||
|
lastException = e; |
||||
|
} |
||||
|
count++; |
||||
|
if (count < retries) { |
||||
|
Thread.sleep(intervalMs); |
||||
|
} |
||||
|
} |
||||
|
if (lastException != null) { |
||||
|
throw new RuntimeException(lastException); |
||||
|
} else { |
||||
|
return result; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@FunctionalInterface |
||||
|
public interface SupplierWithThrowable<T> { |
||||
|
T get() throws Throwable; |
||||
|
} |
||||
|
} |
||||
@ -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,154 @@ |
|||||
|
/** |
||||
|
* 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); |
||||
|
|
||||
|
Device savedDevice = doExecuteWithRetriesAndInterval(() -> doGet("/api/tenant/devices?deviceName=" + "Gateway Device Request Attributes", Device.class), |
||||
|
20, |
||||
|
100); |
||||
|
|
||||
|
assertNotNull(savedDevice); |
||||
|
|
||||
|
Thread.sleep(1000); |
||||
|
|
||||
|
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,171 @@ |
|||||
|
/** |
||||
|
* 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(1000); |
||||
|
|
||||
|
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); |
||||
|
|
||||
|
Device savedDevice = doExecuteWithRetriesAndInterval(() -> doGet("/api/tenant/devices?deviceName=" + "Gateway Device Subscribe to attribute updates", Device.class), |
||||
|
20, |
||||
|
100); |
||||
|
|
||||
|
assertNotNull(savedDevice); |
||||
|
|
||||
|
client.subscribe(MqttTopics.GATEWAY_ATTRIBUTES_TOPIC, MqttQoS.AT_MOST_ONCE.value()); |
||||
|
|
||||
|
Thread.sleep(1000); |
||||
|
|
||||
|
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,206 @@ |
|||||
|
/** |
||||
|
* 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.Ignore; |
||||
|
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)); |
||||
|
|
||||
|
loginUser(customerAdmin.getName(), CUSTOMER_USER_PASSWORD); |
||||
|
ClaimRequest claimRequest; |
||||
|
if (!emptyPayload) { |
||||
|
claimRequest = new ClaimRequest("value"); |
||||
|
} else { |
||||
|
claimRequest = new ClaimRequest(null); |
||||
|
} |
||||
|
|
||||
|
ClaimResponse claimResponse = doExecuteWithRetriesAndInterval( |
||||
|
() -> doPostClaimAsync("/api/customer/device/" + savedDevice.getName() + "/claim", claimRequest, ClaimResponse.class, status().isBadRequest()), |
||||
|
20, |
||||
|
100 |
||||
|
); |
||||
|
|
||||
|
assertEquals(claimResponse, ClaimResponse.FAILURE); |
||||
|
|
||||
|
client.publish(MqttTopics.DEVICE_CLAIM_TOPIC, new MqttMessage(payloadBytes)); |
||||
|
|
||||
|
ClaimResult claimResult = doExecuteWithRetriesAndInterval( |
||||
|
() -> doPostClaimAsync("/api/customer/device/" + savedDevice.getName() + "/claim", claimRequest, ClaimResult.class, status().isOk()), |
||||
|
20, |
||||
|
100 |
||||
|
); |
||||
|
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)); |
||||
|
|
||||
|
Device savedDevice = doExecuteWithRetriesAndInterval( |
||||
|
() -> doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class), |
||||
|
20, |
||||
|
100 |
||||
|
); |
||||
|
|
||||
|
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)); |
||||
|
|
||||
|
ClaimResult claimResult = doExecuteWithRetriesAndInterval( |
||||
|
() -> doPostClaimAsync("/api/customer/device/" + deviceName + "/claim", claimRequest, ClaimResult.class, status().isOk()), |
||||
|
20, |
||||
|
100 |
||||
|
); |
||||
|
|
||||
|
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,58 @@ |
|||||
|
/** |
||||
|
* 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.Ignore; |
||||
|
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,116 @@ |
|||||
|
/** |
||||
|
* 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.Ignore; |
||||
|
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,182 @@ |
|||||
|
/** |
||||
|
* 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() + 5000; |
||||
|
|
||||
|
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); |
||||
|
|
||||
|
Device firstDevice = doExecuteWithRetriesAndInterval(() -> doGet("/api/tenant/devices?deviceName=" + firstDeviceName, Device.class), |
||||
|
20, |
||||
|
100); |
||||
|
|
||||
|
assertNotNull(firstDevice); |
||||
|
|
||||
|
Device secondDevice = doExecuteWithRetriesAndInterval(() -> doGet("/api/tenant/devices?deviceName=" + secondDeviceName, Device.class), |
||||
|
20, |
||||
|
100); |
||||
|
|
||||
|
assertNotNull(secondDevice); |
||||
|
|
||||
|
Thread.sleep(2000); |
||||
|
|
||||
|
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,300 @@ |
|||||
|
/** |
||||
|
* 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.eclipse.paho.client.mqttv3.persist.MemoryPersistence; |
||||
|
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); |
||||
|
|
||||
|
String deviceName = "Device A"; |
||||
|
|
||||
|
Device device = doExecuteWithRetriesAndInterval(() -> doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class), |
||||
|
20, |
||||
|
100); |
||||
|
|
||||
|
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() + 5000; |
||||
|
|
||||
|
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); |
||||
|
|
||||
|
Device firstDevice = doExecuteWithRetriesAndInterval(() -> doGet("/api/tenant/devices?deviceName=" + firstDeviceName, Device.class), |
||||
|
20, |
||||
|
100); |
||||
|
|
||||
|
assertNotNull(firstDevice); |
||||
|
|
||||
|
Device secondDevice = doExecuteWithRetriesAndInterval(() -> doGet("/api/tenant/devices?deviceName=" + secondDeviceName, Device.class), |
||||
|
20, |
||||
|
100); |
||||
|
|
||||
|
assertNotNull(secondDevice); |
||||
|
|
||||
|
Thread.sleep(2000); |
||||
|
|
||||
|
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, new MemoryPersistence()); |
||||
|
|
||||
|
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); |
||||
|
|
||||
|
String deviceName = "Device A"; |
||||
|
Device device = doExecuteWithRetriesAndInterval(() -> doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class), |
||||
|
20, |
||||
|
100); |
||||
|
assertNotNull(device); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,116 @@ |
|||||
|
/** |
||||
|
* 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); |
||||
|
|
||||
|
Device device = doExecuteWithRetriesAndInterval(() -> doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class), |
||||
|
20, |
||||
|
100); |
||||
|
|
||||
|
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,33 @@ |
|||||
|
/** |
||||
|
* 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.dao.rule; |
||||
|
|
||||
|
import org.thingsboard.server.common.data.id.EntityId; |
||||
|
import org.thingsboard.server.common.data.id.RuleNodeId; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.common.data.page.PageData; |
||||
|
import org.thingsboard.server.common.data.page.PageLink; |
||||
|
import org.thingsboard.server.common.data.rule.RuleNodeState; |
||||
|
|
||||
|
public interface RuleNodeStateService { |
||||
|
|
||||
|
PageData<RuleNodeState> findByRuleNodeId(TenantId tenantId, RuleNodeId ruleNodeId, PageLink pageLink); |
||||
|
|
||||
|
RuleNodeState findByRuleNodeIdAndEntityId(TenantId tenantId, RuleNodeId ruleNodeId, EntityId entityId); |
||||
|
|
||||
|
RuleNodeState save(TenantId tenantId, RuleNodeState ruleNodeState); |
||||
|
|
||||
|
} |
||||
@ -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,37 @@ |
|||||
|
/** |
||||
|
* 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.device.profile; |
||||
|
|
||||
|
import com.fasterxml.jackson.annotation.JsonIgnore; |
||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
||||
|
import com.fasterxml.jackson.annotation.JsonSubTypes; |
||||
|
import com.fasterxml.jackson.annotation.JsonTypeInfo; |
||||
|
|
||||
|
@JsonIgnoreProperties(ignoreUnknown = true) |
||||
|
@JsonTypeInfo( |
||||
|
use = JsonTypeInfo.Id.NAME, |
||||
|
include = JsonTypeInfo.As.PROPERTY, |
||||
|
property = "type") |
||||
|
@JsonSubTypes({ |
||||
|
@JsonSubTypes.Type(value = SimpleAlarmConditionSpec.class, name = "SIMPLE"), |
||||
|
@JsonSubTypes.Type(value = DurationAlarmConditionSpec.class, name = "DURATION"), |
||||
|
@JsonSubTypes.Type(value = RepeatingAlarmConditionSpec.class, name = "REPEATING")}) |
||||
|
public interface AlarmConditionSpec { |
||||
|
|
||||
|
@JsonIgnore |
||||
|
AlarmConditionSpecType getType(); |
||||
|
|
||||
|
} |
||||
@ -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.common.data.device.profile; |
||||
|
|
||||
|
public enum AlarmConditionSpecType { |
||||
|
|
||||
|
SIMPLE, |
||||
|
DURATION, |
||||
|
REPEATING |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,35 @@ |
|||||
|
/** |
||||
|
* 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.device.profile; |
||||
|
|
||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
||||
|
import com.fasterxml.jackson.annotation.JsonSubTypes; |
||||
|
import com.fasterxml.jackson.annotation.JsonTypeInfo; |
||||
|
|
||||
|
@JsonIgnoreProperties(ignoreUnknown = true) |
||||
|
@JsonTypeInfo( |
||||
|
use = JsonTypeInfo.Id.NAME, |
||||
|
include = JsonTypeInfo.As.PROPERTY, |
||||
|
property = "type") |
||||
|
@JsonSubTypes({ |
||||
|
@JsonSubTypes.Type(value = AnyTimeSchedule.class, name = "ANY_TIME"), |
||||
|
@JsonSubTypes.Type(value = SpecificTimeSchedule.class, name = "SPECIFIC_TIME"), |
||||
|
@JsonSubTypes.Type(value = CustomTimeSchedule.class, name = "CUSTOM")}) |
||||
|
public interface AlarmSchedule { |
||||
|
|
||||
|
AlarmScheduleType getType(); |
||||
|
|
||||
|
} |
||||
@ -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.common.data.device.profile; |
||||
|
|
||||
|
public enum AlarmScheduleType { |
||||
|
|
||||
|
ANY_TIME, |
||||
|
SPECIFIC_TIME, |
||||
|
CUSTOM |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
/** |
||||
|
* 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.device.profile; |
||||
|
|
||||
|
public class AnyTimeSchedule implements AlarmSchedule { |
||||
|
|
||||
|
@Override |
||||
|
public AlarmScheduleType getType() { |
||||
|
return AlarmScheduleType.ANY_TIME; |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,33 @@ |
|||||
|
/** |
||||
|
* 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.device.profile; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
@Data |
||||
|
public class CustomTimeSchedule implements AlarmSchedule { |
||||
|
|
||||
|
private String timezone; |
||||
|
private List<CustomTimeScheduleItem> items; |
||||
|
|
||||
|
@Override |
||||
|
public AlarmScheduleType getType() { |
||||
|
return AlarmScheduleType.CUSTOM; |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,30 @@ |
|||||
|
/** |
||||
|
* 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.device.profile; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
@Data |
||||
|
public class CustomTimeScheduleItem { |
||||
|
|
||||
|
private boolean enabled; |
||||
|
private int dayOfWeek; |
||||
|
private long startsOn; |
||||
|
private long endsOn; |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,34 @@ |
|||||
|
/** |
||||
|
* 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.device.profile; |
||||
|
|
||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
||||
|
import lombok.Data; |
||||
|
|
||||
|
import java.util.concurrent.TimeUnit; |
||||
|
|
||||
|
@Data |
||||
|
@JsonIgnoreProperties(ignoreUnknown = true) |
||||
|
public class DurationAlarmConditionSpec implements AlarmConditionSpec { |
||||
|
|
||||
|
private TimeUnit unit; |
||||
|
private long value; |
||||
|
|
||||
|
@Override |
||||
|
public AlarmConditionSpecType getType() { |
||||
|
return AlarmConditionSpecType.DURATION; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,33 @@ |
|||||
|
/** |
||||
|
* 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.device.profile; |
||||
|
|
||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
||||
|
import lombok.Data; |
||||
|
|
||||
|
import java.util.concurrent.TimeUnit; |
||||
|
|
||||
|
@Data |
||||
|
@JsonIgnoreProperties(ignoreUnknown = true) |
||||
|
public class RepeatingAlarmConditionSpec implements AlarmConditionSpec { |
||||
|
|
||||
|
private int count; |
||||
|
|
||||
|
@Override |
||||
|
public AlarmConditionSpecType getType() { |
||||
|
return AlarmConditionSpecType.REPEATING; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,28 @@ |
|||||
|
/** |
||||
|
* 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.device.profile; |
||||
|
|
||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
||||
|
import lombok.Data; |
||||
|
|
||||
|
@Data |
||||
|
@JsonIgnoreProperties(ignoreUnknown = true) |
||||
|
public class SimpleAlarmConditionSpec implements AlarmConditionSpec { |
||||
|
@Override |
||||
|
public AlarmConditionSpecType getType() { |
||||
|
return AlarmConditionSpecType.SIMPLE; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,36 @@ |
|||||
|
/** |
||||
|
* 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.device.profile; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
|
||||
|
import java.util.List; |
||||
|
import java.util.Set; |
||||
|
|
||||
|
@Data |
||||
|
public class SpecificTimeSchedule implements AlarmSchedule { |
||||
|
|
||||
|
private String timezone; |
||||
|
private Set<Integer> daysOfWeek; |
||||
|
private long startsOn; |
||||
|
private long endsOn; |
||||
|
|
||||
|
@Override |
||||
|
public AlarmScheduleType getType() { |
||||
|
return AlarmScheduleType.SPECIFIC_TIME; |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,35 @@ |
|||||
|
/** |
||||
|
* 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.id; |
||||
|
|
||||
|
import com.fasterxml.jackson.annotation.JsonCreator; |
||||
|
import com.fasterxml.jackson.annotation.JsonProperty; |
||||
|
|
||||
|
import java.util.UUID; |
||||
|
|
||||
|
public class RuleNodeStateId extends UUIDBased { |
||||
|
|
||||
|
private static final long serialVersionUID = 1L; |
||||
|
|
||||
|
@JsonCreator |
||||
|
public RuleNodeStateId(@JsonProperty("id") UUID id) { |
||||
|
super(id); |
||||
|
} |
||||
|
|
||||
|
public static RuleNodeStateId fromString(String eventId) { |
||||
|
return new RuleNodeStateId(UUID.fromString(eventId)); |
||||
|
} |
||||
|
} |
||||
@ -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.common.data.rule; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
@Data |
||||
|
public class RuleChainData { |
||||
|
|
||||
|
List<RuleChain> ruleChains; |
||||
|
List<RuleChainMetaData> metadata; |
||||
|
} |
||||
@ -0,0 +1,31 @@ |
|||||
|
/** |
||||
|
* 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.rule; |
||||
|
|
||||
|
import lombok.AllArgsConstructor; |
||||
|
import lombok.Data; |
||||
|
import org.thingsboard.server.common.data.id.RuleChainId; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; |
||||
|
|
||||
|
@Data |
||||
|
@AllArgsConstructor |
||||
|
public class RuleChainImportResult { |
||||
|
|
||||
|
private TenantId tenantId; |
||||
|
private RuleChainId ruleChainId; |
||||
|
private ComponentLifecycleEvent lifecycleEvent; |
||||
|
} |
||||
@ -0,0 +1,42 @@ |
|||||
|
/** |
||||
|
* 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.rule; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
import org.thingsboard.server.common.data.BaseData; |
||||
|
import org.thingsboard.server.common.data.id.EntityId; |
||||
|
import org.thingsboard.server.common.data.id.RuleNodeId; |
||||
|
import org.thingsboard.server.common.data.id.RuleNodeStateId; |
||||
|
|
||||
|
@Data |
||||
|
public class RuleNodeState extends BaseData<RuleNodeStateId> { |
||||
|
|
||||
|
private RuleNodeId ruleNodeId; |
||||
|
private EntityId entityId; |
||||
|
private String stateData; |
||||
|
|
||||
|
public RuleNodeState() { |
||||
|
super(); |
||||
|
} |
||||
|
|
||||
|
public RuleNodeState(RuleNodeStateId id) { |
||||
|
super(id); |
||||
|
} |
||||
|
|
||||
|
public RuleNodeState(RuleNodeState event) { |
||||
|
super(event); |
||||
|
} |
||||
|
} |
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue